21#include <opencv2/core/core.hpp>
22#include <opencv2/dnn.hpp>
23#include <opencv2/imgproc.hpp>
32 (TinyYOLOv3) (TinyYOLOv2) );
36 "classnames, configname, modelname, etc for the selected network. When "
37 "the selected model is Custom, those other parameters will be set instead "
38 "from the module's params.cfg config file.",
39 Model::Custom, Model_Values, ParamCateg);
43 JEVOIS_SHARE_PATH
"/opencv-dnn/detection/opencv_face_detector.classes", ParamCateg);
47 "Can have extension .prototxt (Caffe), .pbtxt (TensorFlow), or .cfg (Darknet).",
48 JEVOIS_SHARE_PATH
"/opencv-dnn/detection/opencv_face_detector.prototxt", ParamCateg);
52 "Can have extension .caffemodel (Caffe), .pb (TensorFlow), .t7 or .net (Torch), "
53 "or .weights (Darknet).",
54 JEVOIS_SHARE_PATH
"/opencv-dnn/detection/opencv_face_detector.caffemodel", ParamCateg);
58 "to make it match camera frame size. NOTE: for YOLO v3 sizes must be multiples of 32.",
59 cv::Size(160, 120), ParamCateg);
75 2.0F / 255.0F, ParamCateg);
79 cv::Scalar(127.5F, 127.5F, 127.5F), ParamCateg);
87 "next time you load a different model.",
88 Target::OpenCL, Target_Values, ParamCateg);
172 public jevois::Parameter<model, classnames, configname, modelname, netin,
173 thresh, nms, rgb, scale, mean
198 classnames::freeze(
false);
199 configname::freeze(
false);
200 modelname::freeze(
false);
217 classnames::set(
JEVOIS_SHARE_PATH "/opencv-dnn/detection/opencv_face_detector.classes");
218 modelname::set(
JEVOIS_SHARE_PATH "/opencv-dnn/detection/opencv_face_detector.caffemodel");
219 configname::set(
JEVOIS_SHARE_PATH "/opencv-dnn/detection/opencv_face_detector.prototxt");
221 mean::set(cv::Scalar(104.0F, 177.0F, 123.0F));
225 case Model::MobileNetSSDvoc:
227 modelname::set(
JEVOIS_SHARE_PATH "/opencv-dnn/detection/MobileNetSSD_deploy.caffemodel");
228 configname::set(
JEVOIS_SHARE_PATH "/opencv-dnn/detection/MobileNetSSD_deploy.prototxt");
231 case Model::MobileNetSSDcoco:
233 modelname::set(
JEVOIS_SHARE_PATH "/opencv-dnn/detection/ssd_mobilenet_v1_coco_2017_11_17.pb");
234 configname::set(
JEVOIS_SHARE_PATH "/opencv-dnn/detection/ssd_mobilenet_v1_coco_2017_11_17.pbtxt");
238 case Model::MobileNet2SSDcoco:
240 modelname::set(
JEVOIS_SHARE_PATH "/opencv-dnn/detection/ssd_mobilenet_v2_coco_2018_03_29.pb");
241 configname::set(
JEVOIS_SHARE_PATH "/opencv-dnn/detection/ssd_mobilenet_v2_coco_2018_03_29.pbtxt");
244 case Model::TinyYOLOv3:
250 case Model::TinyYOLOv2:
252 modelname::set(
JEVOIS_SHARE_PATH "/darknet/yolo/weights/yolov2-tiny-voc.weights");
254 netin::set(cv::Size(320, 240));
258 if (val != Model::Custom)
260 classnames::freeze(
true);
261 configname::freeze(
true);
262 modelname::freeze(
true);
279 std::ifstream ifs(classnames::get());
280 if (ifs.is_open() ==
false)
LFATAL(
"Class names file " << classnames::get() <<
" not found");
282 while (std::getline(ifs, line))
itsClasses.push_back(line);
285 itsNet = cv::dnn::readNet(modelname::get(), configname::get());
286 itsNet.setPreferableBackend(cv::dnn::DNN_BACKEND_OPENCV);
288 switch(target::get())
290 case Target::CPU:
itsNet.setPreferableTarget(cv::dnn::DNN_TARGET_CPU);
break;
291 case Target::OpenCL:
itsNet.setPreferableTarget(cv::dnn::DNN_TARGET_OPENCL);
break;
292 case Target::OpenCL_FP16:
itsNet.setPreferableTarget(cv::dnn::DNN_TARGET_OPENCL_FP16);
break;
295 itsNet.setPreferableTarget(cv::dnn::DNN_TARGET_CPU);
299 std::vector<cv::String> layersNames =
itsNet.getLayerNames();
310 float const confThreshold = thresh::get() * 0.01F;
311 float const nmsThreshold = nms::get() * 0.01F;
313 std::vector<int> classIds;
314 std::vector<float> confidences;
315 std::vector<cv::Rect> boxes;
316 if (
itsNet.getLayer(0)->outputNameToIndex(
"im_info") != -1)
320 if (outs.size() != 1)
LFATAL(
"Malformed output layers");
321 float* data = (
float*)outs[0].data;
322 for (
size_t i = 0; i < outs[0].total(); i += 7)
324 float confidence = data[i + 2];
325 if (confidence > confThreshold)
327 int left = (int)data[i + 3];
328 int top = (int)data[i + 4];
329 int right = (int)data[i + 5];
330 int bottom = (int)data[i + 6];
331 int width = right - left + 1;
332 int height = bottom - top + 1;
333 classIds.push_back((
int)(data[i + 1]) - 1);
334 boxes.push_back(cv::Rect(left, top, width, height));
335 confidences.push_back(confidence);
343 if (outs.size() != 1)
LFATAL(
"Malformed output layers");
344 float* data = (
float*)outs[0].data;
345 for (
size_t i = 0; i < outs[0].total(); i += 7)
347 float confidence = data[i + 2];
348 if (confidence > confThreshold)
350 int left = (int)(data[i + 3] * frame.cols);
351 int top = (int)(data[i + 4] * frame.rows);
352 int right = (int)(data[i + 5] * frame.cols);
353 int bottom = (int)(data[i + 6] * frame.rows);
354 int width = right - left + 1;
355 int height = bottom - top + 1;
356 classIds.push_back((
int)(data[i + 1]) - 1);
357 boxes.push_back(cv::Rect(left, top, width, height));
358 confidences.push_back(confidence);
364 for (
size_t i = 0; i < outs.size(); ++i)
368 float* data = (
float*)outs[i].data;
369 for (
int j = 0; j < outs[i].rows; ++j, data += outs[i].cols)
371 cv::Mat scores = outs[i].row(j).colRange(5, outs[i].cols);
372 cv::Point classIdPoint;
374 minMaxLoc(scores, 0, &confidence, 0, &classIdPoint);
375 if (confidence > confThreshold)
377 int centerX = (int)(data[0] * frame.cols);
378 int centerY = (int)(data[1] * frame.rows);
379 int width = (int)(data[2] * frame.cols);
380 int height = (int)(data[3] * frame.rows);
381 int left = centerX - width / 2;
382 int top = centerY - height / 2;
384 classIds.push_back(classIdPoint.x);
385 confidences.push_back((
float)confidence);
386 boxes.push_back(cv::Rect(left, top, width, height));
394 std::vector<int> indices;
395 cv::dnn::NMSBoxes(boxes, confidences, confThreshold, nmsThreshold, indices);
398 for (
size_t i = 0; i < indices.size(); ++i)
400 int idx = indices[i];
401 cv::Rect
const & box = boxes[idx];
402 std::vector<jevois::ObjReco> data;
403 float const conf = confidences[idx] * 100.0F;
405 if (classIds[idx] <
int(
itsClasses.size())) name =
itsClasses[classIds[idx]];
else name =
"Oooops";
406 data.push_back({ conf, name });
437 cv::dnn::blobFromImage(cvimg, blob, scale::get(), netin::get(), mean::get(), rgb::get(),
false);
461 inimg.
require(
"input", w,
h, V4L2_PIX_FMT_YUYV);
466 outimg = outframe.get();
480 cv::dnn::blobFromImage(cvimg, blob, scale::get(), netin::get(), mean::get(), rgb::get(),
false);
496 std::vector<double> layersTimes;
497 double freq = cv::getTickFrequency() / 1000;
498 double t =
itsNet.getPerfProfile(layersTimes) / freq;
499 std::string label = model::strget() +
jevois::sformat(
" inference time: %.2f ms", t);
503 std::string
const & fpscpu = timer.
stop();
JEVOIS_REGISTER_MODULE(ArUcoBlob)
#define JEVOIS_SHARE_PATH
Detect and recognize multiple objects in scenes using OpenCV Deep Neural Nets (DNN)
JEVOIS_DECLARE_PARAMETER(mean, cv::Scalar, "Mean BGR value subtracted from input image", cv::Scalar(127.5F, 127.5F, 127.5F), ParamCateg)
Parameter.
JEVOIS_DECLARE_PARAMETER(rgb, bool, "When true, model works with RGB input images instead BGR ones", true, ParamCateg)
Parameter.
JEVOIS_DECLARE_PARAMETER(scale, float, "Value scaling factor applied to input pixels", 2.0F/255.0F, ParamCateg)
Parameter.
void postprocess(cv::Mat const &frame, std::vector< cv::Mat > const &outs, jevois::RawImage *outframe=nullptr)
Post-processing to extract boxes from network outputs.
std::vector< int > itsOutLayers
virtual ~DetectionDNN()
Virtual destructor for safe inheritance.
virtual void process(jevois::InputFrame &&inframe) override
Processing function, no video output.
void onParamChange(model const &, Model const &val) override
Parameter callback: set the selected model.
JEVOIS_DECLARE_PARAMETER(thresh, float, "Detection threshold in percent confidence", 50.0F, jevois::Range< float >(0.0F, 100.0F), ParamCateg)
Parameter.
JEVOIS_DEFINE_ENUM_CLASS(Model,(Custom)(Face)(MobileNetSSDvoc)(MobileNetSSDcoco)(MobileNet2SSDcoco)(TinyYOLOv3)(TinyYOLOv2))
Enum.
DetectionDNN(std::string const &instance)
Constructor.
JEVOIS_DEFINE_ENUM_CLASS(Target,(CPU)(OpenCL)(OpenCL_FP16))
Enum.
JEVOIS_DECLARE_PARAMETER(target, Target, "OpenCV compute target to use. Changes will take effect " "next time you load a different model.", Target::OpenCL, Target_Values, ParamCateg)
Parameter.
JEVOIS_DECLARE_PARAMETER(configname, std::string, "Path to a text file that contains network configuration. " "Can have extension .prototxt (Caffe), .pbtxt (TensorFlow), or .cfg (Darknet).", JEVOIS_SHARE_PATH "/opencv-dnn/detection/opencv_face_detector.prototxt", ParamCateg)
Parameter.
std::vector< cv::String > itsOutNames
JEVOIS_DECLARE_PARAMETER(modelname, std::string, "Path to a binary file of model contains trained weights. " "Can have extension .caffemodel (Caffe), .pb (TensorFlow), .t7 or .net (Torch), " "or .weights (Darknet).", JEVOIS_SHARE_PATH "/opencv-dnn/detection/opencv_face_detector.caffemodel", ParamCateg)
Parameter.
std::vector< std::string > itsClasses
JEVOIS_DECLARE_PARAMETER(nms, float, "Non-maximum suppression intersection-over-union threshold in percent", 45.0F, jevois::Range< float >(0.0F, 100.0F), ParamCateg)
Parameter.
JEVOIS_DECLARE_PARAMETER(classnames, std::string, "Path to a text file with names of classes to label detected objects", JEVOIS_SHARE_PATH "/opencv-dnn/detection/opencv_face_detector.classes", ParamCateg)
Parameter.
JEVOIS_DECLARE_PARAMETER_WITH_CALLBACK(model, Model, "Shortcut parameter to load a model. This sets parameters " "classnames, configname, modelname, etc for the selected network. When " "the selected model is Custom, those other parameters will be set instead " "from the module's params.cfg config file.", Model::Custom, Model_Values, ParamCateg)
Parameter.
void load()
Load and initialize a model.
std::string itsOutLayerType
JEVOIS_DECLARE_PARAMETER(netin, cv::Size, "Width and height (in pixels) of the neural network input layer, or [0 0] " "to make it match camera frame size. NOTE: for YOLO v3 sizes must be multiples of 32.", cv::Size(160, 120), ParamCateg)
Parameter.
virtual void process(jevois::InputFrame &&inframe, jevois::OutputFrame &&outframe) override
Processing function with video output to USB.
void require(char const *info, unsigned int w, unsigned int h, unsigned int f) const
void sendSerialObjDetImg2D(unsigned int camw, unsigned int camh, float x, float y, float w, float h, std::vector< ObjReco > const &res)
StdModule(std::string const &instance)
std::string const & stop(double *seconds)
void paste(RawImage const &src, RawImage &dest, int dx, int dy)
void writeText(RawImage &img, std::string const &txt, int x, int y, unsigned int col, Font font=Font6x10)
void drawFilledRect(RawImage &img, int x, int y, unsigned int w, unsigned int h, unsigned int col)
cv::Mat convertToCvBGR(RawImage const &src)
void drawRect(RawImage &img, int x, int y, unsigned int w, unsigned int h, unsigned int thick, unsigned int col)
std::future< std::invoke_result_t< std::decay_t< Function >, std::decay_t< Args >... > > async(Function &&f, Args &&... args)
std::string sformat(char const *fmt,...) __attribute__((format(__printf__
unsigned short constexpr Black
unsigned short constexpr White
unsigned short constexpr LightGreen