Basic Image AlgorithmS Library  2.8.0
 All Classes Namespaces Functions Variables Typedefs Enumerations Enumerator Friends Groups Pages
BlobDetectorBFS.cpp
1 #include "BlobDetectorBFS.hh"
2 #include <Base/ImageUtils/ImageDraw.hh>
3 #include <Base/Image/ImageConvert.hh>
4 #include <Base/Image/ImageIO.hh>
5 using namespace BIAS;
6 using namespace std;
7 
8 /*
9  @brief standard constructor
10 */
11 template <class StorageType>
14  UL_[0] = UL_[1] = LR_[0] = LR_[1] = 0;
15  threshold_ = 128;
16  gaussSigma_=0.0;
17  minWeight_=0;
18  maxWeight_=UINT_MAX;
19  eraseBelowMinWeight_=false;
20 }
21 
22 /*
23  @brief standard Destructor
24 */
25 template <class StorageType>
28 }
29 
30 
31 /*
32  @brief Detect and return blobs in an image
33  @param[in] image the image to detect blobs in
34  @param[out] corners vector where detected blobs are stored in
35  @return 0 on success, -1 on failure
36 */
37 template <class StorageType>
40  std::vector<BIAS::BIASBlob> &blobs){
41 
42  if(image.GetChannelCount()!=1){
43  BIASERR("Blob detection only for 1 channel images, please convert!");
44  return -1;
45  }
46 
47  corners_.clear();
48  unsigned int w = image.GetWidth();
49  unsigned int h = image.GetHeight();
50 
51  //check roi
52  if(UL_[0] == 0 && UL_[1] == 0 && LR_[0] == 0 && LR_[1] == 0){
53  UL_[0] = UL_[1] = 0;
54  LR_[0] = w;
55  LR_[1] = h;
56  }
57  queue.reserve(w*h);
58  cluster.reserve(w*h);
59  markerBackup_.Init(w,h,1);
60  marker_.Init(w,h,1);
61  markerBackup_.FillImageWithConstValue((unsigned char)0);
62  unsigned char **idaMark = markerBackup_.GetImageDataArray();
63  for (unsigned int y=UL_[1];y<LR_[1];y++) {
64  idaMark[y][UL_[0] ]=255;
65  idaMark[y][LR_[0]-1]=255;
66  }
67  for (unsigned int x=UL_[0];x<LR_[0];x++) {
68  idaMark[UL_[1] ][x]=255;
69  idaMark[LR_[1]-1][x]=255;
70  }
71 
72 
73  marker_.CopyIn_NoInit(markerBackup_.GetImageData());
74 
75  StorageType **idaIn = image.GetImageDataArray();
76 
77  if (gaussSigma_!= 0.0) {
78  smoothFilter_.SetSigma(gaussSigma_);
79  smoothFilter_.Filter(image, imageSmooth_);
80  idaIn = ( StorageType**)imageSmooth_.GetImageDataArray();
81  }
82  //BIAS::ImageIO::Save("marker",marker_);
83  BIAS::BIASBlob cornersCurrent;
84  unsigned int maxcornerweight = 0;
85  cornersCurrent.weight = 0;
86  maxCorners_ = cornersCurrent;
87  int ret = -1;
88 
89  StorageType **idaOrig;
90  if (eraseBelowMinWeight_) {
91  if (gaussSigma_ != 0.0) {
92  image.CopyIn_NoInit(imageSmooth_.GetImageData());
93  }
94  idaOrig = image.GetImageDataArray();
95  } else {
96  idaOrig = NULL;
97  }
98  for (unsigned int y=UL_[1];y<LR_[1];y++) {
99  for (unsigned int x=UL_[0];x<LR_[0];x++) {
100 
101  StorageType val = idaIn[y][x];
102  unsigned char mar = idaMark[y][x];
103  if (val > threshold_) {
104  if (mar == 0) {
105  if ((ClusterDescent_(x,y,w,h, idaIn, idaMark, cornersCurrent, idaOrig) == 0) &&
106  (cornersCurrent.weight > minWeight_) && (cornersCurrent.weight < maxWeight_)) {
107 
108  corners_.push_back(cornersCurrent);
109  if (cornersCurrent.weight > maxcornerweight) {
110  maxCorners_ = cornersCurrent;
111  maxcornerweight = cornersCurrent.weight;
112  ret = 0;
113  }
114  }
115  }
116  } else {
117  if (idaOrig != NULL) {
118  idaOrig[y][x] = 0;
119  }
120  }
121  }
122  }
123  blobs.clear();
124  for(unsigned k=0;k<corners_.size();k++){
125  blobs.push_back(corners_[k]);
126  }
127 
128  return ret;
129 }
130 
131 
132 /*
133  @brief protected blobDetection function
134  @param[in] x start x pixel coordinate
135  @param[in] y start y pixel coordinate
136  @param[in] w width of search space
137  @param[in] h height of search space
138  @param[in] idaIn input image imageDataArray
139  @param[out] idaMark mark image imageDataArray (handled pixels are marked here)
140  @param[out] corners detected corners
141  @return int 0 on success -1 on failure
142 */
143 template <class StorageType>
145 ClusterDescent_(const unsigned int x, const unsigned int y,
146  const unsigned int w, const unsigned int h,
147  StorageType **idaIn, unsigned char **idaMark,
148  BIAS::BIASBlob &corners, StorageType **idaOrig) {
149  BIAS::Vector2<unsigned int> start(x,y);
150  queue.clear();
151  cluster.clear();
152 
153  queue.push_back(start);
154  idaMark[y][x] = 255;
155  while (!queue.empty()) {
156 
157  Vector2<unsigned int> cur = queue.back();
158  if (cluster.size() > w*h)
159  break;
160  queue.pop_back();
161  cluster.push_back(cur);
162 
163  n[0] = n[2] = n[4] = cur[0]-1;
164  n[6] = n[8] = cur[0];
165  n[10] = n[12] = n[14] = cur[0]+1;
166  n[1] = n[7] = n[11] = cur[1]-1;
167  n[3] = n[13] = cur[1];
168  n[5] = n[9] = n[15] = cur[1]+1;
169 
170  unsigned int x,y;
171  for (unsigned int i=0;i<16;i+=2) {
172  x=n[i];y=n[i+1];
173  if (idaMark[ y ][ x ] == 0) {
174  idaMark[ y ][ x ] = 255;
175  if (idaIn[ y ][ x ] > threshold_) {
176  start[0]=x; start[1]=y;
177  cluster.push_back(start);
178  queue.push_back(start);
179  }
180  }
181  }
182 
183 /*
184  // can go x+1
185  if (cur[0]<LR_[0]-1) {
186  if (idaMark[cur[1]][cur[0]+1] == 0) {
187  idaMark[cur[1]][cur[0]+1] = 255;
188  if (idaIn[cur[1]][cur[0]+1] > threshold_) {
189  start[0]=cur[0]+1; start[1]=cur[1];
190  cluster.push_back(start);
191  queue.push_back(start);
192  }
193  }
194  // can go x+1 and y-1
195  if (cur[1]>UL_[1]) {
196  if (idaMark[cur[1]-1][cur[0]+1] == 0) {
197  idaMark[cur[1]-1][cur[0]+1] = 255;
198  if (idaIn[cur[1]-1][cur[0]+1] > threshold_) {
199  start[0]=cur[0]+1; start[1]=cur[1]-1;
200  cluster.push_back(start);
201  queue.push_back(start);
202  }
203  }
204  }
205  // can go x+1 and y+1
206  if (cur[1]<LR_[1]-1) {
207  if (idaMark[cur[1]+1][cur[0]+1] == 0) {
208  idaMark[cur[1]+1][cur[0]+1] = 255;
209  if (idaIn[cur[1]+1][cur[0]+1] > threshold_) {
210  start[0]=cur[0]+1; start[1]=cur[1]+1;
211  cluster.push_back(start);
212  queue.push_back(start);
213  }
214  }
215  }
216  }//end if (cur[0]<LR_[0]-1)
217 
218  // can go x-1
219  if (cur[0]>UL_[0]) {
220  if (idaMark[cur[1]][cur[0]-1] == 0) {
221  idaMark[cur[1]][cur[0]-1] = 255;
222  if (idaIn[cur[1]][cur[0]-1] > threshold_) {
223  start[0]=cur[0]-1; start[1]=cur[1];
224  cluster.push_back(start);
225  queue.push_back(start);
226  }
227  }
228  // can go x-1 and y+1
229  if (cur[1]<LR_[1]-1) {
230  if (idaMark[cur[1]+1][cur[0]-1] == 0) {
231  idaMark[cur[1]+1][cur[0]-1] = 255;
232  if (idaIn[cur[1]+1][cur[0]-1] > threshold_) {
233  start[0]=cur[0]-1; start[1]=cur[1]+1;
234  cluster.push_back(start);
235  queue.push_back(start);
236  }
237  }
238  }
239  // can go x-1 and y-1
240  if (cur[1]>UL_[1]) {
241  if (idaMark[cur[1]-1][cur[0]-1] == 0) {
242  idaMark[cur[1]-1][cur[0]-1] = 255;
243  if (idaIn[cur[1]-1][cur[0]-1] > threshold_) {
244  start[0]=cur[0]-1;start[1]=cur[1]-1;
245  cluster.push_back(start);
246  queue.push_back(start);
247  }
248  }
249  }
250  }
251  // can go y+1
252  if (cur[1]<LR_[1]-1) {
253  if (idaMark[cur[1]+1][cur[0]] == 0) {
254  idaMark[cur[1]+1][cur[0]] = 255;
255  if (idaIn[cur[1]+1][cur[0]] > threshold_) {
256  start[0]=cur[0];start[1]=cur[1]+1;
257  cluster.push_back(start);
258  queue.push_back(start);
259  }
260  }
261  }
262  // can go y-1
263  if (cur[1]>UL_[1]) {
264  if (idaMark[cur[1]-1][cur[0]] == 0) {
265  idaMark[cur[1]-1][cur[0]] = 255;
266  if (idaIn[cur[1]-1][cur[0]] > threshold_) {
267  start[0]=cur[0]; start[1]=cur[1]-1;
268  cluster.push_back(start);
269  queue.push_back(start);
270  }
271  }
272  }
273 */
274  }
275 
276  //detect center of mass
277  corners.weight = cluster.size();
278  if (corners.weight > minWeight_ && corners.weight < maxWeight_) {
279  corners.UL[0] = cluster[0][0];
280  corners.UL[1] = cluster[0][1];
281  corners.LR[0] = cluster[0][0];
282  corners.LR[1] = cluster[0][1];
283  corners.centerofmass[0] = cluster[0][0];
284  corners.centerofmass[1] = cluster[0][1];
285  for (unsigned int i=1;i<corners.weight;i++) {
286  BIAS::Vector2<unsigned int> cur( cluster[i]);
287  corners.centerofmass[0] += cur[0];
288  corners.centerofmass[1] += cur[1];
289  if (cur[0] > corners.LR[0])
290  corners.LR[0] = cur[0];
291  if (cur[0] < corners.UL[0])
292  corners.UL[0] = cur[0];
293  if (cur[1] > corners.LR[1])
294  corners.LR[1] = cur[1];
295  if (cur[1] < corners.UL[1])
296  corners.UL[1] = cur[1];
297  }
298  corners.centerofmass[0] /= corners.weight;
299  corners.centerofmass[1] /= corners.weight;
300  return 0;
301  } else {
302  // if "erase below min size" this will clean up the small cluster
303  if (idaOrig != NULL) {
304  for (unsigned int i=0;i<cluster.size();i++) {
305  idaOrig[ cluster[i][1] ][ cluster[i][0] ] = 0;
306  }
307  }
308  return -1;
309  }
310  return 0;
311 }
312 
313 
314 /*
315  @brief draws the detected blobs in image
316  @param[in] image the blobs are drawn in here
317  @return 0 on sucess, -1 on failure
318 */
319 template <class StorageType>
322  StorageType green[3] = { 0,255,0 };
323  StorageType red[3] = { 255,0,0 };
324  //StorageType yellow[3] = { 255,255,0 };
325  BIAS::BIASBlob c;
326 
327  if(image.GetChannelCount() != 3){
328  BIAS::Image<StorageType> tmpImage(image);
329  BIAS::ImageConvert::ToRGB(tmpImage,image);
330  }
331 
332  for (unsigned int i=0; i<corners_.size();i++) {
333  c = corners_[i];
334  ImageDraw<StorageType>::RectangleCorners(image, (unsigned)c.UL[0],
335  (unsigned)c.UL[1], (unsigned)c.LR[0],
336  (unsigned)c.LR[1], green);
338  (unsigned)c.centerofmass[1], 5, red);
339  }
340  if (GetLargestBoundingBox(c) == 0) {
342  (unsigned)c.UL[0], (unsigned)c.UL[1],
343  (unsigned)c.LR[0], (unsigned)c.LR[1], red);
345  (unsigned)c.UL[0]-1, (unsigned)c.UL[1]-1,
346  (unsigned)c.LR[0]+1, (unsigned)c.LR[1]+1, red);
348  (unsigned)c.UL[0]+1, (unsigned)c.UL[1]+1,
349  (unsigned)c.LR[0]-1, (unsigned)c.LR[1]-1, red);
350  }
351  return 0;
352 }
353 
354 namespace BIAS{
355 template class BlobDetectorBFS<unsigned char>;
356 template class BlobDetectorBFS<float>;
357 
358 // fill in instances as required
359 #ifdef BUILD_IMAGE_INT
360 template class BlobDetectorBFS<int>;
361 #endif
362 #ifdef BUILD_IMAGE_CHAR
363 //template class BlobDetectorBFS<char>;
364 #endif
365 #ifdef BUILD_IMAGE_SHORT
366 #endif
367 #ifdef BUILD_IMAGE_USHORT
368 //template class BlobDetectorBFS<unsigned short>;
369 #endif
370 #ifdef BUILD_IMAGE_UINT
371 #endif
372 #ifdef BUILD_IMAGE_DOUBLE
373 #endif
374 }
unsigned int weight
void CopyIn_NoInit(void *data)
Take some data and fill it into the Image.
Definition: ImageBase.cpp:827
BIAS::Vector2< double > LR
static int CircleCenterFilled(Image< StorageType > &im, unsigned int CenterX, unsigned int CenterY, unsigned int Radius, const StorageType Value[])
draws a filled circle using Value
Definition: ImageDraw.cpp:1023
unsigned int GetWidth() const
Definition: ImageBase.hh:312
int Detect(BIAS::Image< StorageType > &image, std::vector< BIAS::BIASBlob > &blobs)
Detect and return blobs in an image.
static int RectangleCorners(Image< StorageType > &im, const int minx, const int miny, const int maxx, const int maxy, const StorageType value[])
rectangles
Definition: ImageDraw.cpp:94
Helper class to store blob corners.
int ClusterDescent_(const unsigned int x, const unsigned int y, const unsigned int w, const unsigned int h, StorageType **idaIn, unsigned char **idaMark, BIAS::BIASBlob &corners, StorageType **idaOrig)
protected blobDetection function
BIAS::Vector2< double > centerofmass
unsigned int GetChannelCount() const
returns the number of Color channels, e.g.
Definition: ImageBase.hh:382
unsigned int GetHeight() const
Definition: ImageBase.hh:319
The image template class for specific storage types.
Definition: Image.hh:78
BlobDetectorBFS()
standard constructor
BIAS::Vector2< double > UL
int DrawInImage(BIAS::Image< StorageType > &image)
draws the detected blobs in image
virtual ~BlobDetectorBFS()
standard destructor
static int ToRGB(const Image< StorageType > &source, Image< StorageType > &dest)
Create a RGB converted copy of source image in this.
const StorageType ** GetImageDataArray() const
overloaded GetImageDataArray() from ImageBase
Definition: Image.hh:153