blob: 3b38830a53bc6e6c3ef51f4690f897b692f63ef9 [file] [log] [blame]
agsantos5aa39652020-08-11 18:18:04 -04001/**
2 * Copyright (C) 2020 Savoir-faire Linux Inc.
3 *
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}
36
37const char sep = separator();
38
39const std::string TAG = "FORESEG";
40
41PluginParameters* mPluginParameters = getGlobalPluginParameters();
42
agsantosac1940d2020-09-17 10:18:40 -040043namespace jami {
agsantos5aa39652020-08-11 18:18:04 -040044
agsantosac1940d2020-09-17 10:18:40 -040045PluginProcessor::PluginProcessor(const std::string& dataPath)
46 : pluginInference {TFModel {dataPath + sep + "models" + sep + mPluginParameters->model}}
agsantos5aa39652020-08-11 18:18:04 -040047{
agsantosac1940d2020-09-17 10:18:40 -040048 initModel();
49 setBackgroundImage(mPluginParameters->image);
agsantos5aa39652020-08-11 18:18:04 -040050}
51
52void
agsantos9dcf4302020-09-01 18:21:48 -040053PluginProcessor::setBackgroundImage(const std::string& backgroundPath)
agsantos5aa39652020-08-11 18:18:04 -040054{
agsantosac1940d2020-09-17 10:18:40 -040055 cv::Size size = cv::Size {0, 0};
agsantos5aa39652020-08-11 18:18:04 -040056
agsantosac1940d2020-09-17 10:18:40 -040057 if (!backgroundImage.empty())
58 size = backgroundImage.size();
agsantos5aa39652020-08-11 18:18:04 -040059
agsantosac1940d2020-09-17 10:18:40 -040060 cv::Mat newBackgroundImage = cv::imread(backgroundPath);
61 if (newBackgroundImage.cols == 0) {
62 Plog::log(Plog::LogPriority::ERR, TAG, "Background image not Loaded");
63 } else {
64 Plog::log(Plog::LogPriority::INFO, TAG, "Background image Loaded");
65 cv::cvtColor(newBackgroundImage, newBackgroundImage, cv::COLOR_BGR2RGB);
66 newBackgroundImage.convertTo(newBackgroundImage, CV_32FC3);
67 if (size.height) {
68 cv::resize(newBackgroundImage, newBackgroundImage, size);
69 backgroundRotation = 0;
70 }
71 backgroundImage = newBackgroundImage.clone();
72 newBackgroundImage.release();
73 hasBackground_ = true;
74 }
agsantos5aa39652020-08-11 18:18:04 -040075}
76
77void
78PluginProcessor::initModel()
79{
agsantosac1940d2020-09-17 10:18:40 -040080 try {
81 pluginInference.init();
82 } catch (std::exception& e) {
83 Plog::log(Plog::LogPriority::ERR, TAG, e.what());
84 }
85 std::ostringstream oss;
86 oss << "Model is allocated " << pluginInference.isAllocated();
87 Plog::log(Plog::LogPriority::INFO, TAG, oss.str());
agsantos5aa39652020-08-11 18:18:04 -040088}
89
agsantos5aa39652020-08-11 18:18:04 -040090#ifdef TFLITE
91void
92PluginProcessor::feedInput(const cv::Mat& frame)
93{
agsantosac1940d2020-09-17 10:18:40 -040094 auto pair = pluginInference.getInput();
95 uint8_t* inputPointer = pair.first;
agsantos5aa39652020-08-11 18:18:04 -040096
agsantosac1940d2020-09-17 10:18:40 -040097 cv::Mat temp(frame.rows, frame.cols, CV_8UC3, inputPointer);
98 frame.convertTo(temp, CV_8UC3);
agsantos5aa39652020-08-11 18:18:04 -040099
agsantosac1940d2020-09-17 10:18:40 -0400100 inputPointer = nullptr;
agsantos5aa39652020-08-11 18:18:04 -0400101}
102#else
103void
104PluginProcessor::feedInput(const cv::Mat& frame)
105{
agsantosac1940d2020-09-17 10:18:40 -0400106 pluginInference.ReadTensorFromMat(frame);
agsantos5aa39652020-08-11 18:18:04 -0400107}
agsantosac1940d2020-09-17 10:18:40 -0400108#endif // TFLITE
agsantos5aa39652020-08-11 18:18:04 -0400109
110int
111PluginProcessor::getBackgroundRotation()
112{
agsantosac1940d2020-09-17 10:18:40 -0400113 return backgroundRotation;
agsantos5aa39652020-08-11 18:18:04 -0400114}
115
116void
117PluginProcessor::setBackgroundRotation(int angle)
118{
agsantosac1940d2020-09-17 10:18:40 -0400119 if (backgroundRotation != angle && (backgroundRotation - angle) != 0) {
agsantosb74f4cb2020-10-01 14:30:43 -0400120 rotateFrame(backgroundRotation - angle, backgroundImage);
agsantosac1940d2020-09-17 10:18:40 -0400121 backgroundRotation = angle;
122 }
agsantos5aa39652020-08-11 18:18:04 -0400123}
124
125void
agsantosac1940d2020-09-17 10:18:40 -0400126PluginProcessor::computePredictions()
agsantos5aa39652020-08-11 18:18:04 -0400127{
agsantosb74f4cb2020-10-01 14:30:43 -0400128 if (count == 0) {
129 // Run the graph
130 pluginInference.runGraph();
131 auto predictions = pluginInference.masksPredictions();
agsantos5aa39652020-08-11 18:18:04 -0400132
agsantosb74f4cb2020-10-01 14:30:43 -0400133 // Save the predictions
134 computedMask = predictions;
135 }
agsantos5aa39652020-08-11 18:18:04 -0400136}
137
138void
139PluginProcessor::printMask()
140{
agsantosac1940d2020-09-17 10:18:40 -0400141 for (size_t i = 0; i < computedMask.size(); i++) {
142 // Log the predictions
143 std::ostringstream oss;
144 oss << "\nclass: " << computedMask[i] << std::endl;
145 Plog::log(Plog::LogPriority::INFO, TAG, oss.str());
146 }
agsantos5aa39652020-08-11 18:18:04 -0400147}
148
agsantos5aa39652020-08-11 18:18:04 -0400149void
agsantos31d1a0b2020-10-23 14:05:53 -0400150PluginProcessor::resetInitValues(const cv::Size& modelInputSize)
151{
152 previousMasks[0] = cv::Mat(modelInputSize.height, modelInputSize.width, CV_32FC1, double(0.));
153 previousMasks[1] = cv::Mat(modelInputSize.height, modelInputSize.width, CV_32FC1, double(0.));
154 kSize = cv::Size(modelInputSize.width * kernelSize, modelInputSize.height * kernelSize);
155 if (kSize.height % 2 == 0) {
156 kSize.height -= 1;
157 }
158 if (kSize.width % 2 == 0) {
159 kSize.width -= 1;
160 }
161 count = 0;
162 grabCutMode = cv::GC_INIT_WITH_MASK;
163 grabCutIterations = 5;
164}
165
166void
agsantos5aa39652020-08-11 18:18:04 -0400167copyByLine(uchar* frameData, uchar* applyMaskData, const int lineSize, cv::Size size)
168{
agsantosac1940d2020-09-17 10:18:40 -0400169 if (3 * size.width == lineSize) {
170 std::memcpy(frameData, applyMaskData, size.height * size.width * 3);
agsantosac1940d2020-09-17 10:18:40 -0400171 } else {
172 int rows = size.height;
173 int offset = 0;
174 int maskoffset = 0;
175 for (int i = 0; i < rows; i++) {
176 std::memcpy(frameData + offset, applyMaskData + maskoffset, lineSize);
177 offset += lineSize;
178 maskoffset += 3 * size.width;
179 }
180 }
agsantos5aa39652020-08-11 18:18:04 -0400181}
182
183void
agsantosac1940d2020-09-17 10:18:40 -0400184PluginProcessor::drawMaskOnFrame(
185 cv::Mat& frame, cv::Mat& frameReduced, std::vector<float> computedMask, int lineSize, int angle)
agsantos5aa39652020-08-11 18:18:04 -0400186{
agsantosac1940d2020-09-17 10:18:40 -0400187 if (computedMask.empty()) {
188 return;
189 }
agsantosb74f4cb2020-10-01 14:30:43 -0400190
agsantosac1940d2020-09-17 10:18:40 -0400191 int maskSize = static_cast<int>(std::sqrt(computedMask.size()));
192 cv::Mat maskImg(maskSize, maskSize, CV_32FC1, computedMask.data());
agsantosb74f4cb2020-10-01 14:30:43 -0400193 cv::Mat* applyMask = &frameReduced;
194 cv::Mat output;
agsantos5aa39652020-08-11 18:18:04 -0400195
agsantosb74f4cb2020-10-01 14:30:43 -0400196 if (count == 0) {
197 rotateFrame(-angle, maskImg);
198#ifdef TFLITE
199 for (int i = 0; i < maskImg.cols; i++) {
200 for (int j = 0; j < maskImg.rows; j++) {
201 if (maskImg.at<float>(j, i) == 15)
202 maskImg.at<float>(j, i) = 1.;
203 else
204 maskImg.at<float>(j, i) = smoothFactors[0] * previousMasks[0].at<float>(j, i)
205 + smoothFactors[1] * previousMasks[1].at<float>(j, i);
206 }
207 }
208 cv::morphologyEx(maskImg,
209 maskImg,
210 cv::MORPH_CLOSE,
211 cv::getStructuringElement(cv::MORPH_ELLIPSE, kSize),
212 cv::Point(-1, -1),
213 4);
214#else
215 cv::resize(maskImg, maskImg, cv::Size(frameReduced.cols, frameReduced.rows));
agsantos5aa39652020-08-11 18:18:04 -0400216
agsantosb74f4cb2020-10-01 14:30:43 -0400217 double m, M;
218 cv::minMaxLoc(maskImg, &m, &M);
219
220 if (M < 2) { // avoid detection if there is any one in frame
221 maskImg = 0. * maskImg;
222 } else {
223 for (int i = 0; i < maskImg.cols; i++) {
224 for (int j = 0; j < maskImg.rows; j++) {
225 maskImg.at<float>(j, i) = (maskImg.at<float>(j, i) - m) / (M - m);
226
227 if (maskImg.at<float>(j, i) < 0.4)
228 maskImg.at<float>(j, i) = 0.;
229 else if (maskImg.at<float>(j, i) < 0.7) {
230 float value = maskImg.at<float>(j, i) * smoothFactors[0]
231 + previousMasks[0].at<float>(j, i) * smoothFactors[1]
232 + previousMasks[1].at<float>(j, i) * smoothFactors[2];
233 maskImg.at<float>(j, i) = 0.;
234 if (value > 0.7)
235 maskImg.at<float>(j, i) = 1.;
236 } else
237 maskImg.at<float>(j, i) = 1.;
238 }
239 }
240 }
241#endif
242 if (cv::countNonZero(maskImg) != 0) {
243#ifdef TFLITE
244 cv::Mat tfMask;
245 tfMask = maskImg.clone();
246 tfMask *= 255.;
247 tfMask.convertTo(tfMask, CV_8UC1);
248 cv::threshold(tfMask, tfMask, 127, 255, cv::THRESH_BINARY);
249 if (cv::countNonZero(tfMask) != 0) {
250#endif
251 cv::Mat dilate;
agsantos31d1a0b2020-10-23 14:05:53 -0400252 cv::dilate(maskImg,
253 dilate,
254 cv::getStructuringElement(cv::MORPH_ELLIPSE, kSize),
255 cv::Point(-1, -1),
256 2);
agsantosb74f4cb2020-10-01 14:30:43 -0400257 cv::erode(maskImg,
258 maskImg,
259 cv::getStructuringElement(cv::MORPH_ELLIPSE, kSize),
260 cv::Point(-1, -1),
261 2);
262 for (int i = 0; i < maskImg.cols; i++) {
263 for (int j = 0; j < maskImg.rows; j++) {
264 if (dilate.at<float>(j, i) != maskImg.at<float>(j, i))
265 maskImg.at<float>(j, i) = grabcutClass;
266 }
267 }
268 maskImg.convertTo(maskImg, CV_8UC1);
269 applyMask->convertTo(*applyMask, CV_8UC1);
270 cv::Rect rect(1, 1, maskImg.rows, maskImg.cols);
agsantos31d1a0b2020-10-23 14:05:53 -0400271 cv::grabCut(*applyMask,
272 maskImg,
273 rect,
274 bgdModel,
275 fgdModel,
276 grabCutIterations,
277 grabCutMode);
278
279 grabCutMode = cv::GC_EVAL;
280 grabCutIterations = 1;
281
agsantosb74f4cb2020-10-01 14:30:43 -0400282 maskImg = maskImg & 1;
283#ifdef TFLITE
284 cv::bitwise_and(maskImg, tfMask, maskImg);
285 }
286#endif
287 maskImg.convertTo(maskImg, CV_32FC1);
288 maskImg *= 255.;
agsantos31d1a0b2020-10-23 14:05:53 -0400289 GaussianBlur(maskImg, maskImg, cv::Size(7, 7), 0); // float mask from 0 to 255.
agsantosb74f4cb2020-10-01 14:30:43 -0400290 maskImg = maskImg / 255.;
291 }
292 previousMasks[1] = previousMasks[0].clone();
293 previousMasks[0] = maskImg.clone();
294 }
295
296 cv::Mat roiMaskImg = previousMasks[0].clone();
agsantosac1940d2020-09-17 10:18:40 -0400297 cv::Mat roiMaskImgComplementary = 1. - roiMaskImg; // mask from 1. to 0
agsantos5aa39652020-08-11 18:18:04 -0400298
agsantosac1940d2020-09-17 10:18:40 -0400299 std::vector<cv::Mat> channels;
300 std::vector<cv::Mat> channelsComplementary;
agsantos5aa39652020-08-11 18:18:04 -0400301
agsantosac1940d2020-09-17 10:18:40 -0400302 channels.emplace_back(roiMaskImg);
303 channels.emplace_back(roiMaskImg);
304 channels.emplace_back(roiMaskImg);
305 channelsComplementary.emplace_back(roiMaskImgComplementary);
306 channelsComplementary.emplace_back(roiMaskImgComplementary);
307 channelsComplementary.emplace_back(roiMaskImgComplementary);
agsantos5aa39652020-08-11 18:18:04 -0400308
agsantosac1940d2020-09-17 10:18:40 -0400309 cv::merge(channels, roiMaskImg);
310 cv::merge(channelsComplementary, roiMaskImgComplementary);
agsantos5aa39652020-08-11 18:18:04 -0400311
agsantosac1940d2020-09-17 10:18:40 -0400312 int origType = frameReduced.type();
313 int roiMaskType = roiMaskImg.type();
agsantos5aa39652020-08-11 18:18:04 -0400314
agsantosb74f4cb2020-10-01 14:30:43 -0400315 frameReduced.convertTo(output, roiMaskType);
316 output = output.mul(roiMaskImg);
317 output += backgroundImage.mul(roiMaskImgComplementary);
318 output.convertTo(output, origType);
agsantos5aa39652020-08-11 18:18:04 -0400319
agsantosb74f4cb2020-10-01 14:30:43 -0400320 cv::resize(output, output, cv::Size(frame.cols, frame.rows));
agsantos5aa39652020-08-11 18:18:04 -0400321
agsantosb74f4cb2020-10-01 14:30:43 -0400322 copyByLine(frame.data, output.data, lineSize, cv::Size(frame.cols, frame.rows));
323 count++;
324 count = count % frameCount;
agsantos5aa39652020-08-11 18:18:04 -0400325}
326
327void
328PluginProcessor::rotateFrame(int angle, cv::Mat& mat)
329{
agsantosb74f4cb2020-10-01 14:30:43 -0400330 if (angle == -90)
331 cv::rotate(mat, mat, cv::ROTATE_90_COUNTERCLOCKWISE);
332 else if (std::abs(angle) == 180)
333 cv::rotate(mat, mat, cv::ROTATE_180);
334 else if (angle == 90)
335 cv::rotate(mat, mat, cv::ROTATE_90_CLOCKWISE);
agsantos5aa39652020-08-11 18:18:04 -0400336}
agsantos9dcf4302020-09-01 18:21:48 -0400337
338bool
339PluginProcessor::hasBackground() const
340{
341 return hasBackground_;
342}
agsantos5aa39652020-08-11 18:18:04 -0400343} // namespace jami