segmentation
All Data Structures Namespaces Files Functions Variables Modules Pages
dispBlobber.cpp
1 /*
2  * Copyright (C) 2015 iCub Facility - Istituto Italiano di Tecnologia
3  * Authors: Tanis Mar, Giulia Pasquale
4  * email: tanis.mar@iit.it, giulia.pasquale@iit.it
5  * Permission is granted to copy, distribute, and/or modify this program
6  * under the terms of the GNU General Public License, version 2 or any
7  * later version published by the Free Software Foundation.
8  *
9  * A copy of the license can be found at
10  * http://www.robotcub.org/icub/license/gpl.txt
11  *
12  * This program is distributed in the hope that it will be useful, but
13  * WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
15  * Public License for more details
16  */
17 
18 #include <cstdio>
19 #include <opencv2/imgproc/types_c.h>
20 #include "dispBlobber.hpp"
21 
22 using namespace std;
23 
24 dispBlobber::dispBlobber(int imH, int imW, int _bufferSize,
25  int _margin,
26  int _backgroundThresh,
27  int _minBlobSize, int _maxBlobSize, int _gaussSize,
28  int _imageThreshRatioLow, int _imageThreshRatioHigh)
29 {
30 
31  aux.create(imH, imW, CV_8U);
32  fillMask.create(imH + 2, imW + 2, CV_8U);
33 
34  margin = _margin;
35 
36  backgroundThresh = _backgroundThresh; // threshold of intensity on the image under which info is ignored
37 
38  minBlobSize = _minBlobSize;
39  maxBlobSize = _maxBlobSize;
40 
41  gaussSize = _gaussSize;
42 
43  imageThreshRatioLow = _imageThreshRatioLow;
44  imageThreshRatioHigh = _imageThreshRatioHigh;
45 
46  blue = cv::Scalar(255,0,0);
47  green = cv::Scalar(0,255,0);
48  red = cv::Scalar(0,0,255);
49  white = cv::Scalar(255,255,255);
50 
51  bufferSize = _bufferSize;
52 
53 }
54 
55 bool dispBlobber::setThresh(int low)
56 {
57  if ( low<0 || low>255 ) {
58  fprintf(stdout,"Please select valid luminance values (0-255). \n");
59  return false;
60  }
61 
62  fprintf(stdout,"New Threshold is : %i\n", low);
63  backgroundThresh = low;
64  return true;
65 }
66 
67 bool dispBlobber::setMargin(int mrg)
68 {
69  fprintf(stdout,"New margin : %d\n", mrg);
70  margin = mrg;
71  return true;
72 }
73 
74 double dispBlobber::extractBlob(std::vector<cv::Mat> &images, std::vector<int> &roi, std::vector<int> &centroid, cv::Mat &blob)
75 {
76 
77  cv::Mat image = images[0].clone();
78 
79  cv::cvtColor(image, image, CV_BGR2GRAY);
80 
81 
82  /* Filter */
83 
84  double sigmaX1 = 1.5;
85  double sigmaY1 = 1.5;
86  cv::GaussianBlur(image, image, cv::Size(gaussSize,gaussSize), sigmaX1, sigmaY1);
87 
88  cv::threshold(image, image, backgroundThresh, -1, CV_THRESH_TOZERO);
89 
90  int dilate_niter = 4;
91  int erode_niter = 2;
92  double sigmaX2 = 2;
93  double sigmaY2 = 2;
94 
95  cv::dilate(image, image, cv::Mat(), cv::Point(-1,-1), dilate_niter, cv::BORDER_CONSTANT, cv::morphologyDefaultBorderValue());
96 
97  cv::GaussianBlur(image, image, cv::Size(gaussSize,gaussSize), sigmaX2, sigmaY2, cv::BORDER_DEFAULT);
98 
99  cv::erode(image, image, cv::Mat(), cv::Point(-1,-1), erode_niter, cv::BORDER_CONSTANT, cv::morphologyDefaultBorderValue());
100 
101 
102  /* Find closest valid blob */
103 
104  double minVal, maxVal;
105  cv::Point minLoc, maxLoc;
106 
107  int fillFlags = 8 | ( 255 << 8 ) | cv::FLOODFILL_FIXED_RANGE; // flags for floodFill
108 
109  aux = image.clone();
110 
111  int fillSize = 0;
112  while (fillSize < minBlobSize){
113 
114  cv::minMaxLoc( aux, &minVal, &maxVal, &minLoc, &maxLoc );
115 
116  // if its too small, paint it black and search again
117  fillSize = floodFill(aux, maxLoc, 0, 0, cv::Scalar(maxVal/imageThreshRatioLow), cv::Scalar(maxVal/imageThreshRatioHigh), fillFlags);
118 
119  }
120  // paint closest valid blob white
121  fillMask.setTo(0);
122  cv::floodFill(image, fillMask, maxLoc, 255, 0, cv::Scalar(maxVal/imageThreshRatioLow), cv::Scalar(maxVal/imageThreshRatioHigh), cv::FLOODFILL_MASK_ONLY + fillFlags);
123 
124  /* Find contours */
125 
126  std::vector<std::vector<cv::Point > > contours;
127  std::vector<cv::Vec4i> hierarchy;
128 
129  // use aux because findContours modify the input image
130  aux = fillMask(cv::Range(1,image.rows+1), cv::Range(1,image.cols+1)).clone();
131  cv::findContours( aux, contours, hierarchy, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE, cv::Point(0, 0) );
132 
133 
134  /* If any blob is found check again that only the biggest valid blob is selected */
135 
136  int blobI = -1;
137  double blobSizeOld = -1, blobSize = -1;
138  for( size_t c = 0; c < contours.size(); c++ ){
139 
140  // find the area of contour
141  blobSize = cv::contourArea(contours[c]);
142 
143  // select only the biggest valid blob
144  if( blobSize > minBlobSize && blobSize > blobSizeOld && blobSize < maxBlobSize)
145  {
146  blobI = c;
147  blobSizeOld = blobSize;
148  }
149  }
150 
151  /* If any blob is found (after the double-check) */
152 
153  if (blobI>=0)
154  {
155 
156  /* Get the ROI */
157 
158  cv::Rect blobBox = cv::boundingRect(contours[blobI]);
159 
160  cv::Rect imBox(cv::Point(std::max(blobBox.tl().x-margin,0),std::max(blobBox.tl().y-margin,0)),cv::Point(std::min(blobBox.br().x+margin,image.cols-1),std::min(blobBox.br().y+margin,image.rows-1)));
161 
162 
163  /* Get the current centroid */
164 
165  cv::Moments mu = moments( contours[blobI], false );
166 
167  //cv::Point center2Dcoords = cv::Point2f( mu.m10/mu.m00 , mu.m01/mu.m00 );
168  cv::Point2f center2Dcoords = cv::Point2f( mu.m10/mu.m00 , mu.m01/mu.m00 );
169 
170  /* Update the buffer */
171 
172  center2DcoordsBuffer.push_back(center2Dcoords);
173 
174  if ((int)center2DcoordsBuffer.size()>bufferSize) {
175  assert(!center2DcoordsBuffer.empty());
176  center2DcoordsBuffer.erase(center2DcoordsBuffer.begin());
177  }
178 
179  /* Update the mean */
180 
181  cv::Point2f zero(0.0f, 0.0f);
182  cv::Point2f sum = std::accumulate(center2DcoordsBuffer.begin(), center2DcoordsBuffer.end(), zero);
183  mean_centroid.x = (int)round(sum.x / center2DcoordsBuffer.size());
184  mean_centroid.y = (int)round(sum.y / center2DcoordsBuffer.size());
185 
186  /* Return results */
187 
188  roi.push_back(imBox.tl().x);
189  roi.push_back(imBox.tl().y);
190 
191  roi.push_back(imBox.br().x);
192  roi.push_back(imBox.br().y);
193 
194  //centroid.push_back(center2Dcoords.x);
195  //centroid.push_back(center2Dcoords.y);
196  centroid.push_back(mean_centroid.x);
197  centroid.push_back(mean_centroid.y);
198 
199  fillMask(cv::Range(1,image.rows+1), cv::Range(1,image.cols+1)).copyTo(blob);
200 
201  }
202  else
203  {
204 
205  blob = cv::Mat::zeros(image.rows, image.cols, CV_8U);
206 
207  blobSize = -1;
208 
209  centroid.push_back(mean_centroid.x);
210  centroid.push_back(mean_centroid.y);
211  }
212 
213  return blobSize;
214 }
215