JeVois  1.21
JeVois Smart Embedded Machine Vision Toolkit
Share this page:
Loading...
Searching...
No Matches
PostProcessorSegment.C
Go to the documentation of this file.
1// ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
2//
3// JeVois Smart Embedded Machine Vision Toolkit - Copyright (C) 2021 by Laurent Itti, the University of Southern
4// California (USC), and iLab at USC. See http://iLab.usc.edu and http://jevois.org for information about this project.
5//
6// This file is part of the JeVois Smart Embedded Machine Vision Toolkit. This program is free software; you can
7// redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software
8// Foundation, version 2. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
9// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
10// License for more details. You should have received a copy of the GNU General Public License along with this program;
11// if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
12//
13// Contact information: Laurent Itti - 3641 Watt Way, HNB-07A - Los Angeles, CA 90089-2520 - USA.
14// Tel: +1 213 740 3527 - itti@pollux.usc.edu - http://iLab.usc.edu - http://jevois.org
15// ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
16/*! \file */
17
20#include <jevois/DNN/Utils.H>
22#include <jevois/Core/Module.H>
25
26#include <opencv2/dnn.hpp>
27
28// ####################################################################################################
30{
31#ifdef JEVOIS_PRO
32 if (itsHelper) itsHelper->releaseImage("ppsr");
33#endif
34}
35
36// ####################################################################################################
38{
39 segtype::freeze(doit);
40}
41
42// ####################################################################################################
44{
45 // Colormap from pascal VOC segmentation benchmark:
46 for (size_t i = 0; i < 256; ++i)
47 {
48 uint32_t & c = itsColor[i]; c = 0;
49 uint32_t ind = i;
50
51 for (int shift = 7; shift >= 0; --shift)
52 {
53 for (int channel = 0; channel < 3; ++channel) c |= ((ind >> channel) & 1) << (shift + 8 * (3-channel));
54 ind >>= 3;
55 }
56 }
57}
58
59// ####################################################################################################
60template <typename T>
61void jevois::dnn::PostProcessorSegment::process(cv::Mat const & results)
62{
63 int const bgclass = bgid::get();
64 uint32_t const alph = alpha::get() << 24;
65 cv::MatSize const rs = results.size;
66 T const * r = reinterpret_cast<T const *>(results.data);
67 T const thresh(cthresh::get() * 0.01F);
68
69 switch (segtype::get())
70 {
71 // ----------------------------------------------------------------------------------------------------
72 case jevois::dnn::postprocessor::SegType::ClassesHWC:
73 {
74 // tensor should be 4D 1xHxWxC, where C is the number of classes. We pick the class index with max value and
75 // apply the colormap to it:
76 if (rs.dims() != 4 || rs[0] != 1) LTHROW("Need 1xHxWxC for C classes");
77 int const numclass = rs[3]; int const siz = rs[1] * rs[2] * numclass;
78
79 // Apply colormap, converting from RGB to RGBA:
80 itsOverlay = cv::Mat(rs[1], rs[2], CV_8UC4);
81 uint32_t * im = reinterpret_cast<uint32_t *>(itsOverlay.data);
82
83 for (int i = 0; i < siz; i += numclass)
84 {
85 int maxc = -1; T maxval = thresh;
86 for (int c = 0; c < numclass; ++c)
87 {
88 T v = *r++;
89 if (v > maxval) { maxval = v; maxc = c; }
90 }
91
92 // Use full transparent for class bgclass or if out of bounds, otherwise colormap:
93 if (maxc < 0 || maxc > 255 || maxc == bgclass) *im++ = 0; else *im++ = itsColor[maxc] | alph;
94 }
95 }
96 break;
97
98 // ----------------------------------------------------------------------------------------------------
99 case jevois::dnn::postprocessor::SegType::ClassesCHW:
100 {
101 // tensor should be 4D 1xCxHxW, where C is the number of classes. We pick the class index with max value and
102 // apply the colormap to it:
103 if (rs.dims() != 4 || rs[0] != 1) LTHROW("Need 1xCxHxW for C classes");
104 int const numclass = rs[1]; int const hw = rs[2] * rs[3];
105
106 // Apply colormap, converting from RGB to RGBA:
107 itsOverlay = cv::Mat(rs[2], rs[3], CV_8UC4);
108 uint32_t * im = reinterpret_cast<uint32_t *>(itsOverlay.data);
109
110 for (int i = 0; i < hw; ++i)
111 {
112 int maxc = -1; T maxval = thresh;
113 for (int c = 0; c < numclass; ++c)
114 {
115 T v = results.at<T>(i + c * hw);
116 if (v > maxval) { maxval = v; maxc = c; }
117 }
118
119 // Use full transparent for class bgclass or if out of bounds, otherwise colormap:
120 if (maxc < 0 || maxc > 255 || maxc == bgclass) *im++ = 0; else *im++ = itsColor[maxc] | alph;
121 }
122 }
123 break;
124
125 // ----------------------------------------------------------------------------------------------------
126 case jevois::dnn::postprocessor::SegType::ArgMax:
127 {
128 // tensor should be 2D HxW, 3D 1xHxW, or 4D 1xHxWx1 and contain class ID in each pixel:
129 if (rs.dims() != 2 && (rs.dims() != 3 || rs[0] != 1) && (rs.dims() != 4 || rs[0] != 1 || rs[3] != 1))
130 LTHROW("Need shape HxW, 1xHxW, or 1xHxWx1 with class ID in each pixel");
131 int const siz = rs[1] * rs[2];
132
133 // Apply colormap, converting from RGB to RGBA:
134 itsOverlay = cv::Mat(rs[1], rs[2], CV_8UC4);
135 uint32_t * im = reinterpret_cast<uint32_t *>(itsOverlay.data);
136
137 for (int i = 0; i < siz; ++i)
138 {
139 // Use full transparent for class bgclass or if out of bounds, otherwise colormap:
140 int32_t const id = *r++;
141 if (id < 0 || id > 255 || id == bgclass) *im++ = 0; else *im++ = itsColor[id] | alph;
142 }
143 }
144 break;
145 }
146}
147
148// ####################################################################################################
149void jevois::dnn::PostProcessorSegment::process(std::vector<cv::Mat> const & outs, jevois::dnn::PreProcessor * preproc)
150{
151 try
152 {
153 if (outs.size() != 1) LTHROW("Need exactly one output blob");
154
155 // Patch up the colormap if background class ID is 0:
156 if (bgid::get() != 0) itsColor[0] = 0xff0000; else itsColor[0] = 0;
157
158 // Post-process:
159 cv::Mat const & results = outs[0];
160
161 switch (results.type())
162 {
163 case CV_8UC1: process<uint8_t>(results); break;
164 case CV_16UC1: process<uint16_t>(results); break;
165 case CV_32FC1: process<float>(results); break;
166 case CV_32SC1: process<int32_t>(results); break;
167
168 default: LTHROW("Unsupported data type in tensor " << jevois::dnn::shapestr(results));
169 }
170 }
171 // Abort here if the received outputs were malformed:
172 catch (std::exception const & e)
173 {
174 std::string err = "Selected segtype is " + segtype::strget() + " and network produced:\n\n";
175 for (cv::Mat const & m : outs) err += "- " + jevois::dnn::shapestr(m) + "\n";
176 err += "\nFATAL ERROR(s):\n\n";
177 err += e.what();
178 LFATAL(err);
179 }
180
181 // Compute overlay corner coords within the input image, for use in report():
182 preproc->getUnscaledCropRect(0, itsTLx, itsTLy, itsBRx, itsBRy);
183}
184
185// ####################################################################################################
187 jevois::OptGUIhelper * helper, bool /*overlay*/, bool /*idle*/)
188{
189 // Remember our helper, will be used in destructor to free overlay OpenGL texture:
190 itsHelper = helper;
191
192 // Outputs may not be ready yet:
193 if (itsOverlay.empty()) return;
194
195 // If desired, draw boxes in output image:
196 if (outimg)
197 {
198 // todo: blend into YUYV
199 }
200
201#ifdef JEVOIS_PRO
202 // Draw the image on top of our input image, as a semi-transparent overlay. OpenGL will do scaling and blending:
203 if (helper)
204 {
205 // Convert box coords from input image to display:
206 ImVec2 tl = helper->i2d(itsTLx, itsTLy), br = helper->i2d(itsBRx, itsBRy);
207 int dtlx = tl.x, dtly = tl.y;
208 unsigned short dw = br.x - tl.x, dh = br.y - tl.y;
209
210 // Draw overlay:
211 helper->drawImage("ppsr", itsOverlay, true, dtlx, dtly, dw, dh, false /* noalias */, true /* isoverlay */);
212 }
213#else
214 (void)helper; // keep compiler happy
215#endif
216
217 if (mod)
218 {
219 // todo send results to serial
220 }
221}
#define LTHROW(msg)
Definition Log.H:251
Helper class to assist modules in creating graphical and GUI elements.
Definition GUIhelper.H:133
void releaseImage(char const *name)
Release an image.
Definition GUIhelper.C:675
ImVec2 i2d(ImVec2 p, char const *name=nullptr)
Convert coordinates of a point from within a rendered image to on-screen.
Definition GUIhelper.C:402
void drawImage(char const *name, RawImage const &img, int &x, int &y, unsigned short &w, unsigned short &h, bool noalias=false, bool isoverlay=false)
Draw a RawImage, copying pixel data to an OpenGL texture.
Definition GUIhelper.C:289
A raw image as coming from a V4L2 Camera and/or being sent out to a USB Gadget.
Definition RawImage.H:111
Base class for a module that supports standardized serial messages.
Definition Module.H:234
void process(std::vector< cv::Mat > const &outs, PreProcessor *preproc) override
Process outputs and draw/send some results.
void report(jevois::StdModule *mod, jevois::RawImage *outimg=nullptr, jevois::OptGUIhelper *helper=nullptr, bool overlay=true, bool idle=false) override
Report what happened in last process() to console/output video/GUI.
void freeze(bool doit) override
Freeze/unfreeze parameters that users should not change while running.
void postInit() override
Create colormap in postInit()
Pre-Processor for neural network pipeline.
cv::Rect getUnscaledCropRect(size_t blobnum=0)
Get unscaled crop rectangle in image coordinates.
#define LFATAL(msg)
Convenience macro for users to print out console or syslog messages, FATAL level.
Definition Log.H:230
std::string shapestr(cv::Mat const &m)
Get a string of the form: "nD AxBxC... TYPE" from an n-dimensional cv::Mat with data type TYPE.
Definition Utils.C:105