blob: d289b878d79ca59ed1f7488ea2dd8462b0bfe260 [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
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 */
20
21#include "pluginProcessor.h"
22// System includes
23#include <algorithm>
24#include <cstring>
25// OpenCV headers
26#include <opencv2/imgproc.hpp>
27#include <opencv2/imgcodecs.hpp>
28#include <opencv2/core.hpp>
29// Logger
30#include <pluglog.h>
31
32extern "C" {
33#include <libavutil/display.h>
34}
35
36const char sep = separator();
37
38const std::string TAG = "FORESEG";
39
40PluginParameters* mPluginParameters = getGlobalPluginParameters();
41
42namespace jami
43{
44
45PluginProcessor::PluginProcessor(const std::string& dataPath):
agsantos364bbae2020-08-17 11:29:56 -040046pluginInference{TFModel{dataPath + sep + "models" + sep + mPluginParameters->model}}
agsantos5aa39652020-08-11 18:18:04 -040047{
48 initModel();
agsantos9dcf4302020-09-01 18:21:48 -040049 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{
agsantos5aa39652020-08-11 18:18:04 -040055 cv::Size size = cv::Size{0,0};
56
57 if (!backgroundImage.empty())
58 size = backgroundImage.size();
59
agsantos9dcf4302020-09-01 18:21:48 -040060 cv::Mat newBackgroundImage = cv::imread(backgroundPath);
61 if (newBackgroundImage.cols == 0) {
agsantos5aa39652020-08-11 18:18:04 -040062 Plog::log(Plog::LogPriority::ERR, TAG, "Background image not Loaded");
63 }
64 else {
65 Plog::log(Plog::LogPriority::INFO, TAG, "Background image Loaded");
agsantos9dcf4302020-09-01 18:21:48 -040066 cv::cvtColor(newBackgroundImage, newBackgroundImage, cv::COLOR_BGR2RGB);
67 newBackgroundImage.convertTo(newBackgroundImage, CV_32FC3);
68 if (size.height) {
69 cv::resize(newBackgroundImage, newBackgroundImage, size);
70 backgroundRotation = 0;
71 }
72 backgroundImage = newBackgroundImage.clone();
73 newBackgroundImage.release();
74 hasBackground_ = true;
agsantos5aa39652020-08-11 18:18:04 -040075 }
76}
77
78void
79PluginProcessor::initModel()
80{
81 try {
82 pluginInference.init();
83 }
84 catch (std::exception& e) {
85 Plog::log(Plog::LogPriority::ERR, TAG, e.what());
86 }
87 std::ostringstream oss;
88 oss << "Model is allocated " << pluginInference.isAllocated();
89 Plog::log(Plog::LogPriority::INFO, TAG, oss.str());
90}
91
92
93#ifdef TFLITE
94void
95PluginProcessor::feedInput(const cv::Mat& frame)
96{
97 auto pair = pluginInference.getInput();
98 uint8_t* inputPointer = pair.first;
99
100 cv::Mat temp(frame.rows, frame.cols, CV_8UC3, inputPointer);
101 frame.convertTo(temp, CV_8UC3);
102
103 inputPointer = nullptr;
104}
105#else
106void
107PluginProcessor::feedInput(const cv::Mat& frame)
108{
109 pluginInference.ReadTensorFromMat(frame);
110}
111#endif //TFLITE
112
113int
114PluginProcessor::getBackgroundRotation()
115{
116 return backgroundRotation;
117}
118
119void
120PluginProcessor::setBackgroundRotation(int angle)
121{
122 if (backgroundRotation != angle && (backgroundRotation - angle) != 0) {
123 switch (backgroundRotation - angle) {
124 case 90:
125 cv::rotate(backgroundImage, backgroundImage, cv::ROTATE_90_CLOCKWISE);
126 break;
127 case 180:
128 cv::rotate(backgroundImage, backgroundImage, cv::ROTATE_180);
129 break;
130 case -180:
131 cv::rotate(backgroundImage, backgroundImage, cv::ROTATE_180);
132 break;
133 case -90:
134 cv::rotate(backgroundImage, backgroundImage, cv::ROTATE_90_COUNTERCLOCKWISE);
135 break;
136 }
137 backgroundRotation = angle;
138 }
139}
140
141void
142PluginProcessor::computePredictions()
143{
144 // Run the graph
145 pluginInference.runGraph();
146 auto predictions = pluginInference.masksPredictions();
147
148 // Save the predictions
149 computedMask = predictions;
150}
151
152void
153PluginProcessor::printMask()
154{
155 for (size_t i = 0; i < computedMask.size(); i++)
156 {
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 }
162}
163
164
165void
166copyByLine(uchar* frameData, uchar* applyMaskData, const int lineSize, cv::Size size)
167{
168 if (3 * size.width == lineSize) {
169 std::memcpy(frameData, applyMaskData, size.height * size.width * 3);;
170 }
171 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 }
181}
182
183void
184PluginProcessor::drawMaskOnFrame(cv::Mat& frame,
185 cv::Mat& frameReduced,
186 std::vector<float>computedMask,
187 int lineSize, int angle)
188{
189 if (computedMask.empty()) {
190 return;
191 }
192 if (previousMasks[0].empty()) {
193 previousMasks[0] = cv::Mat(frameReduced.rows, frameReduced.cols, CV_32FC1, double(0.));
194 previousMasks[1] = cv::Mat(frameReduced.rows, frameReduced.cols, CV_32FC1, double(0.));
195 }
196 int maskSize = static_cast<int> (std::sqrt(computedMask.size()));
197 cv::Mat maskImg(maskSize, maskSize, CV_32FC1, computedMask.data());
198
199 rotateFrame(-angle, maskImg);
200#ifdef TFLITE
201 for (int i = 0; i < maskImg.cols; i++) {
202 for (int j = 0; j < maskImg.rows; j++) {
203 if (maskImg.at<float>(j, i) == 15)
204 maskImg.at<float>(j, i) = 255.;
205 else
206 maskImg.at<float>(j, i) = (float)((int)((0.6 * maskImg.at<float>(j, i) + 0.3 * previousMasks[0].at<float>(j, i) + 0.1 * previousMasks[1].at<float>(j, i))) % 256);
207 }
208 }
209#else // TFLITE
210 cv::resize(maskImg, maskImg, cv::Size(frameReduced.cols, frameReduced.rows));
211
212 double m, M;
213 cv::minMaxLoc(maskImg, &m, &M);
214
215 if (M < 2) { //avoid detection if there is any one in frame
216 maskImg = 0. * maskImg;
217 }
218 else {
219 for (int i = 0; i < maskImg.cols; i++) {
220 for (int j = 0; j < maskImg.rows; j++) {
221 maskImg.at<float>(j, i) = (maskImg.at<float>(j, i) - m) / (M - m);
222
223 if (maskImg.at<float>(j, i) < 0.4)
224 maskImg.at<float>(j, i) = 0.;
225 else if (maskImg.at<float>(j, i) < 0.7) {
226 float value = maskImg.at<float>(j, i) * 0.6 + previousMasks[0].at<float>(j, i) * 0.3 + previousMasks[1].at<float>(j, i) * 0.1;
227 maskImg.at<float>(j, i) = 0.;
228 if (value > 0.7)
229 maskImg.at<float>(j, i) = 1.;
230 }
231 else
232 maskImg.at<float>(j, i) = 1.;
233 }
234 }
235 }
236#endif
237
238 previousMasks[1] = previousMasks[0].clone();
239 previousMasks[0] = maskImg.clone();
240
241 kSize = cv::Size(maskImg.cols * 0.05, maskImg.rows * 0.05);
242 if (kSize.height % 2 == 0)
243 kSize.height -= 1;
244 if (kSize.width % 2 == 0)
245 kSize.width -= 1;
246
247#ifndef TFLITE
248 cv::dilate(maskImg, maskImg, cv::getStructuringElement(cv::MORPH_CROSS, kSize));
249 maskImg = maskImg * 255.;
250#endif
251 GaussianBlur (maskImg, maskImg, kSize, 0); //mask from 0 to 255.
252 maskImg = maskImg / 255.;
253
254 cv::Mat applyMask = frameReduced.clone();
255 cv::Mat roiMaskImg = maskImg.clone();
256 cv::Mat roiMaskImgComplementary = 1. - roiMaskImg; //mask from 1. to 0
257
258 std::vector<cv::Mat> channels;
259 std::vector<cv::Mat> channelsComplementary;
260
261 channels.emplace_back(roiMaskImg);
262 channels.emplace_back(roiMaskImg);
263 channels.emplace_back(roiMaskImg);
264 channelsComplementary.emplace_back(roiMaskImgComplementary);
265 channelsComplementary.emplace_back(roiMaskImgComplementary);
266 channelsComplementary.emplace_back(roiMaskImgComplementary);
267
268 cv::merge(channels, roiMaskImg);
269 cv::merge(channelsComplementary, roiMaskImgComplementary);
270
271 int origType = frameReduced.type();
272 int roiMaskType = roiMaskImg.type();
273
274 applyMask.convertTo(applyMask, roiMaskType);
275 applyMask = applyMask.mul(roiMaskImg);
276 applyMask += backgroundImage.mul(roiMaskImgComplementary);
277 applyMask.convertTo(applyMask, origType);
278
279 cv::resize(applyMask, applyMask, cv::Size(frame.cols, frame.rows));
280
281 copyByLine(frame.data, applyMask.data, lineSize, cv::Size(frame.cols, frame.rows));
282}
283
284void
285PluginProcessor::rotateFrame(int angle, cv::Mat& mat)
286{
287 if (angle != 0) {
288 switch (angle) {
289 case -90:
290 cv::rotate(mat, mat, cv::ROTATE_90_COUNTERCLOCKWISE);
291 break;
292 case 180:
293 cv::rotate(mat, mat, cv::ROTATE_180);
294 break;
295 case -180:
296 cv::rotate(mat, mat, cv::ROTATE_180);
297 break;
298 case 90:
299 cv::rotate(mat, mat, cv::ROTATE_90_CLOCKWISE);
300 break;
301 }
302 }
303}
agsantos9dcf4302020-09-01 18:21:48 -0400304
305bool
306PluginProcessor::hasBackground() const
307{
308 return hasBackground_;
309}
agsantos5aa39652020-08-11 18:18:04 -0400310} // namespace jami