JeVoisBase  1.21
JeVois Smart Embedded Machine Vision Toolkit Base Modules
Share this page:
Loading...
Searching...
No Matches
SaliencySURF.C
Go to the documentation of this file.
1// ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
2//
3// JeVois Smart Embedded Machine Vision Toolkit - Copyright (C) 2016 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
18
19#include <jevois/Core/Module.H>
20#include <jevois/Debug/Log.H>
21#include <jevois/Util/Utils.H>
23#include <jevois/Debug/Timer.H>
25
26#include <linux/videodev2.h>
29#include <opencv2/opencv.hpp>
30
31// icon by Freepik in people at flaticon
32
33// Module parameters:
34#define PATHPREFIX "/jevois/data/saliencysurf/"
35static jevois::ParameterCategory const ParamCateg("Salient Regions Options");
36
37//! Parameter \relates SaliencySURF
38JEVOIS_DECLARE_PARAMETER(inhsigma, float, "Sigma (pixels) used for inhibition of return", 32.0F, ParamCateg);
39
40//! Parameter \relates SaliencySURF
41JEVOIS_DECLARE_PARAMETER(regions, size_t, "Number of salient regions", 2, ParamCateg);
42
43//! Parameter \relates SaliencySURF
44JEVOIS_DECLARE_PARAMETER(rsiz, size_t, "Width and height (pixels) of salient regions", 64, ParamCateg);
45
46//! Parameter \relates SaliencySURF
47JEVOIS_DECLARE_PARAMETER(save, bool, "Save regions when true, useful to create a training set. They will be saved to "
48 PATHPREFIX, false, ParamCateg);
49
50//! Simple salient region detection and identification using keypoint matching
51/*! This module finds objects by matching keypoint descriptors between a current set of salient regions and a set of
52 training images.
53
54 Here we use SURF keypoints and descriptors as provided by OpenCV. The algorithm is quite slow and consists of 3
55 phases:
56 - detect keypoint locations,
57 - compute keypoint descriptors,
58 - and match descriptors from current image to training image descriptors.
59
60 Here, we alternate between computing keypoints and descriptors on one frame (or more, depending on how slow that
61 gets), and doing the matching on the next frame. This module also provides an example of letting some computation
62 happen even after we exit the \c process() function. Here, we keep detecting keypoints and computing descriptors
63 even outside \c process().
64
65 Also see the \jvmod{ObjectDetect} module for a related algorithm (without attention).
66
67 Training
68 --------
69
70 Simply add images of the objects you want to detect into <b>JEVOIS:/modules/JeVois/SaliencySURF/images/</b> on your
71 JeVois microSD card.
72
73 Those will be processed when the module starts.
74
75 The names of recognized objects returned by this module are simply the file names of the pictures you have added in
76 that directory. No additional training procedure is needed.
77
78 Beware that the more images you add, the slower the algorithm will run, and the higher your chances of confusions
79 among several of your objects.
80
81 This module provides parameters that allow you to determine how strict a match should be. With stricter matching,
82 you may sometimes miss an object (i.e., it was there, but was not detected by the algorithm). With looser matching,
83 you may get more false alarms (i.e., there was something else in the camera's view, but it was matched as one of
84 your objects). If you are experiencing difficulties getting any matches, try to loosen the settings, for example:
85
86 \verbatim
87 setpar goodpts 5 ... 100
88 setpar distthresh 0.5
89 \endverbatim
90
91 @author Laurent Itti
92
93 @displayname Saliency SURF
94 @videomapping YUYV 320 288 30.0 YUYV 320 240 30.0 JeVois SaliencySURF
95 @email itti\@usc.edu
96 @address University of Southern California, HNB-07A, 3641 Watt Way, Los Angeles, CA 90089-2520, USA
97 @copyright Copyright (C) 2016 by Laurent Itti, iLab and the University of Southern California
98 @mainurl http://jevois.org
99 @supporturl http://jevois.org/doc
100 @otherurl http://iLab.usc.edu
101 @license GPL v3
102 @distribution Unrestricted
103 @restrictions None
104 \ingroup modules */
105class SaliencySURF : public jevois::Module, public jevois::Parameter<inhsigma, regions, rsiz, save>
106{
107 public:
108 // ####################################################################################################
109 //! Constructor
110 // ####################################################################################################
111 SaliencySURF(std::string const & instance) : jevois::Module(instance), itsBuf(1000)
112 {
113 itsSaliency = addSubComponent<Saliency>("saliency");
114 itsMatcher = addSubComponent<ObjectMatcher>("surf");
115 }
116
117 // ####################################################################################################
118 //! Virtual destructor for safe inheritance
119 // ####################################################################################################
121 { }
122
123 // ####################################################################################################
124 //! Get started
125 // ####################################################################################################
126 void postInit() override
127 {
128 // Get our run() thread going, it is in charge of compresing and saving frames:
129 itsRunFut = jevois::async(std::bind(&SaliencySURF::run, this));
130
131 LINFO("Using " << itsMatcher->numtrain() << " Training Images.");
132 }
133
134 // ####################################################################################################
135 //! Get stopped
136 // ####################################################################################################
137 void postUninit() override
138 {
139 // Push an empty frame into our buffer to signal the end of video to our thread:
140 itsBuf.push(cv::Mat());
141
142 // Wait for the thread to complete:
143 LINFO("Waiting for writer thread to complete, " << itsBuf.filled_size() << " frames to go...");
144 try { itsRunFut.get(); } catch (...) { jevois::warnAndIgnoreException(); }
145 LINFO("Writer thread completed. Syncing disk...");
146 if (std::system("/bin/sync")) LERROR("Error syncing disk -- IGNORED");
147 }
148
149 // ####################################################################################################
150 //! Processing function
151 // ####################################################################################################
152 virtual void process(jevois::InputFrame && inframe, jevois::OutputFrame && outframe) override
153 {
154 static jevois::Timer timer("processing", 30, LOG_DEBUG);
155
156 // Wait for next available camera image. Any resolution ok, but require YUYV since we assume it for drawings:
157 jevois::RawImage inimg = inframe.get(); unsigned int const w = inimg.width, h = inimg.height;
158 inimg.require("input", w, h, V4L2_PIX_FMT_YUYV);
159
160 timer.start();
161
162 // While we process it, start a thread to wait for output frame and paste the input image into it:
163 jevois::RawImage outimg; // main thread should not use outimg until paste thread is complete
164 cv::Mat grayimg;
165
166 auto paste_fut = jevois::async([&]() {
167 // Convert input image to greyscale:
168 grayimg = jevois::rawimage::convertToCvGray(inimg);
169
170 // Wait output frame and paste input into it:
171 outimg = outframe.get();
172 outimg.require("output", w, h + 4*12, inimg.fmt);
173 jevois::rawimage::paste(inimg, outimg, 0, 0);
174 jevois::rawimage::writeText(outimg, "JeVois Saliency + SURF Demo", 3, 3, jevois::yuyv::White);
175 jevois::rawimage::drawFilledRect(outimg, 0, h, w, outimg.height-h, 0x8000);
176 });
177
178 // Compute the saliency map, no gist:
179 itsSaliency->process(inimg, false);
180
181 // Get some info from the saliency computation:
182 int const smlev = itsSaliency->smscale::get();
183 int const smfac = (1 << smlev);
184 int const rwh = rsiz::get();
185
186 // We need the grayscale and output images to proceed:
187 paste_fut.get();
188
189 // Process each region:
190 int k = 0;
191 for (size_t i = 0; i < regions::get(); ++i)
192 {
193 // Find most salient point:
194 int mx, my; intg32 msal; itsSaliency->getSaliencyMax(mx, my, msal);
195
196 // Compute attended ROI (note: coords must be even to avoid flipping U/V when we later paste):
197 unsigned int const dmx = (mx << smlev) + (smfac >> 2);
198 unsigned int const dmy = (my << smlev) + (smfac >> 2);
199 int rx = (std::min(int(w) - rwh/2, std::max(rwh/2, int(dmx + 1 + (smfac >> 2))))) & (~1);
200 int ry = (std::min(int(h) - rwh/2, std::max(rwh/2, int(dmy + 1 + (smfac >> 2))))) & (~1);
201 unsigned short col = jevois::yuyv::White;
202
203 // Grab the ROI:
204 cv::Mat roi = grayimg(cv::Rect(rx - rwh/2, ry - rwh/2, rwh, rwh));
205
206 // Save it if desired:
207 if (save::get()) itsBuf.push(roi);
208
209 // Process it through our matcher:
210 size_t trainidx;
211 double dist = itsMatcher->process(roi, trainidx);
212 if (dist < 100.0)
213 {
214 jevois::rawimage::writeText(outimg, std::string("Detected: ") + itsMatcher->traindata(trainidx).name +
215 " avg distance " + std::to_string(dist), 3, h + k*12 + 2, jevois::yuyv::White);
217 ++k;
218 }
219
220 // Draw the ROI:
221 jevois::rawimage::drawRect(outimg, rx - rwh/2, ry - rwh/2, rwh, rwh, 1, col);
222
223 // Inhibit this salient location so we move to the next one:
224 itsSaliency->inhibitionOfReturn(mx, my, inhsigma::get() / smfac);
225 }
226
227 // Show processing fps:
228 std::string const & fpscpu = timer.stop();
229 jevois::rawimage::writeText(outimg, fpscpu, 3, h - 13, jevois::yuyv::White);
230
231 // Send the output image with our processing results to the host over USB:
232 outframe.send();
233 }
234
235 private:
236 // ####################################################################################################
237 void run() // Runs in a thread to save regions as images, for training
238 {
239 size_t frame = 0; char tmp[2048];
240
241 // Create directory just in case it does not exist:
242 std::string const cmd = "/bin/mkdir -p " PATHPREFIX;
243 if (std::system(cmd.c_str())) LERROR("Error running [" << cmd << "] -- IGNORED");
244
245 while (true)
246 {
247 // Get next frame from the buffer:
248 cv::Mat im = itsBuf.pop();
249
250 // An empty image will be pushed when we are ready to unload the module:
251 if (im.empty()) break;
252
253 // Write the frame:
254 std::snprintf(tmp, 2047, "%s/frame%06zu.png", PATHPREFIX, frame);
255 cv::imwrite(tmp, im);
256
257 // Report what is going on once in a while:
258 if ((++frame % 100) == 0) LINFO("Saved " << frame << " salient regions.");
259 }
260 }
261
262 std::shared_ptr<ObjectMatcher> itsMatcher;
263 std::shared_ptr<Saliency> itsSaliency;
264 std::future<void> itsRunFut;
266};
267
268// Allow the module to be loaded as a shared object (.so) file:
JEVOIS_REGISTER_MODULE(ArUcoBlob)
int h
#define PATHPREFIX
Simple salient region detection and identification using keypoint matching.
JEVOIS_DECLARE_PARAMETER(regions, size_t, "Number of salient regions", 2, ParamCateg)
Parameter.
virtual void process(jevois::InputFrame &&inframe, jevois::OutputFrame &&outframe) override
Processing function.
void postInit() override
Get started.
JEVOIS_DECLARE_PARAMETER(save, bool, "Save regions when true, useful to create a training set. They will be saved to " PATHPREFIX, false, ParamCateg)
Parameter.
virtual ~SaliencySURF()
Virtual destructor for safe inheritance.
void postUninit() override
Get stopped.
JEVOIS_DECLARE_PARAMETER(rsiz, size_t, "Width and height (pixels) of salient regions", 64, ParamCateg)
Parameter.
SaliencySURF(std::string const &instance)
Constructor.
JEVOIS_DECLARE_PARAMETER(inhsigma, float, "Sigma (pixels) used for inhibition of return", 32.0F, ParamCateg)
Parameter.
size_t filled_size() const
void push(T const &val)
friend friend class Module
unsigned int fmt
unsigned int width
unsigned int height
void require(char const *info, unsigned int w, unsigned int h, unsigned int f) const
std::string const & stop(double *seconds)
ENV_INTG32_TYPE intg32
32-bit signed integer
Definition env_types.h:52
std::string warnAndIgnoreException(std::string const &prefix="")
#define LERROR(msg)
#define LINFO(msg)
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)
cv::Mat convertToCvGray(RawImage const &src)
void drawFilledRect(RawImage &img, int x, int y, unsigned int w, unsigned int h, unsigned int col)
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)
unsigned short constexpr White
unsigned short constexpr LightGreen