104 #include <yarp/os/Network.h>
105 #include <yarp/os/BufferedPort.h>
106 #include <yarp/os/RFModule.h>
107 #include <yarp/os/PeriodicThread.h>
108 #include <yarp/os/Stamp.h>
110 #include <yarp/sig/Image.h>
111 #include <yarp/sig/Vector.h>
113 #include <yarp/cv/Cv.h>
115 #include <opencv2/core.hpp>
116 #include <opencv2/opencv.hpp>
126 using namespace yarp::os;
127 using namespace yarp::sig;
128 using namespace yarp::cv;
130 class BlobDetectorThread:
public PeriodicThread
135 BufferedPort<ImageOf<PixelMono>> port_i_img;
136 BufferedPort<Image> port_i_propImg;
143 int gaussian_winsize;
159 Vector area, orientation, axe1, axe2;
162 cv::Point tmpCenter[500], pt1, pt2;
167 BlobDetectorThread(ResourceFinder &_rf)
168 :PeriodicThread(0.005),rf(_rf) { }
172 virtual bool threadInit()
174 string name=rf.find(
"name").asString();
176 port_i_img.open(
"/"+name+
"/img:i");
177 port_i_propImg.open(
"/"+name+
"/propImg:i");
178 port_o_img.open(
"/"+name+
"/img:o");
179 port_o_propImg.open(
"/"+name+
"/propImg:o");
181 port_o_blobs.open(
"/"+name+
"/blobs:o");
182 port_o_clean.open(
"/"+name+
"/binary:o");
184 gaussian_winsize=rf.check(
"gaussian_winsize",Value(9)).asInt32();
186 thresh=rf.check(
"thresh",Value(10.0)).asFloat64();
187 erode_itr=rf.check(
"erode_itr",Value(8)).asInt32();
188 dilate_itr=rf.check(
"dilate_itr",Value(3)).asInt32();
189 window_ratio=rf.check(
"window_ratio",Value(0.6)).asFloat64();
191 maxHeight = rf.check(
"maxHeight",Value(150)).asInt32();
192 maxWidth = rf.check(
"maxWidth",Value(150)).asInt32();
194 details=rf.check(
"details",Value(
"off")).asString();
196 offset=rf.check(
"offset",Value(0)).asInt32();
204 orientation.resize(500);
211 virtual void setThreshold(
double newThreshold)
213 lock_guard<mutex> lg(mtx);
214 thresh = newThreshold;
219 if (ImageOf<PixelMono> *img=port_i_img.read(
false))
222 port_i_img.getEnvelope(ts);
224 cv::Mat gray=toCvMat(*img);
225 cv::Mat imgProp=gray.clone();
226 imgProp.setTo(cv::Scalar::all(0));
228 cv::GaussianBlur(gray,gray,cv::Size(gaussian_winsize,gaussian_winsize),0.0);
229 cv::threshold(gray,gray,thresh,255.0,cv::THRESH_BINARY);
230 cv::equalizeHist(gray,gray);
231 cv::erode(gray,gray,cv::Mat(),cv::Point(-1,-1),erode_itr);
232 cv::dilate(gray,gray,cv::Mat(),cv::Point(-1,-1),dilate_itr);
234 lock_guard<mutex> lg(mtx);
238 int w_offset=cvRound(0.5*gray.size().width*(1.0-window_ratio));
239 int h_offset=cvRound(0.5*gray.size().height*(1.0-window_ratio));
241 for (
int row=h_offset; row<gray.size().height-h_offset; row++)
243 for (
int col=w_offset; col<gray.size().width-w_offset; col++)
245 if (gray.at<uchar>(row,col)==255)
248 cv::floodFill(gray,cv::Point(col,row),cv::Scalar(255-(blobs.size()+non_blobs.size()+1)),&comp);
250 if (5<comp.width && comp.width<maxWidth && 5<comp.height && comp.height<maxHeight)
252 Bottle &b=blobs.addList();
253 b.addFloat64(comp.x-offset);
254 b.addFloat64(comp.y-offset);
255 b.addFloat64(comp.x+comp.width+offset);
256 b.addFloat64(comp.y+comp.height+offset);
259 if (orientation.size() > 0 )
261 b.addFloat64(orientation[itr+1]);
262 b.addInt32((
int)axe2[itr+1]);
263 b.addInt32((
int)axe1[itr+1]);
270 Bottle &n=non_blobs.addList();
271 n.addFloat64(comp.x-offset);
272 n.addFloat64(comp.y-offset);
273 n.addFloat64(comp.x+comp.width+offset);
274 n.addFloat64(comp.y+comp.height+offset);
277 if (orientation.size() > 0 )
279 n.addFloat64(orientation[itr+1]);
280 n.addInt32((
int)axe2[itr+1]);
281 n.addInt32((
int)axe1[itr+1]);
294 lock_guard<mutex> lg(contours);
298 port_o_img.setEnvelope(ts);
299 port_o_img.write(*img);
301 port_o_blobs.setEnvelope(ts);
302 port_o_blobs.write(blobs);
303 Image *prop= port_i_propImg.read(
false);
307 port_o_propImg.setEnvelope(ts);
308 port_o_propImg.write(*prop);
311 if (!imgProp.empty())
313 ImageOf<PixelMono> sendImg;
314 sendImg.resize(imgProp.size().width,imgProp.size().height);
315 imgProp.copyTo(toCvMat(sendImg));
316 port_o_clean.setEnvelope(ts);
317 port_o_clean.write(sendImg);
322 virtual void threadRelease()
326 port_o_blobs.close();
327 port_i_propImg.close();
328 port_o_clean.close();
329 port_o_propImg.close();
332 bool execReq(
const Bottle &command, Bottle &reply)
334 if(command.get(0).asVocab32()==Vocab32::encode(
"thresh"))
336 lock_guard<mutex> lg(mtx);
337 thresh = command.get(1).asFloat64();
338 reply.addVocab32(
"ok");
341 if(command.get(0).asVocab32()==Vocab32::encode(
"erode"))
343 lock_guard<mutex> lg(mtx);
344 erode_itr = command.get(1).asInt32();
345 reply.addVocab32(
"ok");
348 if(command.get(0).asVocab32()==Vocab32::encode(
"dilate"))
350 lock_guard<mutex> lg(mtx);
351 dilate_itr = command.get(1).asInt32();
352 reply.addVocab32(
"ok");
359 void cleanBlobs(cv::Mat &image)
361 for (
int i=0; i<blobs.size(); i++)
363 cv::Point cog(-1,-1);
364 if ((i>=0) && (i<blobs.size()))
367 Bottle *item=blobs.get(i).asList();
369 cout <<
"ITEM IS NULL" << cog.x << cog.y <<endl;
371 tl.x=(int)item->get(0).asFloat64() - 2;
372 tl.y=(int)item->get(1).asFloat64() - 2;
373 br.x=(int)item->get(2).asFloat64() + 2;
374 br.y=(int)item->get(3).asFloat64() + 2;
376 image(cv::Rect(tl.x,tl.y,br.x-tl.x,br.y-tl.y)).copyTo(imgProp(cv::Rect(tl.x,tl.y,br.x-tl.x,br.y-tl.y)));
381 void processImg(cv::Mat &image)
383 for (
int i=0; i<blobs.size(); i++)
385 cv::Point cog(-1,-1);
386 if ((i>=0) && (i<blobs.size()))
389 Bottle *item=blobs.get(i).asList();
391 cout <<
"ITEM IS NULL" << cog.x << cog.y <<endl;
393 tl.x=(int)item->get(0).asFloat64() - 10;
394 tl.y=(int)item->get(1).asFloat64() - 10;
395 br.x=(int)item->get(2).asFloat64() + 10;
396 br.y=(int)item->get(3).asFloat64() + 10;
398 cog.x=(tl.x + br.x)>>1;
399 cog.y=(tl.y + br.y)>>1;
401 cv::Mat imageRoi=image(cv::Rect(tl.x,tl.y,br.x-tl.x,br.y-tl.y));
402 getOrientations(imageRoi);
409 void getOrientations(cv::Mat &image)
411 vector<vector<cv::Point>> cont;
412 cv::findContours(image,cont,CV_RETR_LIST,CV_CHAIN_APPROX_NONE);
418 cv::RotatedRect boxtmp = cv::minAreaRect(cv::Mat(c));
419 tmpCenter[numObj].x = cvRound(boxtmp.center.x);
420 tmpCenter[numObj].y = cvRound(boxtmp.center.y);
421 area[numObj] = cv::contourArea(c);
426 Vector index(numObj);
427 for (
int x=1; x<numObj; x++)
429 if (abs( tmpCenter[x].x -tmpCenter[x+1].x ) < 10 )
431 if (abs( tmpCenter[x].y -tmpCenter[x+1].y) < 10)
433 if ( area[x] < area[x+1] )
450 for (
int i= 0; i<inc; i++)
451 if (numBlobs == index[i])
457 cv::RotatedRect box = cv::fitEllipse(cv::Mat(c));
458 cv::Point center = box.center;
459 cv::Size size(cvRound(box.size.width*0.5),cvRound(box.size.height*0.5));
461 if ((box.size.width > 0) && (box.size.width < 300) && (box.size.height > 0) && (box.size.height < 300))
463 axe1[numBlobs] = size.width;
464 axe2[numBlobs] = size.height;
466 vector<float> line(4);
467 cv::fitLine(c,line,CV_DIST_L2,0,0.01,0.01);
468 float t = (box.size.width + box.size.height)/2;
469 pt1.x = cvRound(line[2] - line[0] *t );
470 pt1.y = cvRound(line[3] - line[1] *t );
471 pt2.x = cvRound(line[2] + line[0] *t );
472 pt2.y = cvRound(line[3] + line[1] *t );
473 cv::line(image,pt1,pt2,cv::Scalar(255,255,255),2,cv::LINE_AA);
475 thetatmp = (180.0/M_PI) * atan2( (
double)(pt2.y - pt1.y) , (
double)(pt2.x - pt1.x) );
477 thetatmp=(thetatmp<0.0?-thetatmp:180.0-thetatmp);
478 orientation[numBlobs]=thetatmp;
485 class BlobDetectorModule:
public RFModule
488 BlobDetectorThread *bdThr;
492 BlobDetectorModule() { }
494 virtual bool configure(ResourceFinder &rf)
496 bdThr=
new BlobDetectorThread(rf);
504 string name=rf.find(
"name").asString();
505 rpcPort.open(
"/"+name+
"/rpc");
511 virtual bool interruptModule()
528 virtual double getPeriod()
533 virtual bool updateModule()
538 virtual bool respond(
const Bottle &command, Bottle &reply)
541 if (command.get(0).asString()==
"details")
543 if (command.get(1).asString()==
"on")
545 bdThr->details =
"on";
546 reply.addString(
"setting details to ON");
550 bdThr->details =
"off";
551 reply.addString(
"setting details to OFF");
555 }
else if (command.get(0).asString()==
"thresh")
557 double newThresh = command.get(1).asFloat64();
558 if (newThresh<0 || newThresh>255.0)
560 reply.addString(
"Invalid threshold (expecting a value between 0 and 255)");
563 reply.addString(
"Setting threshold");
564 bdThr->setThreshold(newThresh);
567 if(bdThr->execReq(command,reply))
570 return RFModule::respond(command,reply);
575 int main(
int argc,
char *argv[])
579 if (!yarp.checkNetwork())
583 rf.setDefaultContext(
"blobExtractor");
584 rf.setDefaultConfigFile(
"config.ini");
585 rf.setDefault(
"name",
"blobExtractor");
586 rf.configure(argc,argv);
588 BlobDetectorModule mod;
590 return mod.runModule(rf);