| /*M/////////////////////////////////////////////////////////////////////////////////////// |
| // |
| // IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. |
| // |
| // By downloading, copying, installing or using the software you agree to this license. |
| // If you do not agree to this license, do not download, install, |
| // copy or use the software. |
| // |
| // |
| // Intel License Agreement |
| // For Open Source Computer Vision Library |
| // |
| // Copyright (C) 2000, Intel Corporation, all rights reserved. |
| // Third party copyrights are property of their respective owners. |
| // |
| // Redistribution and use in source and binary forms, with or without modification, |
| // are permitted provided that the following conditions are met: |
| // |
| // * Redistribution's of source code must retain the above copyright notice, |
| // this list of conditions and the following disclaimer. |
| // |
| // * Redistribution's in binary form must reproduce the above copyright notice, |
| // this list of conditions and the following disclaimer in the documentation |
| // and/or other materials provided with the distribution. |
| // |
| // * The name of Intel Corporation may not be used to endorse or promote products |
| // derived from this software without specific prior written permission. |
| // |
| // This software is provided by the copyright holders and contributors "as is" and |
| // any express or implied warranties, including, but not limited to, the implied |
| // warranties of merchantability and fitness for a particular purpose are disclaimed. |
| // In no event shall the Intel Corporation or contributors be liable for any direct, |
| // indirect, incidental, special, exemplary, or consequential damages |
| // (including, but not limited to, procurement of substitute goods or services; |
| // loss of use, data, or profits; or business interruption) however caused |
| // and on any theory of liability, whether in contract, strict liability, |
| // or tort (including negligence or otherwise) arising in any way out of |
| // the use of this software, even if advised of the possibility of such damage. |
| // |
| //M*/ |
| |
| #include "_cvaux.h" |
| |
| typedef struct Seg |
| { |
| ushort y; |
| ushort l; |
| ushort r; |
| ushort Prevl; |
| ushort Prevr; |
| short fl; |
| } |
| Seg; |
| |
| #define UP 1 |
| #define DOWN -1 |
| |
| #define PUSH(Y,IL,IR,IPL,IPR,FL) { stack[StIn].y=(ushort)(Y); \ |
| stack[StIn].l=(ushort)(IL); \ |
| stack[StIn].r=(ushort)(IR); \ |
| stack[StIn].Prevl=(ushort)(IPL); \ |
| stack[StIn].Prevr=(ushort)(IPR); \ |
| stack[StIn].fl=(short)(FL); \ |
| StIn++; } |
| |
| #define POP(Y,IL,IR,IPL,IPR,FL) { StIn--; \ |
| Y=stack[StIn].y; \ |
| IL=stack[StIn].l; \ |
| IR=stack[StIn].r;\ |
| IPL=stack[StIn].Prevl; \ |
| IPR=stack[StIn].Prevr; \ |
| FL=stack[StIn].fl; } |
| |
| |
| #define DIFF(p1,p2) ((unsigned)((p1)[0] - (p2)[0] + d_lw)<=Interval && \ |
| (unsigned)((p1)[1] - (p2)[1] + d_lw)<=Interval && \ |
| (unsigned)((p1)[2] - (p2)[2] + d_lw)<=Interval) |
| |
| /*#define DIFF(p1,p2) (CV_IABS((p1)[0] - (p2)[0]) + \ |
| CV_IABS((p1)[1] - (p2)[1]) + \ |
| CV_IABS((p1)[2] - (p2)[2]) <=Interval )*/ |
| |
| static CvStatus |
| icvSegmFloodFill_Stage1( uchar* pImage, int step, |
| uchar* pMask, int maskStep, |
| CvSize /*roi*/, CvPoint seed, |
| int* newVal, int d_lw, int d_up, |
| CvConnectedComp * region, |
| void *pStack ) |
| { |
| uchar* img = pImage + step * seed.y; |
| uchar* mask = pMask + maskStep * (seed.y + 1); |
| unsigned Interval = (unsigned) (d_up + d_lw); |
| Seg *stack = (Seg*)pStack; |
| int StIn = 0; |
| int i, L, R; |
| int area = 0; |
| int sum[] = { 0, 0, 0 }; |
| int XMin, XMax, YMin = seed.y, YMax = seed.y; |
| int val0[3]; |
| |
| L = R = seed.x; |
| img = pImage + seed.y*step; |
| mask = pMask + seed.y*maskStep; |
| mask[L] = 1; |
| |
| val0[0] = img[seed.x*3]; |
| val0[1] = img[seed.x*3 + 1]; |
| val0[2] = img[seed.x*3 + 2]; |
| |
| while( DIFF( img + (R+1)*3, /*img + R*3*/val0 ) && !mask[R + 1] ) |
| mask[++R] = 2; |
| |
| while( DIFF( img + (L-1)*3, /*img + L*3*/val0 ) && !mask[L - 1] ) |
| mask[--L] = 2; |
| |
| XMax = R; |
| XMin = L; |
| PUSH( seed.y, L, R, R + 1, R, UP ); |
| |
| while( StIn ) |
| { |
| int k, YC, PL, PR, flag/*, curstep*/; |
| |
| POP( YC, L, R, PL, PR, flag ); |
| |
| int data[][3] = { {-flag, L, R}, {flag, L, PL-1}, {flag,PR+1,R}}; |
| |
| if( XMax < R ) |
| XMax = R; |
| |
| if( XMin > L ) |
| XMin = L; |
| |
| if( YMax < YC ) |
| YMax = YC; |
| |
| if( YMin > YC ) |
| YMin = YC; |
| |
| for( k = 0; k < 3; k++ ) |
| { |
| flag = data[k][0]; |
| /*curstep = flag * step;*/ |
| img = pImage + (YC + flag) * step; |
| mask = pMask + (YC + flag) * maskStep; |
| int left = data[k][1]; |
| int right = data[k][2]; |
| |
| for( i = left; i <= right; i++ ) |
| { |
| if( !mask[i] && DIFF( img + i*3, /*img - curstep + i*3*/val0 )) |
| { |
| int j = i; |
| mask[i] = 2; |
| while( !mask[j - 1] && DIFF( img + (j - 1)*3, /*img + j*3*/val0 )) |
| mask[--j] = 2; |
| |
| while( !mask[i + 1] && |
| (DIFF( img + (i+1)*3, /*img + i*3*/val0 ) || |
| (DIFF( img + (i+1)*3, /*img + (i+1)*3 - curstep*/val0) && i < R))) |
| mask[++i] = 2; |
| |
| PUSH( YC + flag, j, i, L, R, -flag ); |
| i++; |
| } |
| } |
| } |
| |
| img = pImage + YC * step; |
| |
| for( i = L; i <= R; i++ ) |
| { |
| sum[0] += img[i*3]; |
| sum[1] += img[i*3 + 1]; |
| sum[2] += img[i*3 + 2]; |
| } |
| |
| area += R - L + 1; |
| } |
| |
| region->area = area; |
| region->rect.x = XMin; |
| region->rect.y = YMin; |
| region->rect.width = XMax - XMin + 1; |
| region->rect.height = YMax - YMin + 1; |
| region->value = cvScalarAll(0); |
| |
| { |
| double inv_area = area ? 1./area : 0; |
| newVal[0] = cvRound( sum[0] * inv_area ); |
| newVal[1] = cvRound( sum[1] * inv_area ); |
| newVal[2] = cvRound( sum[2] * inv_area ); |
| } |
| |
| return CV_NO_ERR; |
| } |
| |
| |
| #undef PUSH |
| #undef POP |
| #undef DIFF |
| |
| |
| static CvStatus |
| icvSegmFloodFill_Stage2( uchar* pImage, int step, |
| uchar* pMask, int maskStep, |
| CvSize /*roi*/, int* newVal, |
| CvRect rect ) |
| { |
| uchar* img = pImage + step * rect.y + rect.x * 3; |
| uchar* mask = pMask + maskStep * rect.y + rect.x; |
| uchar uv[] = { (uchar)newVal[0], (uchar)newVal[1], (uchar)newVal[2] }; |
| int x, y; |
| |
| for( y = 0; y < rect.height; y++, img += step, mask += maskStep ) |
| for( x = 0; x < rect.width; x++ ) |
| if( mask[x] == 2 ) |
| { |
| mask[x] = 1; |
| img[x*3] = uv[0]; |
| img[x*3+1] = uv[1]; |
| img[x*3+2] = uv[2]; |
| } |
| |
| return CV_OK; |
| } |
| |
| #if 0 |
| static void color_derv( const CvArr* srcArr, CvArr* dstArr, int thresh ) |
| { |
| static int tab[] = { 0, 2, 2, 1 }; |
| |
| uchar *src = 0, *dst = 0; |
| int dst_step, src_step; |
| int x, y; |
| CvSize size; |
| |
| cvGetRawData( srcArr, (uchar**)&src, &src_step, &size ); |
| cvGetRawData( dstArr, (uchar**)&dst, &dst_step, 0 ); |
| |
| memset( dst, 0, size.width*sizeof(dst[0])); |
| memset( (uchar*)dst + dst_step*(size.height-1), 0, size.width*sizeof(dst[0])); |
| src += 3; |
| |
| #define CV_IABS(a) (((a) ^ ((a) < 0 ? -1 : 0)) - ((a) < 0 ? -1 : 0)) |
| |
| for( y = 1; y < size.height - 1; y++ ) |
| { |
| src += src_step; |
| dst += dst_step; |
| uchar* src0 = src; |
| |
| dst[0] = dst[size.width - 1] = 0; |
| |
| for( x = 1; x < size.width - 1; x++, src += 3 ) |
| { |
| /*int d[3]; |
| int ad[3]; |
| int f0, f1; |
| int val;*/ |
| int m[3]; |
| double val; |
| //double xx, yy; |
| int dh[3]; |
| int dv[3]; |
| dh[0] = src[0] - src[-3]; |
| dv[0] = src[0] - src[-src_step]; |
| dh[1] = src[1] - src[-2]; |
| dv[1] = src[1] - src[1-src_step]; |
| dh[2] = src[2] - src[-1]; |
| dv[2] = src[2] - src[2-src_step]; |
| |
| m[0] = dh[0]*dh[0] + dh[1]*dh[1] + dh[2]*dh[2]; |
| m[2] = dh[0]*dv[0] + dh[1]*dv[1] + dh[2]*dv[2]; |
| m[1] = dv[0]*dv[0] + dv[1]*dv[1] + dh[2]*dh[2]; |
| |
| val = (m[0] + m[2]) + |
| sqrt(((double)((double)m[0] - m[2]))*(m[0] - m[2]) + (4.*m[1])*m[1]); |
| |
| /* |
| |
| xx = m[1]; |
| yy = v - m[0]; |
| v /= sqrt(xx*xx + yy*yy) + 1e-7; |
| xx *= v; |
| yy *= v; |
| |
| dx[x] = (short)cvRound(xx); |
| dy[x] = (short)cvRound(yy); |
| |
| //dx[x] = (short)cvRound(v); |
| |
| //dx[x] = dy[x] = (short)v; |
| d[0] = src[0] - src[-3]; |
| ad[0] = CV_IABS(d[0]); |
| |
| d[1] = src[1] - src[-2]; |
| ad[1] = CV_IABS(d[1]); |
| |
| d[2] = src[2] - src[-1]; |
| ad[2] = CV_IABS(d[2]); |
| |
| f0 = ad[1] > ad[0]; |
| f1 = ad[2] > ad[f0]; |
| |
| val = d[tab[f0*2 + f1]]; |
| |
| d[0] = src[0] - src[-src_step]; |
| ad[0] = CV_IABS(d[0]); |
| |
| d[1] = src[1] - src[1-src_step]; |
| ad[1] = CV_IABS(d[1]); |
| |
| d[2] = src[2] - src[2-src_step]; |
| ad[2] = CV_IABS(d[2]); |
| |
| f0 = ad[1] > ad[0]; |
| f1 = ad[2] > ad[f0]; |
| |
| dst[x] = (uchar)(val + d[tab[f0*2 + f1]] > thresh ? 255 : 0);*/ |
| dst[x] = (uchar)(val > thresh); |
| } |
| |
| src = src0; |
| } |
| |
| } |
| #endif |
| |
| const CvPoint icvCodeDeltas[8] = |
| { {1, 0}, {1, -1}, {0, -1}, {-1, -1}, {-1, 0}, {-1, 1}, {0, 1}, {1, 1} }; |
| |
| static CvSeq* |
| icvGetComponent( uchar* img, int step, CvRect rect, |
| CvMemStorage* storage ) |
| { |
| const char nbd = 4; |
| int deltas[16]; |
| int x, y; |
| CvSeq* exterior = 0; |
| char* ptr; |
| |
| /* initialize local state */ |
| CV_INIT_3X3_DELTAS( deltas, step, 1 ); |
| memcpy( deltas + 8, deltas, 8 * sizeof( deltas[0] )); |
| |
| ptr = (char*)(img + step*rect.y); |
| rect.width += rect.x; |
| rect.height += rect.y; |
| |
| for( y = rect.y; y < rect.height; y++, ptr += step ) |
| { |
| int prev = ptr[rect.x - 1] & -2; |
| |
| for( x = rect.x; x < rect.width; x++ ) |
| { |
| int p = ptr[x] & -2; |
| |
| //assert( exterior || ((p | prev) & -4) == 0 ); |
| |
| if( p != prev ) |
| { |
| CvSeq *seq = 0; |
| int is_hole = 0; |
| CvSeqWriter writer; |
| char *i0, *i1, *i3, *i4 = 0; |
| int prev_s = -1, s, s_end; |
| CvPoint pt = { x, y }; |
| |
| if( !(prev == 0 && p == 2) ) /* if not external contour */ |
| { |
| /* check hole */ |
| if( p != 0 || prev < 1 ) |
| { |
| prev = p; |
| continue; |
| } |
| |
| is_hole = 1; |
| if( !exterior ) |
| { |
| assert(0); |
| return 0; |
| } |
| } |
| |
| cvStartWriteSeq( CV_SEQ_CONTOUR | (is_hole ? CV_SEQ_FLAG_HOLE : 0), |
| sizeof(CvContour), sizeof(CvPoint), storage, &writer ); |
| s_end = s = is_hole ? 0 : 4; |
| i0 = ptr + x - is_hole; |
| |
| do |
| { |
| s = (s - 1) & 7; |
| i1 = i0 + deltas[s]; |
| if( (*i1 & -2) != 0 ) |
| break; |
| } |
| while( s != s_end ); |
| |
| if( s == s_end ) /* single pixel domain */ |
| { |
| *i0 = (char) (nbd | -128); |
| CV_WRITE_SEQ_ELEM( pt, writer ); |
| } |
| else |
| { |
| i3 = i0; |
| prev_s = s ^ 4; |
| |
| /* follow border */ |
| for( ;; ) |
| { |
| s_end = s; |
| |
| for( ;; ) |
| { |
| i4 = i3 + deltas[++s]; |
| if( (*i4 & -2) != 0 ) |
| break; |
| } |
| s &= 7; |
| |
| /* check "right" bound */ |
| if( (unsigned) (s - 1) < (unsigned) s_end ) |
| { |
| *i3 = (char) (nbd | -128); |
| } |
| else if( *i3 > 0 ) |
| { |
| *i3 = nbd; |
| } |
| |
| if( s != prev_s ) |
| { |
| CV_WRITE_SEQ_ELEM( pt, writer ); |
| prev_s = s; |
| } |
| |
| pt.x += icvCodeDeltas[s].x; |
| pt.y += icvCodeDeltas[s].y; |
| |
| if( i4 == i0 && i3 == i1 ) |
| break; |
| |
| i3 = i4; |
| s = (s + 4) & 7; |
| } /* end of border following loop */ |
| } |
| |
| seq = cvEndWriteSeq( &writer ); |
| cvContourBoundingRect( seq, 1 ); |
| |
| if( !is_hole ) |
| exterior = seq; |
| else |
| { |
| seq->v_prev = exterior; |
| seq->h_next = exterior->v_next; |
| if( seq->h_next ) |
| seq->h_next->h_prev = seq; |
| exterior->v_next = seq; |
| } |
| |
| prev = ptr[x] & -2; |
| } |
| } |
| } |
| |
| return exterior; |
| } |
| |
| |
| |
| CV_IMPL CvSeq* |
| cvSegmentImage( const CvArr* srcarr, CvArr* dstarr, |
| double canny_threshold, |
| double ffill_threshold, |
| CvMemStorage* storage ) |
| { |
| CvSeq* root = 0; |
| CvMat* gray = 0; |
| CvMat* canny = 0; |
| //CvMat* temp = 0; |
| void* stack = 0; |
| |
| CV_FUNCNAME( "cvSegmentImage" ); |
| |
| __BEGIN__; |
| |
| CvMat srcstub, *src; |
| CvMat dststub, *dst; |
| CvMat* mask; |
| CvSize size; |
| CvPoint pt; |
| int ffill_lw_up = cvRound( fabs(ffill_threshold) ); |
| CvSeq* prev_seq = 0; |
| |
| CV_CALL( src = cvGetMat( srcarr, &srcstub )); |
| CV_CALL( dst = cvGetMat( dstarr, &dststub )); |
| |
| size = cvGetSize( src ); |
| |
| CV_CALL( gray = cvCreateMat( size.height, size.width, CV_8UC1 )); |
| CV_CALL( canny = cvCreateMat( size.height, size.width, CV_8UC1 )); |
| //CV_CALL( temp = cvCreateMat( size.height/2, size.width/2, CV_8UC3 )); |
| |
| CV_CALL( stack = cvAlloc( size.width * size.height * sizeof(Seg))); |
| |
| cvCvtColor( src, gray, CV_BGR2GRAY ); |
| cvCanny( gray, canny, 0/*canny_threshold*0.4*/, canny_threshold, 3 ); |
| cvThreshold( canny, canny, 1, 1, CV_THRESH_BINARY ); |
| //cvZero( canny ); |
| //color_derv( src, canny, canny_threshold ); |
| |
| //cvPyrDown( src, temp ); |
| //cvPyrUp( temp, dst ); |
| |
| //src = dst; |
| mask = canny; // a new name for new role |
| |
| // make a non-zero border. |
| cvRectangle( mask, cvPoint(0,0), cvPoint(size.width-1,size.height-1), cvScalarAll(1), 1 ); |
| |
| for( pt.y = 0; pt.y < size.height; pt.y++ ) |
| { |
| for( pt.x = 0; pt.x < size.width; pt.x++ ) |
| { |
| if( mask->data.ptr[mask->step*pt.y + pt.x] == 0 ) |
| { |
| CvConnectedComp region; |
| int avgVal[3] = { 0, 0, 0 }; |
| |
| icvSegmFloodFill_Stage1( src->data.ptr, src->step, |
| mask->data.ptr, mask->step, |
| size, pt, avgVal, |
| ffill_lw_up, ffill_lw_up, |
| ®ion, stack ); |
| |
| /*avgVal[0] = (avgVal[0] + 15) & -32; |
| if( avgVal[0] > 255 ) |
| avgVal[0] = 255; |
| avgVal[1] = (avgVal[1] + 15) & -32; |
| if( avgVal[1] > 255 ) |
| avgVal[1] = 255; |
| avgVal[2] = (avgVal[2] + 15) & -32; |
| if( avgVal[2] > 255 ) |
| avgVal[2] = 255;*/ |
| |
| if( storage ) |
| { |
| CvSeq* tmpseq = icvGetComponent( mask->data.ptr, mask->step, |
| region.rect, storage ); |
| if( tmpseq != 0 ) |
| { |
| ((CvContour*)tmpseq)->color = avgVal[0] + (avgVal[1] << 8) + (avgVal[2] << 16); |
| tmpseq->h_prev = prev_seq; |
| if( prev_seq ) |
| prev_seq->h_next = tmpseq; |
| else |
| root = tmpseq; |
| prev_seq = tmpseq; |
| } |
| } |
| |
| icvSegmFloodFill_Stage2( dst->data.ptr, dst->step, |
| mask->data.ptr, mask->step, |
| size, avgVal, |
| region.rect ); |
| } |
| } |
| } |
| |
| __END__; |
| |
| //cvReleaseMat( &temp ); |
| cvReleaseMat( &gray ); |
| cvReleaseMat( &canny ); |
| cvFree( &stack ); |
| |
| return root; |
| } |
| |
| /* End of file. */ |