root / vision / src / blob.cpp @ 8711dd52
History | View | Annotate | Download (16.2 KB)
1 |
/************************************************************************
|
---|---|
2 |
Blob.cpp
|
3 |
|
4 |
- FUNCIONALITAT: Implementaci? de la classe CBlob
|
5 |
- AUTOR: Inspecta S.L.
|
6 |
MODIFICACIONS (Modificaci?, Autor, Data):
|
7 |
|
8 |
|
9 |
FUNCTIONALITY: Implementation of the CBlob class and some helper classes to perform
|
10 |
some calculations on it
|
11 |
AUTHOR: Inspecta S.L.
|
12 |
MODIFICATIONS (Modification, Author, Date):
|
13 |
|
14 |
**************************************************************************/
|
15 |
|
16 |
|
17 |
#include "blob.h" |
18 |
|
19 |
|
20 |
CBlob::CBlob() |
21 |
{ |
22 |
m_area = m_perimeter = -1;
|
23 |
m_externPerimeter = m_meanGray = m_stdDevGray = -1;
|
24 |
m_boundingBox.width = -1;
|
25 |
m_ellipse.size.width = -1;
|
26 |
m_storage = NULL;
|
27 |
m_id = -1;
|
28 |
} |
29 |
CBlob::CBlob( t_labelType id, CvPoint startPoint, CvSize originalImageSize ) |
30 |
{ |
31 |
m_id = id; |
32 |
m_area = m_perimeter = -1;
|
33 |
m_externPerimeter = m_meanGray = m_stdDevGray = -1;
|
34 |
m_boundingBox.width = -1;
|
35 |
m_ellipse.size.width = -1;
|
36 |
m_storage = cvCreateMemStorage(); |
37 |
m_externalContour = CBlobContour(startPoint, m_storage); |
38 |
m_originalImageSize = originalImageSize; |
39 |
} |
40 |
//! Copy constructor
|
41 |
CBlob::CBlob( const CBlob &src )
|
42 |
{ |
43 |
m_storage = NULL;
|
44 |
*this = src;
|
45 |
} |
46 |
|
47 |
CBlob::CBlob( const CBlob *src )
|
48 |
{ |
49 |
if (src != NULL ) |
50 |
{ |
51 |
m_storage = NULL;
|
52 |
*this = *src;
|
53 |
} |
54 |
} |
55 |
|
56 |
CBlob& CBlob::operator=(const CBlob &src ) |
57 |
{ |
58 |
if( this != &src ) |
59 |
{ |
60 |
m_id = src.m_id; |
61 |
m_area = src.m_area; |
62 |
m_perimeter = src.m_perimeter; |
63 |
m_externPerimeter = src.m_externPerimeter; |
64 |
m_meanGray = src.m_meanGray; |
65 |
m_stdDevGray = src.m_stdDevGray; |
66 |
m_boundingBox = src.m_boundingBox; |
67 |
m_ellipse = src.m_ellipse; |
68 |
m_originalImageSize = src.m_originalImageSize; |
69 |
|
70 |
// clear all current blob contours
|
71 |
ClearContours(); |
72 |
|
73 |
if( m_storage )
|
74 |
cvReleaseMemStorage( &m_storage ); |
75 |
|
76 |
m_storage = cvCreateMemStorage(); |
77 |
|
78 |
m_externalContour = CBlobContour(src.m_externalContour.GetStartPoint(), m_storage ); |
79 |
if( src.m_externalContour.m_contour )
|
80 |
m_externalContour.m_contour = cvCloneSeq( src.m_externalContour.m_contour, m_storage); |
81 |
m_internalContours.clear(); |
82 |
|
83 |
// copy all internal contours
|
84 |
if( src.m_internalContours.size() )
|
85 |
{ |
86 |
m_internalContours = t_contourList( src.m_internalContours.size() ); |
87 |
t_contourList::const_iterator itSrc; |
88 |
t_contourList::iterator it; |
89 |
|
90 |
itSrc = src.m_internalContours.begin(); |
91 |
it = m_internalContours.begin(); |
92 |
|
93 |
while (itSrc != src.m_internalContours.end())
|
94 |
{ |
95 |
*it = CBlobContour((*itSrc).GetStartPoint(), m_storage); |
96 |
if( (*itSrc).m_contour )
|
97 |
(*it).m_contour = cvCloneSeq( (*itSrc).m_contour, m_storage); |
98 |
|
99 |
it++; |
100 |
itSrc++; |
101 |
} |
102 |
} |
103 |
} |
104 |
|
105 |
return *this; |
106 |
} |
107 |
|
108 |
CBlob::~CBlob() |
109 |
{ |
110 |
ClearContours(); |
111 |
|
112 |
if( m_storage )
|
113 |
cvReleaseMemStorage( &m_storage ); |
114 |
} |
115 |
|
116 |
void CBlob::ClearContours()
|
117 |
{ |
118 |
t_contourList::iterator it; |
119 |
|
120 |
it = m_internalContours.begin(); |
121 |
|
122 |
while (it != m_internalContours.end())
|
123 |
{ |
124 |
(*it).ResetChainCode(); |
125 |
it++; |
126 |
} |
127 |
m_internalContours.clear(); |
128 |
|
129 |
m_externalContour.ResetChainCode(); |
130 |
|
131 |
} |
132 |
void CBlob::AddInternalContour( const CBlobContour &newContour ) |
133 |
{ |
134 |
m_internalContours.push_back(newContour); |
135 |
} |
136 |
|
137 |
//! Indica si el blob est? buit ( no t? cap info associada )
|
138 |
//! Shows if the blob has associated information
|
139 |
bool CBlob::IsEmpty()
|
140 |
{ |
141 |
return GetExternalContour()->m_contour == NULL; |
142 |
} |
143 |
|
144 |
/**
|
145 |
- FUNCI?: Area
|
146 |
- FUNCIONALITAT: Get blob area, ie. external contour area minus internal contours area
|
147 |
- PAR?METRES:
|
148 |
-
|
149 |
- RESULTAT:
|
150 |
-
|
151 |
- RESTRICCIONS:
|
152 |
-
|
153 |
- AUTOR: rborras
|
154 |
- DATA DE CREACI?: 2008/04/30
|
155 |
- MODIFICACI?: Data. Autor. Descripci?.
|
156 |
*/
|
157 |
double CBlob::Area()
|
158 |
{ |
159 |
double area;
|
160 |
t_contourList::iterator itContour; |
161 |
|
162 |
area = m_externalContour.GetArea(); |
163 |
|
164 |
itContour = m_internalContours.begin(); |
165 |
|
166 |
while (itContour != m_internalContours.end() )
|
167 |
{ |
168 |
area -= (*itContour).GetArea(); |
169 |
itContour++; |
170 |
} |
171 |
return area;
|
172 |
} |
173 |
|
174 |
/**
|
175 |
- FUNCI?: Perimeter
|
176 |
- FUNCIONALITAT: Get blob perimeter, ie. sum of the lenght of all the contours
|
177 |
- PAR?METRES:
|
178 |
-
|
179 |
- RESULTAT:
|
180 |
-
|
181 |
- RESTRICCIONS:
|
182 |
-
|
183 |
- AUTOR: rborras
|
184 |
- DATA DE CREACI?: 2008/04/30
|
185 |
- MODIFICACI?: Data. Autor. Descripci?.
|
186 |
*/
|
187 |
double CBlob::Perimeter()
|
188 |
{ |
189 |
double perimeter;
|
190 |
t_contourList::iterator itContour; |
191 |
|
192 |
perimeter = m_externalContour.GetPerimeter(); |
193 |
|
194 |
itContour = m_internalContours.begin(); |
195 |
|
196 |
while (itContour != m_internalContours.end() )
|
197 |
{ |
198 |
perimeter += (*itContour).GetPerimeter(); |
199 |
itContour++; |
200 |
} |
201 |
return perimeter;
|
202 |
|
203 |
} |
204 |
|
205 |
/**
|
206 |
- FUNCI?: Exterior
|
207 |
- FUNCIONALITAT: Return true for extern blobs
|
208 |
- PAR?METRES:
|
209 |
- xBorder: true to consider blobs touching horizontal borders as extern
|
210 |
- yBorder: true to consider blobs touching vertical borders as extern
|
211 |
- RESULTAT:
|
212 |
-
|
213 |
- RESTRICCIONS:
|
214 |
-
|
215 |
- AUTOR: rborras
|
216 |
- DATA DE CREACI?: 2008/05/06
|
217 |
- MODIFICACI?: Data. Autor. Descripci?.
|
218 |
*/
|
219 |
int CBlob::Exterior(IplImage *mask, bool xBorder /* = true */, bool yBorder /* = true */) |
220 |
{ |
221 |
if (ExternPerimeter(mask, xBorder, yBorder ) > 0 ) |
222 |
{ |
223 |
return 1; |
224 |
} |
225 |
|
226 |
return 0; |
227 |
} |
228 |
/**
|
229 |
- FUNCI?: ExternPerimeter
|
230 |
- FUNCIONALITAT: Get extern perimeter (perimeter touching image borders)
|
231 |
- PAR?METRES:
|
232 |
- maskImage: if != NULL, counts maskImage black pixels as external pixels and contour points touching
|
233 |
them are counted as external contour points.
|
234 |
- xBorder: true to consider blobs touching horizontal borders as extern
|
235 |
- yBorder: true to consider blobs touching vertical borders as extern
|
236 |
- RESULTAT:
|
237 |
-
|
238 |
- RESTRICCIONS:
|
239 |
-
|
240 |
- AUTOR: rborras
|
241 |
- DATA DE CREACI?: 2008/05/05
|
242 |
- MODIFICACI?: Data. Autor. Descripci?.
|
243 |
- NOTA: If CBlobContour::GetContourPoints aproximates contours with a method different that NONE,
|
244 |
this function will not give correct results
|
245 |
*/
|
246 |
double CBlob::ExternPerimeter( IplImage *maskImage, bool xBorder /* = true */, bool yBorder /* = true */) |
247 |
{ |
248 |
t_PointList externContour, externalPoints; |
249 |
CvSeqReader reader; |
250 |
CvSeqWriter writer; |
251 |
CvPoint actualPoint, previousPoint; |
252 |
bool find = false; |
253 |
int i,j;
|
254 |
int delta = 0; |
255 |
|
256 |
// it is calculated?
|
257 |
if( m_externPerimeter != -1 ) |
258 |
{ |
259 |
return m_externPerimeter;
|
260 |
} |
261 |
|
262 |
// get contour pixels
|
263 |
externContour = m_externalContour.GetContourPoints(); |
264 |
|
265 |
m_externPerimeter = 0;
|
266 |
|
267 |
// there are contour pixels?
|
268 |
if( externContour == NULL ) |
269 |
{ |
270 |
return m_externPerimeter;
|
271 |
} |
272 |
|
273 |
cvStartReadSeq( externContour, &reader); |
274 |
|
275 |
// create a sequence with the external points of the blob
|
276 |
externalPoints = cvCreateSeq( externContour->flags, externContour->header_size, externContour->elem_size, |
277 |
m_storage ); |
278 |
cvStartAppendToSeq( externalPoints, &writer ); |
279 |
previousPoint.x = -1;
|
280 |
|
281 |
// which contour pixels touch border?
|
282 |
for( j=0; j< externContour->total; j++) |
283 |
{ |
284 |
CV_READ_SEQ_ELEM( actualPoint, reader); |
285 |
|
286 |
find = false;
|
287 |
|
288 |
// pixel is touching border?
|
289 |
if ( xBorder & ((actualPoint.x == 0) || (actualPoint.x == m_originalImageSize.width - 1 )) || |
290 |
yBorder & ((actualPoint.y == 0) || (actualPoint.y == m_originalImageSize.height - 1 ))) |
291 |
{ |
292 |
find = true;
|
293 |
} |
294 |
else
|
295 |
{ |
296 |
if( maskImage != NULL ) |
297 |
{ |
298 |
// verify if some of 8-connected neighbours is black in mask
|
299 |
char *pMask;
|
300 |
|
301 |
pMask = (maskImage->imageData + actualPoint.x - 1 + (actualPoint.y - 1) * maskImage->widthStep); |
302 |
|
303 |
for ( i = 0; i < 3; i++, pMask++ ) |
304 |
{ |
305 |
if(*pMask == 0 && !find ) |
306 |
{ |
307 |
find = true;
|
308 |
break;
|
309 |
} |
310 |
} |
311 |
|
312 |
if(!find)
|
313 |
{ |
314 |
pMask = (maskImage->imageData + actualPoint.x - 1 + (actualPoint.y ) * maskImage->widthStep);
|
315 |
|
316 |
for ( i = 0; i < 3; i++, pMask++ ) |
317 |
{ |
318 |
if(*pMask == 0 && !find ) |
319 |
{ |
320 |
find = true;
|
321 |
break;
|
322 |
} |
323 |
} |
324 |
} |
325 |
|
326 |
if(!find)
|
327 |
{ |
328 |
pMask = (maskImage->imageData + actualPoint.x - 1 + (actualPoint.y + 1) * maskImage->widthStep); |
329 |
|
330 |
for ( i = 0; i < 3; i++, pMask++ ) |
331 |
{ |
332 |
if(*pMask == 0 && !find ) |
333 |
{ |
334 |
find = true;
|
335 |
break;
|
336 |
} |
337 |
} |
338 |
} |
339 |
} |
340 |
} |
341 |
|
342 |
if( find )
|
343 |
{ |
344 |
if( previousPoint.x > 0 ) |
345 |
delta = abs(previousPoint.x - actualPoint.x) + abs(previousPoint.y - actualPoint.y); |
346 |
|
347 |
// calculate separately each external contour segment
|
348 |
if( delta > 2 ) |
349 |
{ |
350 |
cvEndWriteSeq( &writer ); |
351 |
m_externPerimeter += cvArcLength( externalPoints, CV_WHOLE_SEQ, 0 );
|
352 |
|
353 |
cvClearSeq( externalPoints ); |
354 |
cvStartAppendToSeq( externalPoints, &writer ); |
355 |
delta = 0;
|
356 |
previousPoint.x = -1;
|
357 |
} |
358 |
|
359 |
CV_WRITE_SEQ_ELEM( actualPoint, writer ); |
360 |
previousPoint = actualPoint; |
361 |
} |
362 |
|
363 |
} |
364 |
|
365 |
cvEndWriteSeq( &writer ); |
366 |
|
367 |
m_externPerimeter += cvArcLength( externalPoints, CV_WHOLE_SEQ, 0 );
|
368 |
|
369 |
cvClearSeq( externalPoints ); |
370 |
|
371 |
// divide by two because external points have one side inside the blob and the other outside
|
372 |
// Perimeter of external points counts both sides, so it must be divided
|
373 |
m_externPerimeter /= 2.0; |
374 |
|
375 |
return m_externPerimeter;
|
376 |
} |
377 |
|
378 |
//! Compute blob's moment (p,q up to MAX_CALCULATED_MOMENTS)
|
379 |
double CBlob::Moment(int p, int q) |
380 |
{ |
381 |
double moment;
|
382 |
t_contourList::iterator itContour; |
383 |
|
384 |
moment = m_externalContour.GetMoment(p,q); |
385 |
|
386 |
itContour = m_internalContours.begin(); |
387 |
|
388 |
while (itContour != m_internalContours.end() )
|
389 |
{ |
390 |
moment -= (*itContour).GetMoment(p,q); |
391 |
itContour++; |
392 |
} |
393 |
return moment;
|
394 |
} |
395 |
|
396 |
/**
|
397 |
- FUNCI?: Mean
|
398 |
- FUNCIONALITAT: Get blob mean color in input image
|
399 |
- PAR?METRES:
|
400 |
- image: image from gray color are extracted
|
401 |
- RESULTAT:
|
402 |
-
|
403 |
- RESTRICCIONS:
|
404 |
-
|
405 |
- AUTOR: rborras
|
406 |
- DATA DE CREACI?: 2008/05/06
|
407 |
- MODIFICACI?: Data. Autor. Descripci?.
|
408 |
*/
|
409 |
double CBlob::Mean( IplImage *image )
|
410 |
{ |
411 |
// it is calculated?
|
412 |
/* if( m_meanGray != -1 )
|
413 |
{
|
414 |
return m_meanGray;
|
415 |
}
|
416 |
*/
|
417 |
// Create a mask with same size as blob bounding box
|
418 |
IplImage *mask; |
419 |
CvScalar mean, std; |
420 |
CvPoint offset; |
421 |
|
422 |
GetBoundingBox(); |
423 |
|
424 |
if (m_boundingBox.height == 0 ||m_boundingBox.width == 0 || !CV_IS_IMAGE( image )) |
425 |
{ |
426 |
m_meanGray = 0;
|
427 |
return m_meanGray;
|
428 |
} |
429 |
|
430 |
// apply ROI and mask to input image to compute mean gray and standard deviation
|
431 |
mask = cvCreateImage( cvSize(m_boundingBox.width, m_boundingBox.height), IPL_DEPTH_8U, 1);
|
432 |
cvSetZero(mask); |
433 |
|
434 |
offset.x = -m_boundingBox.x; |
435 |
offset.y = -m_boundingBox.y; |
436 |
|
437 |
// draw contours on mask
|
438 |
cvDrawContours( mask, m_externalContour.GetContourPoints(), CV_RGB(255,255,255), CV_RGB(255,255,255),0, CV_FILLED, 8, |
439 |
offset ); |
440 |
|
441 |
// draw internal contours
|
442 |
t_contourList::iterator it = m_internalContours.begin(); |
443 |
while(it != m_internalContours.end() )
|
444 |
{ |
445 |
cvDrawContours( mask, (*it).GetContourPoints(), CV_RGB(0,0,0), CV_RGB(0,0,0),0, CV_FILLED, 8, |
446 |
offset ); |
447 |
it++; |
448 |
} |
449 |
|
450 |
cvSetImageROI( image, m_boundingBox ); |
451 |
cvAvgSdv( image, &mean, &std, mask ); |
452 |
|
453 |
m_meanGray = mean.val[0];
|
454 |
m_stdDevGray = std.val[0];
|
455 |
|
456 |
cvReleaseImage( &mask ); |
457 |
cvResetImageROI( image ); |
458 |
|
459 |
return m_meanGray;
|
460 |
} |
461 |
|
462 |
double CBlob::StdDev( IplImage *image )
|
463 |
{ |
464 |
// it is calculated?
|
465 |
/* if( m_stdDevGray != -1 )
|
466 |
{
|
467 |
return m_stdDevGray;
|
468 |
}
|
469 |
*/
|
470 |
// call mean calculation (where also standard deviation is calculated)
|
471 |
Mean( image ); |
472 |
|
473 |
return m_stdDevGray;
|
474 |
} |
475 |
/**
|
476 |
- FUNCI?: GetBoundingBox
|
477 |
- FUNCIONALITAT: Get bounding box (without rotation) of a blob
|
478 |
- PAR?METRES:
|
479 |
-
|
480 |
- RESULTAT:
|
481 |
-
|
482 |
- RESTRICCIONS:
|
483 |
-
|
484 |
- AUTOR: rborras
|
485 |
- DATA DE CREACI?: 2008/05/06
|
486 |
- MODIFICACI?: Data. Autor. Descripci?.
|
487 |
*/
|
488 |
CvRect CBlob::GetBoundingBox() |
489 |
{ |
490 |
// it is calculated?
|
491 |
if( m_boundingBox.width != -1 ) |
492 |
{ |
493 |
return m_boundingBox;
|
494 |
} |
495 |
|
496 |
t_PointList externContour; |
497 |
CvSeqReader reader; |
498 |
CvPoint actualPoint; |
499 |
|
500 |
// get contour pixels
|
501 |
externContour = m_externalContour.GetContourPoints(); |
502 |
|
503 |
// it is an empty blob?
|
504 |
if( !externContour )
|
505 |
{ |
506 |
m_boundingBox.x = 0;
|
507 |
m_boundingBox.y = 0;
|
508 |
m_boundingBox.width = 0;
|
509 |
m_boundingBox.height = 0;
|
510 |
|
511 |
return m_boundingBox;
|
512 |
} |
513 |
|
514 |
cvStartReadSeq( externContour, &reader); |
515 |
|
516 |
m_boundingBox.x = m_originalImageSize.width; |
517 |
m_boundingBox.y = m_originalImageSize.height; |
518 |
m_boundingBox.width = 0;
|
519 |
m_boundingBox.height = 0;
|
520 |
|
521 |
for( int i=0; i< externContour->total; i++) |
522 |
{ |
523 |
CV_READ_SEQ_ELEM( actualPoint, reader); |
524 |
|
525 |
m_boundingBox.x = MIN( actualPoint.x, m_boundingBox.x ); |
526 |
m_boundingBox.y = MIN( actualPoint.y, m_boundingBox.y ); |
527 |
|
528 |
m_boundingBox.width = MAX( actualPoint.x, m_boundingBox.width ); |
529 |
m_boundingBox.height = MAX( actualPoint.y, m_boundingBox.height ); |
530 |
} |
531 |
|
532 |
//m_boundingBox.x = max( m_boundingBox.x , 0 );
|
533 |
//m_boundingBox.y = max( m_boundingBox.y , 0 );
|
534 |
|
535 |
m_boundingBox.width -= m_boundingBox.x; |
536 |
m_boundingBox.height -= m_boundingBox.y; |
537 |
|
538 |
return m_boundingBox;
|
539 |
} |
540 |
|
541 |
/**
|
542 |
- FUNCI?: GetEllipse
|
543 |
- FUNCIONALITAT: Calculates bounding ellipse of external contour points
|
544 |
- PAR?METRES:
|
545 |
-
|
546 |
- RESULTAT:
|
547 |
-
|
548 |
- RESTRICCIONS:
|
549 |
-
|
550 |
- AUTOR: rborras
|
551 |
- DATA DE CREACI?: 2008/05/06
|
552 |
- MODIFICACI?: Data. Autor. Descripci?.
|
553 |
- NOTA: Calculation is made using second order moment aproximation
|
554 |
*/
|
555 |
CvBox2D CBlob::GetEllipse() |
556 |
{ |
557 |
// it is calculated?
|
558 |
if( m_ellipse.size.width != -1 ) |
559 |
return m_ellipse;
|
560 |
|
561 |
double u00,u11,u01,u10,u20,u02, delta, num, den, temp;
|
562 |
|
563 |
// central moments calculation
|
564 |
u00 = Moment(0,0); |
565 |
|
566 |
// empty blob?
|
567 |
if ( u00 <= 0 ) |
568 |
{ |
569 |
m_ellipse.size.width = 0;
|
570 |
m_ellipse.size.height = 0;
|
571 |
m_ellipse.center.x = 0;
|
572 |
m_ellipse.center.y = 0;
|
573 |
m_ellipse.angle = 0;
|
574 |
return m_ellipse;
|
575 |
} |
576 |
u10 = Moment(1,0) / u00; |
577 |
u01 = Moment(0,1) / u00; |
578 |
|
579 |
u11 = -(Moment(1,1) - Moment(1,0) * Moment(0,1) / u00 ) / u00; |
580 |
u20 = (Moment(2,0) - Moment(1,0) * Moment(1,0) / u00 ) / u00; |
581 |
u02 = (Moment(0,2) - Moment(0,1) * Moment(0,1) / u00 ) / u00; |
582 |
|
583 |
|
584 |
// elipse calculation
|
585 |
delta = sqrt( 4*u11*u11 + (u20-u02)*(u20-u02) );
|
586 |
m_ellipse.center.x = u10; |
587 |
m_ellipse.center.y = u01; |
588 |
|
589 |
temp = u20 + u02 + delta; |
590 |
if( temp > 0 ) |
591 |
{ |
592 |
m_ellipse.size.width = sqrt( 2*(u20 + u02 + delta ));
|
593 |
} |
594 |
else
|
595 |
{ |
596 |
m_ellipse.size.width = 0;
|
597 |
return m_ellipse;
|
598 |
} |
599 |
|
600 |
temp = u20 + u02 - delta; |
601 |
if( temp > 0 ) |
602 |
{ |
603 |
m_ellipse.size.height = sqrt( 2*(u20 + u02 - delta ) );
|
604 |
} |
605 |
else
|
606 |
{ |
607 |
m_ellipse.size.height = 0;
|
608 |
return m_ellipse;
|
609 |
} |
610 |
|
611 |
// elipse orientation
|
612 |
if (u20 > u02)
|
613 |
{ |
614 |
num = u02 - u20 + sqrt((u02 - u20)*(u02 - u20) + 4*u11*u11);
|
615 |
den = 2*u11;
|
616 |
} |
617 |
else
|
618 |
{ |
619 |
num = 2*u11;
|
620 |
den = u20 - u02 + sqrt((u20 - u02)*(u20 - u02) + 4*u11*u11);
|
621 |
} |
622 |
if( num != 0 && den != 00 ) |
623 |
{ |
624 |
m_ellipse.angle = 180.0 + (180.0 / CV_PI) * atan( num / den ); |
625 |
} |
626 |
else
|
627 |
{ |
628 |
m_ellipse.angle = 0;
|
629 |
} |
630 |
|
631 |
return m_ellipse;
|
632 |
|
633 |
} |
634 |
|
635 |
/**
|
636 |
- FUNCTION: FillBlob
|
637 |
- FUNCTIONALITY:
|
638 |
- Fills the blob with a specified colour
|
639 |
- PARAMETERS:
|
640 |
- imatge: where to paint
|
641 |
- color: colour to paint the blob
|
642 |
- RESULT:
|
643 |
- modifies input image and returns the seed point used to fill the blob
|
644 |
- RESTRICTIONS:
|
645 |
- AUTHOR: Ricard Borr?s
|
646 |
- CREATION DATE: 25-05-2005.
|
647 |
- MODIFICATION: Date. Author. Description.
|
648 |
*/
|
649 |
void CBlob::FillBlob( IplImage *imatge, CvScalar color, int offsetX /*=0*/, int offsetY /*=0*/) |
650 |
{ |
651 |
cvDrawContours( imatge, m_externalContour.GetContourPoints(), color, color,0, CV_FILLED, 8 ); |
652 |
} |
653 |
|
654 |
|
655 |
/**
|
656 |
- FUNCTION: GetConvexHull
|
657 |
- FUNCTIONALITY: Calculates the convex hull polygon of the blob
|
658 |
- PARAMETERS:
|
659 |
- dst: where to store the result
|
660 |
- RESULT:
|
661 |
- true if no error ocurred
|
662 |
- RESTRICTIONS:
|
663 |
- AUTHOR: Ricard Borr?s
|
664 |
- CREATION DATE: 25-05-2005.
|
665 |
- MODIFICATION: Date. Author. Description.
|
666 |
*/
|
667 |
t_PointList CBlob::GetConvexHull() |
668 |
{ |
669 |
CvSeq *convexHull = NULL;
|
670 |
|
671 |
if( m_externalContour.GetContourPoints() )
|
672 |
convexHull = cvConvexHull2( m_externalContour.GetContourPoints(), m_storage, |
673 |
CV_COUNTER_CLOCKWISE, 1 );
|
674 |
|
675 |
return convexHull;
|
676 |
} |
677 |
|
678 |
/**
|
679 |
- FUNCTION: JoinBlob
|
680 |
- FUNCTIONALITY: Add's external contour to current external contour
|
681 |
- PARAMETERS:
|
682 |
- blob: blob from which extract the added external contour
|
683 |
- RESULT:
|
684 |
- true if no error ocurred
|
685 |
- RESTRICTIONS: Only external contours are added
|
686 |
- AUTHOR: Ricard Borr?s
|
687 |
- CREATION DATE: 25-05-2005.
|
688 |
- MODIFICATION: Date. Author. Description.
|
689 |
*/
|
690 |
void CBlob::JoinBlob( CBlob *blob )
|
691 |
{ |
692 |
CvSeqWriter writer; |
693 |
CvSeqReader reader; |
694 |
t_chainCode chainCode; |
695 |
|
696 |
cvStartAppendToSeq( m_externalContour.GetChainCode(), &writer ); |
697 |
cvStartReadSeq( blob->GetExternalContour()->GetChainCode(), &reader ); |
698 |
|
699 |
for (int i = 0; i < blob->GetExternalContour()->GetChainCode()->total; i++ ) |
700 |
{ |
701 |
CV_READ_SEQ_ELEM( chainCode, reader ); |
702 |
CV_WRITE_SEQ_ELEM( chainCode, writer ); |
703 |
} |
704 |
cvEndWriteSeq( &writer ); |
705 |
|
706 |
} |