Open Source Computer Vision Library
https://opencv.org/
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
585 lines
18 KiB
585 lines
18 KiB
/*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 "precomp.hpp" |
|
|
|
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 |
|
|
|
static const CvPoint icvCodeDeltas[8] = |
|
{ CvPoint(1, 0), CvPoint(1, -1), CvPoint(0, -1), CvPoint(-1, -1), CvPoint(-1, 0), CvPoint(-1, 1), CvPoint(0, 1), CvPoint(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. */
|
|
|