JeVois  1.17
JeVois Smart Embedded Machine Vision Toolkit
Share this page:
PostProcessorDetect.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 
21 #include <jevois/DNN/Utils.H>
22 #include <jevois/Util/Utils.H>
24 #include <jevois/Core/Engine.H>
25 #include <jevois/Core/Module.H>
26 #include <jevois/GPU/GUIhelper.H>
27 
28 #include <opencv2/dnn.hpp>
29 
30 // ####################################################################################################
32 { }
33 
34 // ####################################################################################################
36 {
37  classes::freeze(doit);
38  detecttype::freeze(doit);
39  anchors::freeze(doit);
40 }
41 
42 // ####################################################################################################
43 void jevois::dnn::PostProcessorDetect::onParamChange(postprocessor::classes const & JEVOIS_UNUSED_PARAM(param),
44  std::string const & val)
45 {
46  if (val.empty()) { itsLabels.clear(); return; }
47 
48  // Get the dataroot of our network. We assume that there is a sub-component named "network" that is a sibling of us:
49  std::vector<std::string> dd = jevois::split(Component::descriptor(), ":");
50  dd.back() = "network"; dd.emplace_back("dataroot");
51  std::string const dataroot = engine()->getParamStringUnique(jevois::join(dd, ":"));
52 
53  itsLabels = jevois::dnn::readLabelsFile(jevois::absolutePath(dataroot, val));
54 }
55 
56 // ####################################################################################################
57 void jevois::dnn::PostProcessorDetect::onParamChange(postprocessor::anchors const & JEVOIS_UNUSED_PARAM(param),
58  std::string const & val)
59 {
60  if (val.empty()) { itsAnchors.clear(); return; }
61  auto tok = jevois::split(val, "\\s*;\\s*");
62  if (tok.size() >= 64) LFATAL("Maximum 32 anchors is supported");
63  for (std::string const & t : tok)
64  {
65  std::array<float, 64> a { };
66  auto atok = jevois::split(t, "\\s*,\\s*");
67  int i = 0;
68  for (std::string const & at : atok) a[i++] = std::stoi(at);
69  itsAnchors.emplace_back(std::move(a));
70  }
71 }
72 
73 // ####################################################################################################
74 void jevois::dnn::PostProcessorDetect::process(std::vector<cv::Mat> const & outs, jevois::dnn::PreProcessor * preproc)
75 {
76  if (outs.empty()) LFATAL("No outputs received, we need at least one.");
77  cv::Mat const & out = outs[0]; cv::MatSize const & msiz = out.size;
78 
79  float const confThreshold = thresh::get() * 0.01F;
80  float const nmsThreshold = nms::get() * 0.01F;
81  int const fudge = classoffset::get();
82  itsImageSize = preproc->imagesize();
83 
84  // To draw boxes, we will need to:
85  // - scale from [0..1]x[0..1] to blobw x blobh
86  // - scale and center from blobw x blobh to input image w x h, provided by PreProcessor::b2i()
87  // - when using the GUI, we further scale and translate to OpenGL display coordinates using GUIhelper::i2d()
88  // Here we assume that the first blob sets the input size.
89  cv::Size const bsiz = preproc->blobsize(0);
90 
91  // We keep 3 vectors here instead of creating a class to hold all of the data because OpenCV will need that for
92  // non-maximum suppression:
93  std::vector<int> classIds;
94  std::vector<float> confidences;
95  std::vector<cv::Rect> boxes;
96 
97  // Here we just scale the coords from [0..1]x[0..1] to blobw x blobh:
98  try
99  {
100  switch(detecttype::get())
101  {
102  // ----------------------------------------------------------------------------------------------------
103  case jevois::dnn::postprocessor::DetectType::FasterRCNN:
104  {
105  // Network produces output blob with a shape 1x1xNx7 where N is a number of detections and an every detection is
106  // a vector of values [batchId, classId, confidence, left, top, right, bottom]
107  if (outs.size() != 1 || msiz.dims() != 4 || msiz[0] != 1 || msiz[1] != 1 || msiz[3] != 7)
108  throw std::runtime_error("Expected 1 output blob with shape 1x1xNx7 for N detections with values "
109  "[batchId, classId, confidence, left, top, right, bottom]");
110 
111  float const * data = (float const *)out.data;
112  for (size_t i = 0; i < out.total(); i += 7)
113  {
114  float confidence = data[i + 2];
115  if (confidence > confThreshold)
116  {
117  int left = (int)data[i + 3];
118  int top = (int)data[i + 4];
119  int right = (int)data[i + 5];
120  int bottom = (int)data[i + 6];
121  int width = right - left + 1;
122  int height = bottom - top + 1;
123  classIds.push_back((int)(data[i + 1]) + fudge); // Skip 0th background class id.
124  boxes.push_back(cv::Rect(left, top, width, height));
125  confidences.push_back(confidence);
126  }
127  }
128  }
129  break;
130 
131  // ----------------------------------------------------------------------------------------------------
132  case jevois::dnn::postprocessor::DetectType::SSD:
133  {
134  // Network produces output blob with a shape 1x1xNx7 where N is a number of detections and an every detection is
135  // a vector of values [batchId, classId, confidence, left, top, right, bottom]
136  if (outs.size() != 1 || msiz.dims() != 4 || msiz[0] != 1 || msiz[1] != 1 || msiz[3] != 7)
137  throw std::runtime_error("Expected 1 output blob with shape 1x1xNx7 for N detections with values "
138  "[batchId, classId, confidence, left, top, right, bottom]");
139 
140  float const * data = (float const *)out.data;
141  for (size_t i = 0; i < out.total(); i += 7)
142  {
143  float confidence = data[i + 2];
144  if (confidence > confThreshold)
145  {
146  int left = (int)(data[i + 3] * bsiz.width);
147  int top = (int)(data[i + 4] * bsiz.height);
148  int right = (int)(data[i + 5] * bsiz.width);
149  int bottom = (int)(data[i + 6] * bsiz.height);
150  int width = right - left + 1;
151  int height = bottom - top + 1;
152  classIds.push_back((int)(data[i + 1]) + fudge); // Skip 0th background class id.
153  boxes.push_back(cv::Rect(left, top, width, height));
154  confidences.push_back(confidence);
155  }
156  }
157  }
158  break;
159 
160  // ----------------------------------------------------------------------------------------------------
161  case jevois::dnn::postprocessor::DetectType::TPUSSD:
162  {
163  // Network produces 4 output blobs with shapes 4xN for boxes, N for IDs, N for scores, and 1x1 for count
164  // (see GetDetectionResults in detection/adapter.cc of libcoral):
165  if (outs.size() != 4)
166  throw std::runtime_error("Expected 4 output blobs with shapes 4xN for boxes, N for IDs, N for scores, "
167  "and 1x1 for count");
168  cv::Mat const & bboxes = outs[0];
169  cv::Mat const & ids = outs[1];
170  cv::Mat const & scores = outs[2];
171  cv::Mat const & count = outs[3];
172  if (bboxes.total() != 4 * ids.total() || bboxes.total() != 4 * scores.total() || count.total() != 1)
173  throw std::runtime_error("Expected 4 output blobs with shapes 4xN for boxes, N for IDs, N for scores, "
174  "and 1x1 for count");
175 
176  size_t num = count.at<float>(0);
177  if (num > ids.total()) LFATAL("Too many detections: " << num << " for only " << ids.total() << " ids");
178  float const * bb = (float const *)bboxes.data;
179 
180  for (size_t i = 0; i < num; ++i)
181  {
182  if (scores.at<float>(i) < confThreshold) continue;
183 
184  int top = (int)(bb[4 * i] * bsiz.height);
185  int left = (int)(bb[4 * i + 1] * bsiz.width);
186  int bottom = (int)(bb[4 * i + 2] * bsiz.height);
187  int right = (int)(bb[4 * i + 3] * bsiz.width);
188  int width = right - left + 1;
189  int height = bottom - top + 1;
190  classIds.push_back((int)(ids.at<float>(i)) + fudge); // Skip 0th background class id.
191  boxes.push_back(cv::Rect(left, top, width, height));
192  confidences.push_back(scores.at<float>(i));
193  }
194  }
195  break;
196 
197  // ----------------------------------------------------------------------------------------------------
198  case jevois::dnn::postprocessor::DetectType::YOLO:
199  {
200  for (size_t i = 0; i < outs.size(); ++i)
201  {
202  // Network produces output blob with a shape Nx(5+C) where N is a number of detected objects and C is a number
203  // of classes + 5 where the first 5 numbers are [center_x, center_y, width, height, box score]
204  cv::Mat const & out = outs[i];
205  if (out.size.dims() != 2 || out.cols <= 5)
206  throw std::runtime_error("Expected 1 or more output blobs with shape Nx(5+C) where N is the number of "
207  "detected objects, C is the number of classes, and the first 5 columns are "
208  "[center_x, center_y, width, height, box score]. Incorrect size for output " +
209  std::to_string(i) + ": need Nx(5+C)");
210 
211  float const * data = (float const *)out.data;
212  for (int j = 0; j < out.rows; ++j, data += out.cols)
213  {
214  cv::Mat scores = out.row(j).colRange(5, out.cols);
215  cv::Point classIdPoint;
216  double confidence;
217  cv::minMaxLoc(scores, 0, &confidence, 0, &classIdPoint);
218  if (confidence > confThreshold)
219  {
220  int centerX = (int)(data[0] * bsiz.width);
221  int centerY = (int)(data[1] * bsiz.height);
222  int width = (int)(data[2] * bsiz.width);
223  int height = (int)(data[3] * bsiz.height);
224  int left = centerX - width / 2;
225  int top = centerY - height / 2;
226 
227  classIds.push_back(classIdPoint.x);
228  confidences.push_back((float)confidence);
229  boxes.push_back(cv::Rect(left, top, width, height));
230  }
231  }
232  }
233  }
234  break;
235 
236  // ----------------------------------------------------------------------------------------------------
237  case jevois::dnn::postprocessor::DetectType::RAWYOLOface:
238  {
239  if (outs.size() != 1)
240  throw std::runtime_error("Expected 1 raw YOLO output tensor 1x6xHxW");
241 
242  static float const defaultbiases[10]
243  {1.08*8,1.19*8, 3.42*8,4.41*8, 6.63*8,11.38*8, 9.42*8,5.11*8, 16.62*8,10.52*8};
244  float const * biases = itsAnchors.size() >= 1 ? itsAnchors[0].data() : defaultbiases;
245  jevois::dnn::npu::yolo(outs[0], classIds, confidences, boxes, itsLabels.size(), biases, 0,
246  confThreshold, bsiz, fudge);
247  }
248  break;
249 
250  // ----------------------------------------------------------------------------------------------------
251  case jevois::dnn::postprocessor::DetectType::RAWYOLOv2:
252  {
253  if (outs.size() != 1)
254  throw std::runtime_error("Expected 1 raw YOLO output tensor 1x(5+C)xHxW for C classes");
255 
256  static float const defaultbiases[10] { 0.738768*8,0.874946*8,2.422040*8,2.657040*8,4.309710*8,
257  7.044930*8,10.246000*8,4.594280*8,12.686800*8,11.874100*8 };
258  float const * biases = itsAnchors.size() >= 1 ? itsAnchors[0].data() : defaultbiases;
259  // Myriad-X model gives [1, 21125], reshape to [5, 25, 13, 13] for VOC or
260  // [1,71825] for COCO with 8 classes
261  if (outs[0].size.dims() == 2)
262  {
263  int const n = outs[0].size[1] / (13 * 13);
264  cv::Mat o = outs[0].reshape(0, { 1, n, 13, 13 });
265  jevois::dnn::npu::yolo(o, classIds, confidences, boxes, itsLabels.size(), biases, 0,
266  confThreshold, bsiz, fudge);
267  }
268  else
269  jevois::dnn::npu::yolo(outs[0], classIds, confidences, boxes, itsLabels.size(), biases, 0,
270  confThreshold, bsiz, fudge);
271  }
272  break;
273 
274  // ----------------------------------------------------------------------------------------------------
275  case jevois::dnn::postprocessor::DetectType::RAWYOLOv3:
276  case jevois::dnn::postprocessor::DetectType::RAWYOLOv4:
277  {
278  if (outs.size() != 3)
279  throw std::runtime_error("Expected 3 raw YOLO v3/v4 output tensors 1x(5+C)xHxW; 1x(5+C)xHxW; 1x(5+C)xHxW "
280  "for C classes");
281 
282  static float const defaultbiases[18] {10, 13, 16, 30, 33, 23, 30, 61, 62, 45,
283  59, 119, 116, 90, 156, 198, 373, 326};
284  float const * b0 = itsAnchors.size() >= 1 ? itsAnchors[0].data() : defaultbiases;
285  float const * b1 = itsAnchors.size() >= 2 ? itsAnchors[1].data() : b0;
286  float const * b2 = itsAnchors.size() >= 3 ? itsAnchors[2].data() : b1;
287  jevois::dnn::npu::yolo(outs[0], classIds, confidences, boxes, itsLabels.size(), b2, 2,
288  confThreshold, bsiz, fudge);
289  jevois::dnn::npu::yolo(outs[1], classIds, confidences, boxes, itsLabels.size(), b1, 1,
290  confThreshold, bsiz, fudge);
291  jevois::dnn::npu::yolo(outs[2], classIds, confidences, boxes, itsLabels.size(), b0, 0,
292  confThreshold, bsiz, fudge);
293  }
294  break;
295 
296  // ----------------------------------------------------------------------------------------------------
297  case jevois::dnn::postprocessor::DetectType::RAWYOLOv3tiny:
298  {
299  if (outs.size() != 2)
300  throw std::runtime_error("Expected 2 raw YOLO v3-tiny output tensors 1x(5+C)xHxW; 1x(5+C)xHxW "
301  "for C classes");
302 
303  static float const defaultbiases[12] {10, 14, 23, 27, 37, 58, 81, 82, 135, 169, 344, 319};
304  float const * b0 = itsAnchors.size() >= 1 ? itsAnchors[0].data() : defaultbiases;
305  float const * b1 = itsAnchors.size() >= 2 ? itsAnchors[1].data() : b0;
306  jevois::dnn::npu::yolo(outs[0], classIds, confidences, boxes, itsLabels.size(), b1, 1,
307  confThreshold, bsiz, fudge);
308  jevois::dnn::npu::yolo(outs[1], classIds, confidences, boxes, itsLabels.size(), b0, 0,
309  confThreshold, bsiz, fudge);
310  }
311  break;
312 
313  default:
314  LFATAL("Post-processor detecttype " << detecttype::strget() << " not available on this hardware");
315  }
316  }
317  // Abort here if the received outputs were malformed:
318  catch (std::exception const & e)
319  {
320  std::string err = "With detecttype " + detecttype::strget() + ", error: " + e.what() + "\n\n"
321  "Your network produced the following outputs:\n\n";
322  for (cv::Mat const & m : outs) err += "- " + jevois::dnn::shapestr(m) + "\n";
323  LFATAL(err);
324  }
325 
326  // Cleanup overlapping boxes:
327  std::vector<int> indices;
328  cv::dnn::NMSBoxes(boxes, confidences, confThreshold, nmsThreshold, indices);
329 
330  // Now clamp boxes to be within blob, and adjust the boxes from blob size to input image size:
331  for (cv::Rect & b : boxes)
332  {
333  jevois::dnn::clamp(b, bsiz.width, bsiz.height);
334 
335  cv::Point2f tl = b.tl(); preproc->b2i(tl.x, tl.y);
336  cv::Point2f br = b.br(); preproc->b2i(br.x, br.y);
337  b.x = tl.x; b.y = tl.y; b.width = br.x - tl.x; b.height = br.y - tl.y;
338  }
339 
340  // Store results:
341  itsDetections.clear();
342  for (size_t i = 0; i < indices.size(); ++i)
343  {
344  int idx = indices[i];
345  cv::Rect const & box = boxes[idx];
346  jevois::ObjReco o {confidences[idx] * 100.0f, jevois::dnn::getLabel(itsLabels, classIds[idx]) };
347  std::vector<jevois::ObjReco> ov;
348  ov.emplace_back(o);
349  jevois::ObjDetect od { box.x, box.y, box.x + box.width, box.y + box.height, ov };
350  itsDetections.emplace_back(od);
351  }
352 }
353 
354 // ####################################################################################################
356  jevois::OptGUIhelper * helper, bool overlay,
357  bool JEVOIS_UNUSED_PARAM(idle))
358 {
359  for (jevois::ObjDetect const & o : itsDetections)
360  {
361  std::string categ, label;
362 
363  if (o.reco.empty())
364  {
365  categ = "unknown";
366  label = "unknown";
367  }
368  else
369  {
370  categ = o.reco[0].category;
371  label = jevois::sformat("%s: %.2f", categ.c_str(), o.reco[0].score);
372  }
373 
374  // If desired, draw boxes in output image:
375  if (outimg && overlay)
376  {
377  jevois::rawimage::drawRect(*outimg, o.tlx, o.tly, o.brx - o.tlx, o.bry - o.tly, 2, jevois::yuyv::LightGreen);
378  jevois::rawimage::writeText(*outimg, label, o.tlx + 6, o.tly + 2, jevois::yuyv::LightGreen,
380  }
381 
382 #ifdef JEVOIS_PRO
383  // If desired, draw results on GUI:
384  if (helper)
385  {
386  int col = jevois::dnn::stringToRGBA(categ, 0xff);
387  helper->drawRect(o.tlx, o.tly, o.brx, o.bry, col, true);
388  helper->drawText(o.tlx + 3.0f, o.tly + 3.0f, label.c_str(), col);
389  }
390 #else
391  (void)helper; // keep compiler happy
392 #endif
393 
394  // If desired, send results to serial port:
395  if (mod) mod->sendSerialObjDetImg2D(itsImageSize.width, itsImageSize.height, o);
396  }
397 }
jevois::Component::descriptor
std::string descriptor() const
Get our full descriptor (including all parents) as [Instancename]:[...]:[...].
Definition: Component.C:276
jevois::imu::get
Data collection mode RAW means that the latest available raw data is returned each time get() is called
jevois::dnn::PreProcessor::blobsize
cv::Size blobsize(size_t num) const
Access the width and height of a given blob, accounting for NCHW or NHWC.
Definition: PreProcessor.C:37
jevois::dnn::PostProcessorDetect::freeze
void freeze(bool doit) override
Freeze/unfreeze parameters that users should not change while running.
Definition: PostProcessorDetect.C:35
jevois::dnn::clamp
void clamp(cv::Rect &r, int width, int height)
Clamp a rectangle to within given image width and height.
Definition: Utils.C:250
Module.H
PostProcessorDetect.H
RawImageOps.H
jevois::sformat
std::string sformat(char const *fmt,...) __attribute__((format(__printf__
Create a string using printf style arguments.
Definition: Utils.C:401
jevois::split
std::vector< std::string > split(std::string const &input, std::string const &regex="\\s+")
Split string into vector of tokens using a regex to specify what to split on; default regex splits by...
Definition: Utils.C:257
Utils.H
jevois::RawImage
A raw image as coming from a V4L2 Camera and/or being sent out to a USB Gadget.
Definition: RawImage.H:110
jevois::dnn::stringToRGBA
int stringToRGBA(std::string const &label, unsigned char alpha=128)
Compute a color from a label name.
Definition: Utils.C:76
o
#define o
Definition: Font10x20.C:6
jevois::dnn::readLabelsFile
std::map< int, std::string > readLabelsFile(std::string const &fname)
Read a label file.
Definition: Utils.C:25
jevois::GUIhelper::drawRect
void drawRect(float x1, float y1, float x2, float y2, ImU32 col=IM_COL32(128, 255, 128, 255), bool filled=true)
Draw rectangular box over an image.
Definition: GUIhelper.C:417
jevois::GUIhelper
Helper class to assist modules in creating graphical and GUI elements.
Definition: GUIhelper.H:108
jevois::dnn::shapestr
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
jevois::rawimage::writeText
void writeText(RawImage &img, std::string const &txt, int x, int y, unsigned int col, Font font=Font6x10)
Write some text in an image.
Definition: RawImageOps.C:689
Engine.H
jevois::dnn::getLabel
std::string getLabel(std::map< int, std::string > const &labels, int id)
Get a label from an id.
Definition: Utils.C:68
PostProcessNPUhelpers.H
jevois::ObjReco
A trivial struct to store object recognition results.
Definition: ObjReco.H:23
jevois::dnn::PreProcessor
Pre-Processor for neural network pipeline.
Definition: PreProcessor.H:76
jevois::ObjDetect
A trivial struct to store object detection results.
Definition: ObjDetect.H:26
jevois::dnn::PostProcessorDetect::process
void process(std::vector< cv::Mat > const &outs, PreProcessor *preproc) override
Process outputs and draw/send some results.
Definition: PostProcessorDetect.C:74
jevois::join
std::string join(std::vector< std::string > const &strings, std::string const &delimiter)
Concatenate a vector of tokens into a string.
Definition: Utils.C:267
jevois::StdModule::sendSerialObjDetImg2D
void sendSerialObjDetImg2D(unsigned int camw, unsigned int camh, float x, float y, float w, float h, std::vector< ObjReco > const &res)
Send a standardized object detection + recognition message.
Definition: Module.C:577
jevois::dnn::PostProcessorDetect::onParamChange
void onParamChange(postprocessor::classes const &param, std::string const &val) override
LFATAL
#define LFATAL(msg)
Convenience macro for users to print out console or syslog messages, FATAL level.
Definition: Log.H:217
jevois::GUIhelper::drawText
void drawText(float x, float y, char const *txt, ImU32 col=IM_COL32(128, 255, 128, 255))
Draw text over an image.
Definition: GUIhelper.C:479
jevois::absolutePath
std::string absolutePath(std::string const &root, std::string const &path)
Compute an absolute path from two paths.
Definition: Utils.C:347
jevois::to_string
std::string to_string(T const &val)
Convert from type to string.
PreProcessor.H
jevois::rawimage::drawRect
void drawRect(RawImage &img, int x, int y, unsigned int w, unsigned int h, unsigned int thick, unsigned int col)
Draw a rectangle in a YUYV image.
Definition: RawImageOps.C:607
Utils.H
jevois::dnn::PreProcessor::imagesize
const cv::Size & imagesize() const
Access the last processed image size.
Definition: PreProcessor.C:33
jevois::dnn::PreProcessor::b2i
void b2i(float &x, float &y, size_t blobnum=0)
Convert coordinates from blob back to original image.
Definition: PreProcessor.C:44
jevois::dnn::npu::yolo
void yolo(cv::Mat const &out, std::vector< int > &classIds, std::vector< float > &confidences, std::vector< cv::Rect > &boxes, size_t nclass, float const *biases, int const yolonum, float confThreshold, cv::Size const &bsiz, int fudge)
Definition: PostProcessNPUhelpers.C:52
jevois::StdModule
Base class for a module that supports standardized serial messages.
Definition: Module.H:238
jevois::rawimage::Font10x20
@ Font10x20
Definition: RawImageOps.H:159
GUIhelper.H
jevois::dnn::PostProcessorDetect::~PostProcessorDetect
virtual ~PostProcessorDetect()
Destructor.
Definition: PostProcessorDetect.C:31
jevois::dnn::PostProcessorDetect::report
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.
Definition: PostProcessorDetect.C:355