Basic Image AlgorithmS Library  2.8.0
 All Classes Namespaces Functions Variables Typedefs Enumerations Enumerator Friends Groups Pages
BlobDetectorDOM.cpp
1 /*
2  * BlobDetectorDOM.cpp
3  *
4  * Created on: Nov 10, 2009
5  * Author: jordt
6  */
7 
8 
9 #include "BlobDetectorDOM.hh"
10 #include <Base/ImageUtils/ImageDraw.hh>
11 
12 using namespace std;
13 using namespace BIAS;
14 
15 template <class StorageType>
17  // Just some default values
18  diffThreshold_ = 20.0;
19  minSeedSize_ = 5;
20  maxSeedSize_ = 55;
21  seedSizeStep_ = 10;
22  minBlobSize_ = 2;
23  maxBlobSize_ = 500;
24  numberOfBins_ = 10;
25  maximumBinPopulation_ = 7;
26  similarityThreshold_ = 3;
27  intImg_ = (double*)NULL;
28 }
29 
30 template <class StorageType>
32  if (intImg_ != NULL) delete[] intImg_;
33 }
34 
35 template <class StorageType>
37 
38  // Build integral image
39  CreateIntegralImage(image);
40 
41  // Erase old data
42  generatedBlobs_.resize(0);
43  blobQualityVec_.resize(0);
44 
45  // If no blobs are provided, generate seeds and optimize them:
46  if (blobs.size() == 0){
47 
48  GenerateAndOptimize(generatedBlobs_,blobQualityVec_);
49 
50  refineBlobPosAndSize(image);
51  // Convert the HomgPoint2D data to the BIASBlob data
52  for (unsigned int i = 0; i < generatedBlobs_.size(); i++){
53  if (blobQualityVec_[i] >= diffThreshold_ && minBlobSize_ <= generatedBlobs_[i][2] && maxBlobSize_ >= generatedBlobs_[i][2]){
54  BIASBlob blob;
55  blob.UL[0] = generatedBlobs_[i][0] - generatedBlobs_[i][2];
56  blob.UL[1] = generatedBlobs_[i][1] - generatedBlobs_[i][2];
57  blob.LR[0] = generatedBlobs_[i][0] + generatedBlobs_[i][2];
58  blob.LR[1] = generatedBlobs_[i][1] + generatedBlobs_[i][2];
59  blob.centerofmass[0] = generatedBlobs_[i][0];
60  blob.centerofmass[1] = generatedBlobs_[i][1];
61  blob.weight = (unsigned int) blobQualityVec_[i];
62  blobs.push_back(blob);
63 
64  }
65  }
66  }
67  return 0;
68 }
69 
70 template <class StorageType>
72 
73  for (unsigned int i = 0; i < generatedBlobs_.size(); i++)
74 
75  ImageDraw<StorageType>::InterpolatedCircleCenter(image, generatedBlobs_[i], (float)generatedBlobs_[i][2],color,1.5f,0.6f);
76 
77 
78  return 0;
79 }
80 
81 template <class StorageType>
83  diffThreshold_ = threshold;
84 }
85 
86 template <class StorageType>
87 void BlobDetectorDOM<StorageType>:: SetCabin(int cabinsPerAxis, int maxPopulation){
88  numberOfBins_ = cabinsPerAxis;
89  maximumBinPopulation_ = maxPopulation;
90 }
91 
92 template <class StorageType>
93 void BlobDetectorDOM<StorageType>::SetMinSize(unsigned int minSize){
94  // Divided by 2 because we use half window size parameters
95  minBlobSize_ = minSize / 2;
96 }
97 
98 template <class StorageType>
99 void BlobDetectorDOM<StorageType>::SetMaxSize(unsigned int maxSize){
100  // Divided by 2 because we use half window size parameters
101  maxBlobSize_ = maxSize / 2;
102 }
103 
104 template <class StorageType>
106  unsigned int maxSeedSize,
107  unsigned int seedSizeStep){
108  // Divided by 2 because we use half window size parameters
109  minSeedSize_ = minSeedSize / 2;
110  maxSeedSize_ = maxSeedSize / 2;
111  seedSizeStep_ = seedSizeStep / 2;
112 }
113 
114 
115 template <class StorageType>
116 void BlobDetectorDOM<StorageType>::GetBlobsAsHomgPoint2D(vector<HomgPoint2D> &blobs,vector<float> &qual_vec){
117 
118  blobs = generatedBlobs_;
119  qual_vec = blobQualityVec_;
120 }
121 
122 
123 template <class StorageType>
124 void BlobDetectorDOM<StorageType>::GenerateAndOptimize(vector<HomgPoint2D> &pos_vec, vector<float> &qual_vec){
125 
126  // Cycle through all blob sizes
127  for (int blobSeedSize = minSeedSize_; (unsigned int)blobSeedSize <= maxSeedSize_; blobSeedSize += (int)seedSizeStep_){
128 
129  // Cycle through x and y coordinates to plant seeds
130  for (int x = blobSeedSize; x < (int)width_ - (int)blobSeedSize; x += (int)blobSeedSize)
131  for (int y = blobSeedSize; y < (int)width_ -(int)blobSeedSize; y += (int)blobSeedSize){
132 
133  // Get the nearest DOM maximum from seed position
134  HomgPoint2D blob = GetLocalMaximum(x,y, blobSeedSize);
135  float qual = GetDOM((int)blob[0], (int)blob[1], (int)blob[2]);
136 
137  // If the blob matches the criteria, compare it to blobs in the vicinity:
138  if (qual > diffThreshold_ && blob[2] < maxBlobSize_){
139  int size = pos_vec.size();
140  bool found = false;
141 
142  unsigned int regionPopulation = 0;
143  unsigned int worstInRegion = 0;
144  float worstValueInRegion = FLT_MAX;
145 
146  for (int s = 0; s < size; s++){
147  if (fabs(pos_vec[s][0] - blob[0]) < ((float)width_/(float)numberOfBins_) &&
148  fabs(pos_vec[s][1] - blob[1]) < ((float)height_/(float)numberOfBins_)){
149  if (fabs(pos_vec[s][0] - blob[0]) < similarityThreshold_ &&
150  fabs(pos_vec[s][1] - blob[1]) < similarityThreshold_ &&
151  fabs(pos_vec[s][2] - blob[2]) < similarityThreshold_){
152  found = true;
153  // if a similar blob was allready generated by an other seed, replace it
154  // if the current dom value is better, otherwise drop it
155  if ((float)qual > qual_vec[s]){
156  pos_vec[s] = blob;
157  qual_vec[s] = (float)qual;
158  }
159  } else {
160  regionPopulation ++;
161  if (qual_vec[s] <= worstValueInRegion){
162  worstValueInRegion = (float)qual_vec[s];
163  worstInRegion = s;
164  }
165  }
166  }
167  }
168  if (!found){
169 
170  if (regionPopulation < maximumBinPopulation_){
171  // If the maximum population is not reached, add the current blob
172  pos_vec.push_back(blob);
173  qual_vec.push_back(qual);
174  } else {
175  if (worstValueInRegion <= qual){
176  // If the blob is better than local one and the maximum population is reached,
177  // replace the worst one with the current one
178  qual_vec[worstInRegion] = qual;
179  pos_vec[worstInRegion] = blob;
180  }
181  }
182  }
183  }
184  }
185  }
186 
187 
188 }
189 
190 template <class StorageType>
192 
193 
194  //Cycle through all blobs:
195  for (int i = 0; i < (int)generatedBlobs_.size(); i++){
196  // Get the inner mean value:
197 
198  int x = (int)generatedBlobs_[i][0];
199  int y = (int)generatedBlobs_[i][1];
200  int halfLevelSize = (int)generatedBlobs_[i][2];
201 
202  // For Documentation of this part see GetDOM()
203  double innerValueSum = EvalIntegralImage(x-halfLevelSize,y-halfLevelSize,x+halfLevelSize+1,y+halfLevelSize+1);
204  double completeValueSum = EvalIntegralImage(x-(halfLevelSize*2),y-(halfLevelSize*2),x+(halfLevelSize*2)+1,y+(halfLevelSize*2)+1);\
205  double outerValueSum = completeValueSum - innerValueSum;
206  double numOfInnerElements = (halfLevelSize*2+1)*(halfLevelSize*2+1);
207  double numOfOuterElements = (halfLevelSize*4+1)*(halfLevelSize*4+1) - numOfInnerElements;
208  //double threshold = (outerValueSum/numOfOuterElements) +
209  // ((innerValueSum/numOfInnerElements) - (outerValueSum/numOfOuterElements))/2.0;
210  double DOMValue = ((innerValueSum/numOfInnerElements) - (outerValueSum/numOfOuterElements));
211  bool positiveBlob = ((innerValueSum/numOfInnerElements) > (outerValueSum/numOfOuterElements));
212  //int ElementsAboveThreshold = 0;
213  float weightSum = 0.0;
214  generatedBlobs_[i][0] = 0.0;
215  generatedBlobs_[i][1] = 0.0;
216  generatedBlobs_[i][2] = 0.0;
217  StorageType** ida= img.GetImageDataArray();
218  int numChannels = img.GetChannelCount();
219  for (int xSearch = x-halfLevelSize; xSearch < x+halfLevelSize+1; xSearch++)
220  for (int ySearch = y-halfLevelSize; ySearch < y+halfLevelSize+1; ySearch++){
221  if ((positiveBlob && (float)ida[ySearch][numChannels*xSearch] >= (outerValueSum/numOfOuterElements))||
222  (!positiveBlob && (float)ida[ySearch][numChannels*xSearch] <= (outerValueSum/numOfOuterElements))) {
223 
224  if ((float)ida[ySearch][numChannels*xSearch] >= (innerValueSum/numOfInnerElements) ||
225  (!positiveBlob && (float)ida[ySearch][numChannels*xSearch] <= (innerValueSum/numOfInnerElements))){
226  generatedBlobs_[i][0] += (float)xSearch;
227  generatedBlobs_[i][1] += (float)ySearch;
228  weightSum += 1.0;
229  } else {
230  float weight = fabs(((float)ida[ySearch][numChannels*xSearch] - float(outerValueSum/numOfOuterElements))
231  / float(DOMValue));
232  generatedBlobs_[i][0] += (float)xSearch * weight;
233  generatedBlobs_[i][1] += (float)ySearch * weight;
234  weightSum += weight;
235  }
236 
237  }
238  }
239 
240  generatedBlobs_[i][0] /= (float)weightSum;
241  generatedBlobs_[i][1] /= (float)weightSum;
242 
243  for (int xSearch = x-halfLevelSize; xSearch < x+halfLevelSize+1; xSearch++)
244  for (int ySearch = y-halfLevelSize; ySearch < y+halfLevelSize+1; ySearch++){
245  if ((positiveBlob && (float)ida[ySearch][numChannels*xSearch] >= (outerValueSum/numOfOuterElements))||
246  (!positiveBlob && (float)ida[ySearch][numChannels*xSearch] <= (outerValueSum/numOfOuterElements))) {
247  float xDiff = (float)xSearch - (float)generatedBlobs_[i][0];
248  float yDiff = (float)ySearch - (float)generatedBlobs_[i][1];
249  float dist = sqrt(xDiff*xDiff+yDiff*yDiff);
250  float weight = fabs(((float)ida[ySearch][numChannels*xSearch] - (float)(outerValueSum/numOfOuterElements))
251  / (float)DOMValue);
252  generatedBlobs_[i][2] += dist*weight;
253 
254  }
255  }
256  generatedBlobs_[i][2] /=weightSum;
257  }
258 
259 }
260 
261 
262 template <class StorageType>
264  HomgPoint2D pos;
265  pos[0] = x;
266  pos[1] = y;
267  pos[2] = halfLevelSize;
268  float currentDOMVal = 0.0;
269  float newDOMVal = GetDOM((int)pos[0], (int)pos[1], (int)pos[2]);
270  int bestx_step = 0;
271  int besty_step = 0;
272  int bestl_step = 0;
273  while(currentDOMVal < newDOMVal){
274  currentDOMVal = newDOMVal;
275  float bestDOMVal = newDOMVal;
276  // Look in the direct vicinity of the current dom location and search for a direction
277  // with a higher dom value
278  for (int x_step = -1; x_step <= 1; x_step++){
279  for (int y_step = -1; y_step <= 1; y_step++){
280  for (int l_step = -1; l_step <= 1; l_step++){
281 
282  float DOMVal = GetDOM((int)pos[0]+x_step, (int)pos[1]+y_step, (int)pos[2]+l_step);
283  if (DOMVal > bestDOMVal){
284  bestDOMVal = DOMVal;
285  bestx_step = x_step;
286  besty_step = y_step;
287  bestl_step = l_step;
288  }
289 
290  }
291  }
292  }
293  newDOMVal = bestDOMVal;
294  pos[0] = pos[0]+bestx_step;
295  pos[1] = pos[1]+besty_step;
296  pos[2] = pos[2]+bestl_step;
297  if (pos[2] < 3 || pos[2] > 30) return pos;
298  }
299  return pos;
300 }
301 
302 
303 template <class StorageType>
304 float BlobDetectorDOM<StorageType>::GetDOM(int x, int y, int halfLevelSize){
305 
306  // If the blob is out of bounds, return 0
307  if (x - halfLevelSize*2 < 0 || y - halfLevelSize*2 < 0 || x + halfLevelSize*2 +1 >= (int)width_ || y + halfLevelSize*2 +1>= (int)height_)
308  return 0.0;
309 
310  // Calculate the sum of the inner pixels via integral image:
311  double innerValueSum = EvalIntegralImage(x-halfLevelSize,y-halfLevelSize,x+halfLevelSize+1,y+halfLevelSize+1);
312  // Calculate the sum of pixel values of the doubled square via integral image:
313  double completeValueSum = EvalIntegralImage(x-(halfLevelSize*2),y-(halfLevelSize*2),x+(halfLevelSize*2)+1,y+(halfLevelSize*2)+1);\
314  // outer sum is the big sum minus the inner sum
315  double outerValueSum = completeValueSum - innerValueSum;
316  // Calculate number of pixels:
317  double numOfInnerElements = (halfLevelSize*2+1)*(halfLevelSize*2+1);
318  double numOfOuterElements = (halfLevelSize*4+1)*(halfLevelSize*4+1) - numOfInnerElements;
319  // DOM is average inner minus average outer pixel value
320  double DOMValue = fabs((innerValueSum/numOfInnerElements) - (outerValueSum/numOfOuterElements));
321 
322  return (float)DOMValue;
323 }
324 
325 template <class StorageType>
326 float BlobDetectorDOM<StorageType>::GetAbsoluteDOM(int x, int y, int halfLevelSize){
327 
328  // If the blob is out of bounds, return 0
329  if (x - halfLevelSize*2 < 0 || y - halfLevelSize*2 < 0 || x + halfLevelSize*2 +1 >= (int)width_ || y + halfLevelSize*2 +1>= (int)height_)
330  return 0.0;
331 
332  // Calculate the sum of the inner pixels via integral image:
333  double innerValueSum = EvalIntegralImage(x-halfLevelSize,y-halfLevelSize,x+halfLevelSize+1,y+halfLevelSize+1);
334  // Calculate the sum of pixel values of the doubled square via integral image:
335  double completeValueSum = EvalIntegralImage(x-(halfLevelSize*2),y-(halfLevelSize*2),x+(halfLevelSize*2)+1,y+(halfLevelSize*2)+1);\
336  // outer sum is the big sum minus the inner sum
337  double outerValueSum = completeValueSum - innerValueSum;
338  // Calculate number of pixels:
339  double numOfInnerElements = (halfLevelSize*2+1)*(halfLevelSize*2+1);
340  double numOfOuterElements = (halfLevelSize*4+1)*(halfLevelSize*4+1) - numOfInnerElements;
341  // DOM is average inner minus average outer pixel value
342  return float((innerValueSum/numOfInnerElements) - (outerValueSum/numOfOuterElements));
343 }
344 
345 
346 
347 
348 
349 // ***********************************************************
350 // Integral Image Routines
351 template <class StorageType>
353 
354  if (intImg_ != NULL) delete[]intImg_;
355  width_ = in.GetWidth();
356  height_ = in.GetHeight();
357 
358  unsigned int channels = in.GetChannelCount();
359 
360  intImg_ = new double[width_*height_];
361  StorageType** idaIn = in.GetImageDataArray();
362 
363  intImg_[0] = (double)idaIn[0][0];
364 
365  for (unsigned int x=1; x < width_; x++)
366  intImg_[x] = (double)idaIn[0][x]+intImg_[x-1];
367 
368  for (unsigned int y=1; y < height_; y++)
369  intImg_[y*width_] = (double)idaIn[y][0]+intImg_[(y-1)*width_];
370 
371  for (unsigned int y=1; y < height_; y++)
372  for (unsigned int x=1; x < width_; x++)
373  intImg_[y*width_+x] = (double)idaIn[y][x*channels]+intImg_[(y-1)*width_+x]
374  +intImg_[y*width_+(x-1)] - intImg_[(y-1)*width_+(x-1)];
375 
376 }
377 
378 template <class StorageType>
379 double BlobDetectorDOM<StorageType>::EvalIntegralImage(int xA,int yA,int xB,int yB){
380  return (intImg_[yB*width_+xB] - intImg_[yB*width_+xA] - intImg_[yA*width_+xB] + intImg_[yA*width_+xA]);
381 }
382 
383 
384 namespace BIAS{
385 template class BlobDetectorDOM<unsigned char>;
386 template class BlobDetectorDOM<float>;
387 
388 // fill in instances as required
389 #ifdef BUILD_IMAGE_INT
390 template class BlobDetectorDOM<int>;
391 #endif
392 #ifdef BUILD_IMAGE_CHAR
393 //template class BlobDetectorBFS<char>;
394 #endif
395 #ifdef BUILD_IMAGE_SHORT
396 #endif
397 #ifdef BUILD_IMAGE_USHORT
398 //template class BlobDetectorBFS<unsigned short>;
399 #endif
400 #ifdef BUILD_IMAGE_UINT
401 #endif
402 #ifdef BUILD_IMAGE_DOUBLE
403 #endif
404 }
unsigned int weight
class HomgPoint2D describes a point with 2 degrees of freedom in projective coordinates.
Definition: HomgPoint2D.hh:67
BIAS::Vector2< double > LR
unsigned int GetWidth() const
Definition: ImageBase.hh:312
Helper class to store blob corners.
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
BIAS::Vector2< double > UL
Blob detector for &#39;Difference Of Means&#39;-blobs (so this is not a binary blob detector).
drawing simple entities into the image like rectangles or lines As all functions are static they have...
Definition: ImageDraw.hh:72
const StorageType ** GetImageDataArray() const
overloaded GetImageDataArray() from ImageBase
Definition: Image.hh:153