Project

General

Profile

Statistics
| Branch: | Revision:

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
}