blob: 991bf81b68e8ea658f69796d17b0f780c7765e49 [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();
49 setBackgroundImage(dataPath, mPluginParameters->image);
50}
51
52void
53PluginProcessor::setBackgroundImage(const std::string& dataPath, const std::string& value)
54{
agsantos364bbae2020-08-17 11:29:56 -040055 backgroundPath = dataPath + sep + "backgrounds" + sep + value; //
agsantos5aa39652020-08-11 18:18:04 -040056 cv::Size size = cv::Size{0,0};
57
58 if (!backgroundImage.empty())
59 size = backgroundImage.size();
60
61 backgroundImage = cv::imread(backgroundPath);
62 if (backgroundImage.cols == 0) {
63 Plog::log(Plog::LogPriority::ERR, TAG, "Background image not Loaded");
64 }
65 else {
66 Plog::log(Plog::LogPriority::INFO, TAG, "Background image Loaded");
67 }
68
69 cv::cvtColor(backgroundImage, backgroundImage, cv::COLOR_BGR2RGB);
70 backgroundImage.convertTo(backgroundImage, CV_32FC3);
71 if (size.height) {
72 cv::resize(backgroundImage, backgroundImage, size);
73 backgroundRotation = 0;
74 }
75}
76
77void
78PluginProcessor::initModel()
79{
80 try {
81 pluginInference.init();
82 }
83 catch (std::exception& e) {
84 Plog::log(Plog::LogPriority::ERR, TAG, e.what());
85 }
86 std::ostringstream oss;
87 oss << "Model is allocated " << pluginInference.isAllocated();
88 Plog::log(Plog::LogPriority::INFO, TAG, oss.str());
89}
90
91
92#ifdef TFLITE
93void
94PluginProcessor::feedInput(const cv::Mat& frame)
95{
96 auto pair = pluginInference.getInput();
97 uint8_t* inputPointer = pair.first;
98
99 cv::Mat temp(frame.rows, frame.cols, CV_8UC3, inputPointer);
100 frame.convertTo(temp, CV_8UC3);
101
102 inputPointer = nullptr;
103}
104#else
105void
106PluginProcessor::feedInput(const cv::Mat& frame)
107{
108 pluginInference.ReadTensorFromMat(frame);
109}
110#endif //TFLITE
111
112int
113PluginProcessor::getBackgroundRotation()
114{
115 return backgroundRotation;
116}
117
118void
119PluginProcessor::setBackgroundRotation(int angle)
120{
121 if (backgroundRotation != angle && (backgroundRotation - angle) != 0) {
122 switch (backgroundRotation - angle) {
123 case 90:
124 cv::rotate(backgroundImage, backgroundImage, cv::ROTATE_90_CLOCKWISE);
125 break;
126 case 180:
127 cv::rotate(backgroundImage, backgroundImage, cv::ROTATE_180);
128 break;
129 case -180:
130 cv::rotate(backgroundImage, backgroundImage, cv::ROTATE_180);
131 break;
132 case -90:
133 cv::rotate(backgroundImage, backgroundImage, cv::ROTATE_90_COUNTERCLOCKWISE);
134 break;
135 }
136 backgroundRotation = angle;
137 }
138}
139
140void
141PluginProcessor::computePredictions()
142{
143 // Run the graph
144 pluginInference.runGraph();
145 auto predictions = pluginInference.masksPredictions();
146
147 // Save the predictions
148 computedMask = predictions;
149}
150
151void
152PluginProcessor::printMask()
153{
154 for (size_t i = 0; i < computedMask.size(); i++)
155 {
156 // Log the predictions
157 std::ostringstream oss;
158 oss << "\nclass: "<< computedMask[i] << std::endl;
159 Plog::log(Plog::LogPriority::INFO, TAG, oss.str());
160 }
161}
162
163
164void
165copyByLine(uchar* frameData, uchar* applyMaskData, const int lineSize, cv::Size size)
166{
167 if (3 * size.width == lineSize) {
168 std::memcpy(frameData, applyMaskData, size.height * size.width * 3);;
169 }
170 else {
171 int rows = size.height;
172 int offset = 0;
173 int maskoffset = 0;
174 for (int i = 0; i < rows; i++) {
175 std::memcpy(frameData + offset, applyMaskData + maskoffset, lineSize);
176 offset += lineSize;
177 maskoffset += 3 * size.width;
178 }
179 }
180}
181
182void
183PluginProcessor::drawMaskOnFrame(cv::Mat& frame,
184 cv::Mat& frameReduced,
185 std::vector<float>computedMask,
186 int lineSize, int angle)
187{
188 if (computedMask.empty()) {
189 return;
190 }
191 if (previousMasks[0].empty()) {
192 previousMasks[0] = cv::Mat(frameReduced.rows, frameReduced.cols, CV_32FC1, double(0.));
193 previousMasks[1] = cv::Mat(frameReduced.rows, frameReduced.cols, CV_32FC1, double(0.));
194 }
195 int maskSize = static_cast<int> (std::sqrt(computedMask.size()));
196 cv::Mat maskImg(maskSize, maskSize, CV_32FC1, computedMask.data());
197
198 rotateFrame(-angle, maskImg);
199#ifdef TFLITE
200 for (int i = 0; i < maskImg.cols; i++) {
201 for (int j = 0; j < maskImg.rows; j++) {
202 if (maskImg.at<float>(j, i) == 15)
203 maskImg.at<float>(j, i) = 255.;
204 else
205 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);
206 }
207 }
208#else // TFLITE
209 cv::resize(maskImg, maskImg, cv::Size(frameReduced.cols, frameReduced.rows));
210
211 double m, M;
212 cv::minMaxLoc(maskImg, &m, &M);
213
214 if (M < 2) { //avoid detection if there is any one in frame
215 maskImg = 0. * maskImg;
216 }
217 else {
218 for (int i = 0; i < maskImg.cols; i++) {
219 for (int j = 0; j < maskImg.rows; j++) {
220 maskImg.at<float>(j, i) = (maskImg.at<float>(j, i) - m) / (M - m);
221
222 if (maskImg.at<float>(j, i) < 0.4)
223 maskImg.at<float>(j, i) = 0.;
224 else if (maskImg.at<float>(j, i) < 0.7) {
225 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;
226 maskImg.at<float>(j, i) = 0.;
227 if (value > 0.7)
228 maskImg.at<float>(j, i) = 1.;
229 }
230 else
231 maskImg.at<float>(j, i) = 1.;
232 }
233 }
234 }
235#endif
236
237 previousMasks[1] = previousMasks[0].clone();
238 previousMasks[0] = maskImg.clone();
239
240 kSize = cv::Size(maskImg.cols * 0.05, maskImg.rows * 0.05);
241 if (kSize.height % 2 == 0)
242 kSize.height -= 1;
243 if (kSize.width % 2 == 0)
244 kSize.width -= 1;
245
246#ifndef TFLITE
247 cv::dilate(maskImg, maskImg, cv::getStructuringElement(cv::MORPH_CROSS, kSize));
248 maskImg = maskImg * 255.;
249#endif
250 GaussianBlur (maskImg, maskImg, kSize, 0); //mask from 0 to 255.
251 maskImg = maskImg / 255.;
252
253 cv::Mat applyMask = frameReduced.clone();
254 cv::Mat roiMaskImg = maskImg.clone();
255 cv::Mat roiMaskImgComplementary = 1. - roiMaskImg; //mask from 1. to 0
256
257 std::vector<cv::Mat> channels;
258 std::vector<cv::Mat> channelsComplementary;
259
260 channels.emplace_back(roiMaskImg);
261 channels.emplace_back(roiMaskImg);
262 channels.emplace_back(roiMaskImg);
263 channelsComplementary.emplace_back(roiMaskImgComplementary);
264 channelsComplementary.emplace_back(roiMaskImgComplementary);
265 channelsComplementary.emplace_back(roiMaskImgComplementary);
266
267 cv::merge(channels, roiMaskImg);
268 cv::merge(channelsComplementary, roiMaskImgComplementary);
269
270 int origType = frameReduced.type();
271 int roiMaskType = roiMaskImg.type();
272
273 applyMask.convertTo(applyMask, roiMaskType);
274 applyMask = applyMask.mul(roiMaskImg);
275 applyMask += backgroundImage.mul(roiMaskImgComplementary);
276 applyMask.convertTo(applyMask, origType);
277
278 cv::resize(applyMask, applyMask, cv::Size(frame.cols, frame.rows));
279
280 copyByLine(frame.data, applyMask.data, lineSize, cv::Size(frame.cols, frame.rows));
281}
282
283void
284PluginProcessor::rotateFrame(int angle, cv::Mat& mat)
285{
286 if (angle != 0) {
287 switch (angle) {
288 case -90:
289 cv::rotate(mat, mat, cv::ROTATE_90_COUNTERCLOCKWISE);
290 break;
291 case 180:
292 cv::rotate(mat, mat, cv::ROTATE_180);
293 break;
294 case -180:
295 cv::rotate(mat, mat, cv::ROTATE_180);
296 break;
297 case 90:
298 cv::rotate(mat, mat, cv::ROTATE_90_CLOCKWISE);
299 break;
300 }
301 }
302}
303} // namespace jami