blob: 62de879d683f8bb345301779be7442fef641a266 [file] [log] [blame]
agsantos5aa39652020-08-11 18:18:04 -04001/**
Sébastien Blincb783e32021-02-12 11:34:10 -05002 * Copyright (C) 2020-2021 Savoir-faire Linux Inc.
agsantos5aa39652020-08-11 18:18:04 -04003 *
4 * Author: Aline Gondim Santos <aline.gondimsantos@savoirfairelinux.com>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
agsantosac1940d2020-09-17 10:18:40 -040018 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
19 * USA.
agsantos5aa39652020-08-11 18:18:04 -040020 */
21
22#include "pluginProcessor.h"
23// System includes
24#include <algorithm>
25#include <cstring>
26// OpenCV headers
agsantos5aa39652020-08-11 18:18:04 -040027#include <opencv2/core.hpp>
agsantosac1940d2020-09-17 10:18:40 -040028#include <opencv2/imgcodecs.hpp>
29#include <opencv2/imgproc.hpp>
agsantos5aa39652020-08-11 18:18:04 -040030// Logger
31#include <pluglog.h>
32
33extern "C" {
34#include <libavutil/display.h>
35}
agsantos5aa39652020-08-11 18:18:04 -040036const char sep = separator();
37
38const std::string TAG = "FORESEG";
39
agsantosac1940d2020-09-17 10:18:40 -040040namespace jami {
agsantos5aa39652020-08-11 18:18:04 -040041
agsantos796b5af2020-12-22 19:38:27 -050042PluginProcessor::PluginProcessor(const std::string& dataPath, const std::string& model, const std::string& backgroundImage, bool acc)
agsantos5aa39652020-08-11 18:18:04 -040043{
agsantos796b5af2020-12-22 19:38:27 -050044 activateAcc_ = acc;
45 initModel(dataPath+sep+"model/"+model);
46 setBackgroundImage(backgroundImage);
47}
48
49PluginProcessor::~PluginProcessor()
50{
51 Plog::log(Plog::LogPriority::INFO, TAG, "~pluginprocessor");
52 if (session_)
53 delete session_;
agsantos5aa39652020-08-11 18:18:04 -040054}
55
56void
agsantos9dcf4302020-09-01 18:21:48 -040057PluginProcessor::setBackgroundImage(const std::string& backgroundPath)
agsantos5aa39652020-08-11 18:18:04 -040058{
agsantosac1940d2020-09-17 10:18:40 -040059 cv::Size size = cv::Size {0, 0};
agsantos5aa39652020-08-11 18:18:04 -040060
agsantosac1940d2020-09-17 10:18:40 -040061 if (!backgroundImage.empty())
62 size = backgroundImage.size();
agsantos5aa39652020-08-11 18:18:04 -040063
agsantosac1940d2020-09-17 10:18:40 -040064 cv::Mat newBackgroundImage = cv::imread(backgroundPath);
65 if (newBackgroundImage.cols == 0) {
66 Plog::log(Plog::LogPriority::ERR, TAG, "Background image not Loaded");
67 } else {
68 Plog::log(Plog::LogPriority::INFO, TAG, "Background image Loaded");
69 cv::cvtColor(newBackgroundImage, newBackgroundImage, cv::COLOR_BGR2RGB);
70 newBackgroundImage.convertTo(newBackgroundImage, CV_32FC3);
71 if (size.height) {
72 cv::resize(newBackgroundImage, newBackgroundImage, size);
73 backgroundRotation = 0;
74 }
75 backgroundImage = newBackgroundImage.clone();
76 newBackgroundImage.release();
77 hasBackground_ = true;
78 }
agsantos5aa39652020-08-11 18:18:04 -040079}
80
81void
agsantos796b5af2020-12-22 19:38:27 -050082PluginProcessor::initModel(const std::string& modelPath)
agsantos5aa39652020-08-11 18:18:04 -040083{
agsantosac1940d2020-09-17 10:18:40 -040084 try {
agsantos796b5af2020-12-22 19:38:27 -050085 auto allocator_info = Ort::MemoryInfo::CreateCpu(OrtArenaAllocator, OrtMemTypeDefault);
86 input_tensor_ = Ort::Value::CreateTensor<float>(allocator_info, input_image_.data(), input_image_.size(), input_shape_.data(), input_shape_.size());
87 output_tensor_ = Ort::Value::CreateTensor<float>(allocator_info, results_.data(), results_.size(), output_shape_.data(), output_shape_.size());
88 sessOpt_ = Ort::SessionOptions();
89
90#ifdef NVIDIA
91 if (activateAcc_)
92 Ort::ThrowOnError(OrtSessionOptionsAppendExecutionProvider_CUDA(sessOpt_, 0));
93#endif
94#ifdef ANDROID
95 if (activateAcc_)
96 Ort::ThrowOnError(OrtSessionOptionsAppendExecutionProvider_Nnapi(sessOpt_, 0));
97#endif
98
99 sessOpt_.SetGraphOptimizationLevel(GraphOptimizationLevel::ORT_ENABLE_ALL);
100#ifdef WIN32
101 std::wstring wsTmp(modelPath.begin(), modelPath.end());
102 session_ = new Ort::Session(env, wsTmp.c_str(), sessOpt_);
103#else
104 session_ = new Ort::Session(env, modelPath.c_str(), sessOpt_);
105#endif
106 isAllocated_ = true;
agsantosac1940d2020-09-17 10:18:40 -0400107 } catch (std::exception& e) {
108 Plog::log(Plog::LogPriority::ERR, TAG, e.what());
109 }
110 std::ostringstream oss;
agsantos796b5af2020-12-22 19:38:27 -0500111 oss << "Model is allocated " << isAllocated_;
agsantosac1940d2020-09-17 10:18:40 -0400112 Plog::log(Plog::LogPriority::INFO, TAG, oss.str());
agsantos5aa39652020-08-11 18:18:04 -0400113}
114
agsantos796b5af2020-12-22 19:38:27 -0500115bool
116PluginProcessor::isAllocated()
117{
118 return isAllocated_;
119}
120
agsantos5aa39652020-08-11 18:18:04 -0400121void
122PluginProcessor::feedInput(const cv::Mat& frame)
123{
agsantos796b5af2020-12-22 19:38:27 -0500124 cv::Mat temp(frame.rows, frame.cols, CV_32FC3, input_image_.data());
125 frame.convertTo(temp, CV_32FC3);
agsantos5aa39652020-08-11 18:18:04 -0400126}
agsantos5aa39652020-08-11 18:18:04 -0400127
128int
129PluginProcessor::getBackgroundRotation()
130{
agsantosac1940d2020-09-17 10:18:40 -0400131 return backgroundRotation;
agsantos5aa39652020-08-11 18:18:04 -0400132}
133
134void
135PluginProcessor::setBackgroundRotation(int angle)
136{
agsantosac1940d2020-09-17 10:18:40 -0400137 if (backgroundRotation != angle && (backgroundRotation - angle) != 0) {
agsantosb74f4cb2020-10-01 14:30:43 -0400138 rotateFrame(backgroundRotation - angle, backgroundImage);
agsantosac1940d2020-09-17 10:18:40 -0400139 backgroundRotation = angle;
140 }
agsantos5aa39652020-08-11 18:18:04 -0400141}
142
143void
agsantosac1940d2020-09-17 10:18:40 -0400144PluginProcessor::computePredictions()
agsantos5aa39652020-08-11 18:18:04 -0400145{
agsantosb74f4cb2020-10-01 14:30:43 -0400146 if (count == 0) {
147 // Run the graph
agsantos796b5af2020-12-22 19:38:27 -0500148 session_->Run(Ort::RunOptions{nullptr}, input_names, &input_tensor_, 1, output_names, &output_tensor_, 1);
149 computedMask = std::vector(results_.begin(), results_.end());
agsantosb74f4cb2020-10-01 14:30:43 -0400150 }
agsantos5aa39652020-08-11 18:18:04 -0400151}
152
153void
154PluginProcessor::printMask()
155{
agsantosac1940d2020-09-17 10:18:40 -0400156 for (size_t i = 0; i < computedMask.size(); i++) {
157 // Log the predictions
158 std::ostringstream oss;
159 oss << "\nclass: " << computedMask[i] << std::endl;
160 Plog::log(Plog::LogPriority::INFO, TAG, oss.str());
161 }
agsantos5aa39652020-08-11 18:18:04 -0400162}
163
agsantos5aa39652020-08-11 18:18:04 -0400164void
agsantos31d1a0b2020-10-23 14:05:53 -0400165PluginProcessor::resetInitValues(const cv::Size& modelInputSize)
166{
167 previousMasks[0] = cv::Mat(modelInputSize.height, modelInputSize.width, CV_32FC1, double(0.));
168 previousMasks[1] = cv::Mat(modelInputSize.height, modelInputSize.width, CV_32FC1, double(0.));
169 kSize = cv::Size(modelInputSize.width * kernelSize, modelInputSize.height * kernelSize);
170 if (kSize.height % 2 == 0) {
171 kSize.height -= 1;
172 }
173 if (kSize.width % 2 == 0) {
174 kSize.width -= 1;
175 }
176 count = 0;
177 grabCutMode = cv::GC_INIT_WITH_MASK;
178 grabCutIterations = 5;
179}
180
181void
agsantos5aa39652020-08-11 18:18:04 -0400182copyByLine(uchar* frameData, uchar* applyMaskData, const int lineSize, cv::Size size)
183{
agsantosac1940d2020-09-17 10:18:40 -0400184 if (3 * size.width == lineSize) {
185 std::memcpy(frameData, applyMaskData, size.height * size.width * 3);
agsantosac1940d2020-09-17 10:18:40 -0400186 } else {
187 int rows = size.height;
188 int offset = 0;
189 int maskoffset = 0;
190 for (int i = 0; i < rows; i++) {
191 std::memcpy(frameData + offset, applyMaskData + maskoffset, lineSize);
192 offset += lineSize;
193 maskoffset += 3 * size.width;
194 }
195 }
agsantos5aa39652020-08-11 18:18:04 -0400196}
197
198void
agsantosac1940d2020-09-17 10:18:40 -0400199PluginProcessor::drawMaskOnFrame(
200 cv::Mat& frame, cv::Mat& frameReduced, std::vector<float> computedMask, int lineSize, int angle)
agsantos5aa39652020-08-11 18:18:04 -0400201{
agsantosac1940d2020-09-17 10:18:40 -0400202 if (computedMask.empty()) {
203 return;
204 }
agsantosb74f4cb2020-10-01 14:30:43 -0400205
agsantosb74f4cb2020-10-01 14:30:43 -0400206 if (count == 0) {
agsantos796b5af2020-12-22 19:38:27 -0500207 int maskSize = static_cast<int>(std::sqrt(computedMask.size()));
208 cv::Mat maskImg(maskSize, maskSize, CV_32FC1, computedMask.data());
209 cv::Mat* applyMask = &frameReduced;
210
agsantosb74f4cb2020-10-01 14:30:43 -0400211 rotateFrame(-angle, maskImg);
agsantosb74f4cb2020-10-01 14:30:43 -0400212 cv::resize(maskImg, maskImg, cv::Size(frameReduced.cols, frameReduced.rows));
agsantos5aa39652020-08-11 18:18:04 -0400213
agsantosb74f4cb2020-10-01 14:30:43 -0400214 double m, M;
215 cv::minMaxLoc(maskImg, &m, &M);
216
217 if (M < 2) { // avoid detection if there is any one in frame
218 maskImg = 0. * maskImg;
219 } else {
220 for (int i = 0; i < maskImg.cols; i++) {
221 for (int j = 0; j < maskImg.rows; j++) {
222 maskImg.at<float>(j, i) = (maskImg.at<float>(j, i) - m) / (M - m);
223
224 if (maskImg.at<float>(j, i) < 0.4)
225 maskImg.at<float>(j, i) = 0.;
226 else if (maskImg.at<float>(j, i) < 0.7) {
227 float value = maskImg.at<float>(j, i) * smoothFactors[0]
228 + previousMasks[0].at<float>(j, i) * smoothFactors[1]
229 + previousMasks[1].at<float>(j, i) * smoothFactors[2];
230 maskImg.at<float>(j, i) = 0.;
231 if (value > 0.7)
232 maskImg.at<float>(j, i) = 1.;
233 } else
234 maskImg.at<float>(j, i) = 1.;
235 }
236 }
237 }
agsantosb74f4cb2020-10-01 14:30:43 -0400238 if (cv::countNonZero(maskImg) != 0) {
agsantos796b5af2020-12-22 19:38:27 -0500239 cv::Mat dilate;
240 cv::dilate(maskImg,
241 dilate,
242 cv::getStructuringElement(cv::MORPH_ELLIPSE, kSize),
243 cv::Point(-1, -1),
244 2);
245 cv::erode(maskImg,
246 maskImg,
247 cv::getStructuringElement(cv::MORPH_ELLIPSE, kSize),
248 cv::Point(-1, -1),
249 2);
250 for (int i = 0; i < maskImg.cols; i++) {
251 for (int j = 0; j < maskImg.rows; j++) {
252 if (dilate.at<float>(j, i) != maskImg.at<float>(j, i))
253 maskImg.at<float>(j, i) = grabcutClass;
agsantosb74f4cb2020-10-01 14:30:43 -0400254 }
agsantosb74f4cb2020-10-01 14:30:43 -0400255 }
agsantos796b5af2020-12-22 19:38:27 -0500256 maskImg.convertTo(maskImg, CV_8UC1);
257 applyMask->convertTo(*applyMask, CV_8UC1);
258 cv::Rect rect(1, 1, maskImg.rows, maskImg.cols);
259 cv::grabCut(*applyMask,
260 maskImg,
261 rect,
262 bgdModel,
263 fgdModel,
264 grabCutIterations,
265 grabCutMode);
266
267 grabCutMode = cv::GC_EVAL;
268 grabCutIterations = 1;
269
270 maskImg = maskImg & 1;
agsantosb74f4cb2020-10-01 14:30:43 -0400271 maskImg.convertTo(maskImg, CV_32FC1);
272 maskImg *= 255.;
agsantos31d1a0b2020-10-23 14:05:53 -0400273 GaussianBlur(maskImg, maskImg, cv::Size(7, 7), 0); // float mask from 0 to 255.
agsantosb74f4cb2020-10-01 14:30:43 -0400274 maskImg = maskImg / 255.;
275 }
276 previousMasks[1] = previousMasks[0].clone();
277 previousMasks[0] = maskImg.clone();
278 }
279
280 cv::Mat roiMaskImg = previousMasks[0].clone();
agsantosac1940d2020-09-17 10:18:40 -0400281 cv::Mat roiMaskImgComplementary = 1. - roiMaskImg; // mask from 1. to 0
agsantos5aa39652020-08-11 18:18:04 -0400282
agsantosac1940d2020-09-17 10:18:40 -0400283 std::vector<cv::Mat> channels;
284 std::vector<cv::Mat> channelsComplementary;
agsantos5aa39652020-08-11 18:18:04 -0400285
agsantosac1940d2020-09-17 10:18:40 -0400286 channels.emplace_back(roiMaskImg);
287 channels.emplace_back(roiMaskImg);
288 channels.emplace_back(roiMaskImg);
289 channelsComplementary.emplace_back(roiMaskImgComplementary);
290 channelsComplementary.emplace_back(roiMaskImgComplementary);
291 channelsComplementary.emplace_back(roiMaskImgComplementary);
agsantos5aa39652020-08-11 18:18:04 -0400292
agsantosac1940d2020-09-17 10:18:40 -0400293 cv::merge(channels, roiMaskImg);
294 cv::merge(channelsComplementary, roiMaskImgComplementary);
agsantos5aa39652020-08-11 18:18:04 -0400295
agsantos796b5af2020-12-22 19:38:27 -0500296 cv::Mat output;
297 frameReduced.convertTo(output, roiMaskImg.type());
agsantosb74f4cb2020-10-01 14:30:43 -0400298 output = output.mul(roiMaskImg);
299 output += backgroundImage.mul(roiMaskImgComplementary);
agsantos796b5af2020-12-22 19:38:27 -0500300 output.convertTo(output, frameReduced.type());
agsantos5aa39652020-08-11 18:18:04 -0400301
agsantosb74f4cb2020-10-01 14:30:43 -0400302 cv::resize(output, output, cv::Size(frame.cols, frame.rows));
agsantos5aa39652020-08-11 18:18:04 -0400303
agsantosb74f4cb2020-10-01 14:30:43 -0400304 copyByLine(frame.data, output.data, lineSize, cv::Size(frame.cols, frame.rows));
305 count++;
306 count = count % frameCount;
agsantos5aa39652020-08-11 18:18:04 -0400307}
308
309void
310PluginProcessor::rotateFrame(int angle, cv::Mat& mat)
311{
agsantosb74f4cb2020-10-01 14:30:43 -0400312 if (angle == -90)
313 cv::rotate(mat, mat, cv::ROTATE_90_COUNTERCLOCKWISE);
314 else if (std::abs(angle) == 180)
315 cv::rotate(mat, mat, cv::ROTATE_180);
316 else if (angle == 90)
317 cv::rotate(mat, mat, cv::ROTATE_90_CLOCKWISE);
agsantos5aa39652020-08-11 18:18:04 -0400318}
agsantos9dcf4302020-09-01 18:21:48 -0400319
320bool
321PluginProcessor::hasBackground() const
322{
323 return hasBackground_;
324}
agsantos5aa39652020-08-11 18:18:04 -0400325} // namespace jami