【图像算法】彩色图像分割专题三:边缘检测+区域生长 法

【图像算法】彩色图像分割专题三:边缘检测+区域生长法

  SkySeraph May 15th 2011  HQU

Email:zgzhaobo@gmail.com    QQ:452728574

Latest Modified Date:May 15th 2011 HQU

一 原理:

空间转换:RGB转换为HSI  http://www.cnblogs.com/skyseraph/archive/2011/05/03/2035643.html,结果见实现图1

边缘检测:在HSI空间,对HSI、H、S、I分别利用Canny进行边缘检测,结果见实现图2

区域生长:首先对边缘检测的图像沿边界进行质心计算,把求的的质心作为种子点;区域生长采用四领域像素聚类。

二 源码:

空间转换:http://www.cnblogs.com/skyseraph/archive/2011/05/05/2038317.html

边缘检测:

ContractedBlock.gif ExpandedBlockStart.gif View Code
 
   
1 //
2   // 寻找种子点(边缘检测法)
3   //
4   void CColorSegDlg::OnCanny()
5   // Canny边缘检测
6   {
7 // 验证
8   if ( ! (ToDisplayCtr1))
9 {
10 MessageBox( " Please Load Pic! " );
11 return ;
12 }
13
14 if ( ! (ToDisplayCtr2 && ToDisplayCtr3 && ToDisplayCtr4 && ToDisplayCtr5))
15 {
16 MessageBox( " Please do SpaceConvetion! " );
17 return ;
18 }
19
20 UpdateData(TRUE);
21 int CANNY_T1,CANNY_T2; // canny算子双阈值
22   CANNY_T1 = m_CannyT1;
23 CANNY_T2 = m_CannyT2;
24
25 // 边缘检测图像的 "初始化"
26   ToDisplayCtr2Ed = cvCreateImage(cvGetSize(TheImage),IPL_DEPTH_8U, 1 );
27 ToDisplayCtr3Ed = cvCreateImage(cvGetSize(TheImage),IPL_DEPTH_8U, 1 );
28 ToDisplayCtr4Ed = cvCreateImage(cvGetSize(TheImage),IPL_DEPTH_8U, 1 );
29 ToDisplayCtr5Ed = cvCreateImage(cvGetSize(TheImage),IPL_DEPTH_8U, 1 );
30
31 / /对各通道分量
32   // 定义工作位图并加载
33   IplImage * a;
34 a = ToDisplayCtr3;
35 IplImage * b;
36 b = ToDisplayCtr4;
37 IplImage * c;
38 c = ToDisplayCtr5;
39
40 // 定义辅助位图,描述边缘检测后图像
41   IplImage * aCanny = cvCreateImage(cvGetSize(a),IPL_DEPTH_8U, 1 );
42 IplImage * bCanny = cvCreateImage(cvGetSize(b),IPL_DEPTH_8U, 1 );
43 IplImage * cCanny = cvCreateImage(cvGetSize(c),IPL_DEPTH_8U, 1 );
44
45 // Canny边缘检测
46 // cvSobel(a,aCanny,1,0,3); // 水平sobel核
47 cvCanny(a, aCanny, CANNY_T1, CANNY_T2); // 阈值选择!
48 cvCanny(b, bCanny, CANNY_T1, CANNY_T2);
49 cvCanny(c, cCanny, CANNY_T1, CANNY_T2);
50
51 // 输出并显示
52 // imageReplace(aCanny,&ToDisplayCtr3);
53 // imageReplace(bCanny,&ToDisplayCtr4);
54 // imageReplace(cCanny,&ToDisplayCtr5);
55
56 cvCopyImage(aCanny,ToDisplayCtr3Ed); // 输出处理结果
57 cvCopyImage(bCanny,ToDisplayCtr4Ed);
58 cvCopyImage(cCanny,ToDisplayCtr5Ed);
59
60 DrawPicToHDC(ToDisplayCtr3Ed,IDC_ImgShowCtrl3); // 显示
61 DrawPicToHDC(ToDisplayCtr4Ed,IDC_ImgShowCtrl4);
62 DrawPicToHDC(ToDisplayCtr5Ed,IDC_ImgShowCtrl5);
63
64 // 释放资源
65 cvReleaseImage( & aCanny);
66 cvReleaseImage( & bCanny);
67 cvReleaseImage( & cCanny);
68
69
70 / /对**空间图像
71 /// *
72 // 定义工作位图并加载
73 IplImage * dstColor;
74 dstColor = ToDisplayCtr2;
75 IplImage * dstGray = cvCreateImage(cvGetSize(dstColor),IPL_DEPTH_8U, 1 ); // cvCanny只接受单通道图像作为输入
76 cvCvtColor(dstColor,dstGray,CV_RGB2GRAY);
77
78 // 定义辅助位图,描述边缘检测后图像
79 IplImage * dstCannyGray = cvCreateImage(cvGetSize(dstGray),IPL_DEPTH_8U, 1 ); // cvCanny只接受单通道图像作为输入
80
81 // Canny边缘检测
82 cvCanny(dstGray,dstCannyGray,CANNY_T1,CANNY_T2);
83
84 // 输出并显示
85 cvCopyImage(dstCannyGray,ToDisplayCtr2Ed);
86 DrawPicToHDC(ToDisplayCtr2Ed,IDC_ImgShowCtrl2);
87
88 cvReleaseImage( & dstGray);
89 cvReleaseImage( & dstCannyGray);
90 // */
91
92 }
93
94 void CColorSegDlg::OnSeedsPoint()
95 {
96 // 验证
97 if ( ! (ToDisplayCtr1))
98 {
99 MessageBox( " Please Load Pic! " );
100 return ;
101 }
102
103 if ( ! (ToDisplayCtr2 && ToDisplayCtr3 && ToDisplayCtr4 && ToDisplayCtr5))
104 {
105 MessageBox( " Please do SpaceConvetion! " );
106 return ;
107 }
108
109 UpdateData(TRUE);
110
111 long seedNum = 0 ; // 种子点数
112 seed_Header = (seed_Node * )malloc( sizeof (seed_Node)); // 种子点,链表存贮
113
114 // 定义工作位图
115 IplImage * src ;
116 IplImage * srcCanny;
117 src = ToDisplayCtr2;
118
119 if (m_RGB == 0 ) // R
120 {
121 // src = ToDisplayCtr3;
122 srcCanny = ToDisplayCtr3Ed;
123 }
124
125 if (m_RGB == 1 ) // G
126 {
127 // src = ToDisplayCtr4;
128 srcCanny = ToDisplayCtr4Ed;
129 }
130
131 if (m_RGB == 2 ) // B
132 {
133 // src = ToDisplayCtr5;
134 srcCanny = ToDisplayCtr5Ed;
135 }
136
137 if (m_RGB == 3 ) // RGB
138 {
139 // src = ToDisplayCtr2;
140 srcCanny = ToDisplayCtr2Ed;
141 }
142
143 // 验证
144 if ( ! src && ! srcCanny)
145 {
146 MessageBox( " wrong! " );
147 return ;
148 }
149
150 // 定义辅助位图
151 IplImage * dst = NULL;
152 IplImage * dstCanny = NULL;
153
154 dst = cvCreateImage(cvGetSize(src),IPL_DEPTH_8U, 3 );
155 dstCanny = cvCreateImage(cvGetSize(srcCanny),IPL_DEPTH_8U, 1 );
156 /*
157 if(m_RGB == 3)
158 {
159 dst = cvCreateImage(cvGetSize(src),IPL_DEPTH_8U,3);
160 dstCanny = cvCreateImage(cvGetSize(srcCanny),IPL_DEPTH_8U,1);
161 }
162 else
163 {
164 dst = cvCreateImage(cvGetSize(src),IPL_DEPTH_8U,1);
165 dstCanny = cvCreateImage(cvGetSize(srcCanny),IPL_DEPTH_8U,1);
166 } */
167
168 cvCopyImage(src,dst);
169 cvCopyImage(srcCanny,dstCanny);
170 // dst = src;
171 // dstCanny = srcCanny;
172
173 // 寻找种子点
174 findSeed(dst,dstCanny,seed_Header,seedNum);
175
176 // cout<<seedNum<<endl;
177 m_SeedsPoint = seedNum;
178 UpdateData(FALSE);
179
180 cvReleaseImage( & dst);
181 cvReleaseImage( & dstCanny);
182 }
183
184 // 函数模块
185 // ----------------------------------------------- //
186 // 功能:寻找区域生长种子点
187 // 参数:dst 转换为**空间(如HSI)的彩色图像
188 // bundary 寻找的区域:hsi经canny边缘检测后的图像
189 // seed 种子点,链表存贮
190 // seedNUM 种子点数
191 // 返回:
192 // ----------------------------------------------- //
193 void CColorSegDlg::findSeed(IplImage * dst, IplImage * bundary, seed_Node * seed, long & seedNUM)
194 // 寻找种子点
195 {
196 int width = bundary -> width;
197 int height = bundary -> height;
198 bool * flag = ( bool * )malloc( sizeof ( bool ) * width * height); // 像素访问标记
199 bool first = true ;
200
201 memset(flag, 0 , sizeof ( bool ) * width * height);
202 seedNUM = 0 ;
203 seed_Node * seed_t = seed;
204
205 for ( int row = 0 ; row < height; row ++ ) //
206 for ( int col = 0 ; col < width; col ++ )
207 {
208 if (((uchar * )(bundary -> imageData +
209 row * bundary -> widthStep))[col * bundary -> nChannels] == 0 ) // 像素值==0
210 continue ;
211 if (flag[row * width + col]) // 已经访问过该点
212 continue ;
213 int X = 0 , Y = 0 , num = 0 ;
214 findBundary(bundary, flag, col, row, X, Y, num); // 得到区域重心
215 if (first)
216 {
217 first = false ;
218 }
219 else
220 {
221 seed_t -> next = (seed_Node * )malloc( sizeof (seed_Node));
222 seed_t = seed_t -> next;
223 }
224 seed_t -> x = X / num; // 增加新种子:质心/重心
225 seed_t -> y = Y / num;
226 seed_t -> next = NULL;
227
228 seed_t -> I = ((uchar * )(dst -> imageData + dst -> widthStep * row))[col * dst -> nChannels]; // 修改!
229 seed_t -> J = ((uchar * )(dst -> imageData + dst -> widthStep * row))[col * dst -> nChannels + 1 ];
230 seed_t -> K = ((uchar * )(dst -> imageData + dst -> widthStep * row))[col * dst -> nChannels + 2 ];
231 seed_t -> seedID = ++ seedNUM; // 种子点数加一
232 // segment[bundary->width*seed_t->y + seed_t->x] = seed_t->seedID;
233 }
234
235 free(flag);
236 }
237
238 // ----------------------------------------------- //
239 // 功能:沿边界递归寻找,计算区域重心/质心
240 // 参数:bundary寻找的区域
241 // flag 像素访问标记
242 // x/y 区域中的某点
243 // X/Y 质心/重心
244 // num 边界连接的像素数
245 // ----------------------------------------------- //
246 void CColorSegDlg::findBundary(IplImage * bundary, bool * flag, int x, int y
247 , int & X, int & Y, int & num)
248 // 获取区域重心
249 {
250 if (flag[y * bundary -> width + x]) // 像素已访问
251 return ;
252 if (((uchar * )(bundary -> imageData + y * bundary -> widthStep))[x * bundary -> nChannels] == 0 )
253 return ;
254 flag[y * bundary -> width + x] = true ; // 标记访问
255 X += x; // 质心X方向累加
256 Y += y;
257 num ++ ; // 边界连接的像素数加
258 for ( int i =- 1 ; i < 2 ; i ++ )
259 for ( int j =- 1 ; j < 2 ; j ++ ) // 八点领域扩散
260 {
261 if ( ! i && ! j) continue ;
262 if (x + j < 0 || x + j >= bundary -> width || y + i < 0 || y + i >= bundary -> height)
263 continue ;
264 findBundary(bundary, flag, x + j, y + i, X, Y, num); // 继续寻找
265 }
266 }

区域生长:

//
//						区域生长(基于边缘检测提取种子点)
//
//  区域生长
void CColorSegDlg::OnEdgeRegionGrowth() //消息响应
{
	//  验证
	if(!(ToDisplayCtr1))
	{
		MessageBox("Please Load Pic!");
		return;
	}
	
	if(!(ToDisplayCtr2 && ToDisplayCtr3 && ToDisplayCtr4 && ToDisplayCtr5))
	{
		MessageBox("Please do SpaceConvetion!");
		return;
	}
	
	//  定义工作位图
	IplImage* src ;
	IplImage* srcCanny;
	src = ToDisplayCtr2;
	
	//  判断
	UpdateData(TRUE);
	
	if(m_RGB == 0) //R
	{
		//src = ToDisplayCtr3;
		srcCanny = ToDisplayCtr3Ed;
	}
	
	if(m_RGB == 1) //G
	{
		//src = ToDisplayCtr4;
		srcCanny = ToDisplayCtr4Ed;
	}
	
	if(m_RGB == 2) //B
	{
		//src = ToDisplayCtr5;
		srcCanny = ToDisplayCtr5Ed;
	}
	
	if(m_RGB == 3)//RGB
	{		
		//src = ToDisplayCtr2;
		srcCanny = ToDisplayCtr2Ed;
	}
	
	//  验证
	if(!src && ! srcCanny)
	{
		MessageBox("wrong!");
		return;
	}
	
	//  定义辅助位图
	IplImage* dst = NULL;
	IplImage* dstCanny = NULL;
	
	dst = cvCreateImage(cvGetSize(src),IPL_DEPTH_8U,3);
	dstCanny = cvCreateImage(cvGetSize(srcCanny),IPL_DEPTH_8U,1);
	
	
	cvCopyImage(src,dst);
	cvCopyImage(srcCanny,dstCanny);
	
	//  为分割结果申请空间
	int width = TheImage->width;
	int height = TheImage->height;
	segment = (long *)malloc(sizeof(long)*width*height);    
    memset(segment, 0, sizeof(long)*width*height);
	
	int TT;
	UpdateData(TRUE);
	TT = m_TT;
	
	// 区域生长
    seed_Node *t_Node = seed_Header;
    while(t_Node)                       //基于种子点的区域生长
    {
		regionGrowing(dst, dstCanny, t_Node, t_Node->x, t_Node->y, TT);
		t_Node = t_Node->next;
    }	
	
	//分割结果
	
	//  SegResultImg "初始化"
	SegResultImg = cvCreateImage(cvGetSize(TheImage),IPL_DEPTH_8U,3); 
	//  定义工作位图
	IplImage* SegResultsrc;
	SegResultsrc = SegResultImg;
	
	//  定义辅助位图
	IplImage* SegResultdst = cvCreateImage(cvGetSize(SegResultsrc),IPL_DEPTH_8U,3);
	IplImage* SegResultdstRGB = cvCreateImage(cvGetSize(SegResultsrc),IPL_DEPTH_8U,3);
	
	//print_segment(width, height);
    copy_segment(SegResultdst, seed_Header);
	//HSI2RGB(SegResultdst);
	//cvCvtColor(SegResultdst,SegResultdstRGB,CV_HSV2BGR);
	
	cvCopyImage(SegResultdst,SegResultImg);    
	
	//	cvNamedWindow("SegResultdstRGB result");
	// cvShowImage("SegResultdstRGB result", SegResultdstRGB);
	
	cvNamedWindow("SegResultdst result");
    cvShowImage("SegResultdst result", SegResultdst);
	
	//cvSaveImage("res.bmp", SegResultImg);
	
	cvDestroyWindow("segmentation result");	
	
	cvReleaseImage(&dst);
	cvReleaseImage(&dstCanny);
}

//-----------------------------------------------//
uchar CColorSegDlg::color_distance(uchar h1, uchar h2) 
//  计算颜色距离
{
    if(h1<h2)
        return h2 - h1;
    return h1 - h2;
}

//-----------------------------------------------//
//功能:区域生长		
//参数:dst		转换为**空间(如HSI)的彩色图像
//		bundary	区域:HSI经canny边缘检测后的图像
//		seed	链表存贮的种子点
//		xi/yi	种子点坐标
//		T		相似性准则判断 的阈值
//返回:
//-----------------------------------------------//
void CColorSegDlg::regionGrowing(IplImage *dst, IplImage *bundary, seed_Node *seed,  int xi,  int yi, uchar T)
//  区域生长
{
    int sp = 0;  //栈顶指针
    int width = dst->width;
    int height = dst->height;
    //int stuck[100];
    int *stuck = (int *)malloc(sizeof(int)*width*height*2);//分配堆栈空间,存储种子点坐标
    memset(stuck, 0, sizeof(int)*width*height*2);
    stuck[sp++] = xi;
    stuck[sp++] = yi;
    while(sp)
    {
        int y = stuck[--sp];//取出栈顶元素
        int x = stuck[--sp];
        //if(segment[bundary->width*y + x]!=0 )
        //  continue;
		// 
        uchar a1 = ((uchar *)(dst->imageData + y*dst->widthStep))[dst->nChannels*x];
        uchar b1 = ((uchar *)(dst->imageData + y*dst->widthStep))[dst->nChannels*x+1];
        uchar c1 = ((uchar *)(dst->imageData + y*dst->widthStep))[dst->nChannels*x+2];
		// 
        uchar a2 = ((uchar *)(dst->imageData +
			seed->y*dst->widthStep))[dst->nChannels*seed->x];
        uchar b2 = ((uchar *)(dst->imageData +
			seed->y*dst->widthStep))[dst->nChannels*seed->x+1];
        uchar c2 = ((uchar *)(dst->imageData +
			seed->y*dst->widthStep))[dst->nChannels*seed->x+2];
        //  判断两像素是否属于同一区域
		if(color_distance(a1, a2) > T)  
            continue;
        segment[bundary->width*y + x] = seed->seedID;
        //  重新计算区域颜色
		/*seed->I /= 2;       
        seed->I += a1/2;
        seed->J /= 2;
        seed->J += b1/2;
        seed->K /= 2;
        seed->K += c1/2;*/
		seed->I = a2;
		seed->J = b2;
		seed->K = c2;
		
        for(int i=-1; i<2; i++)
            for(int j=-1; j<2; j++) //对四点领域做扩散
            {
                if((i==-1&&j==-1) || (i==-1&&j==1) || (i==1&&j==-1) || (i==1&&j==1))//4领域
                    continue;
                if(i+y<0 || i+y>=dst->height || j+x<0 || j+x>=dst->width)
                    continue;
                if(segment[bundary->width*(y+i) + x + j]!=0 )
                    continue;
                if(((uchar*)(bundary->imageData+bundary->widthStep*(y+i)))[bundary->nChannels*(x+j)] == 255)
                    //到达边界,结束该方向的生长
                    continue;
				
                stuck[sp++] = x+j;//新种子点入栈
                stuck[sp++] = y+i;
                segment[bundary->width*(y+i) + x + j] = -1;
            }
    }
    free(stuck);
	
}

//-----------------------------------------------//	 
void CColorSegDlg::copy_segment(IplImage *pSeg, seed_Node *node)  
//  生成分割图
{
    int width = pSeg->width;
    int height = pSeg->height;
    for(int row=0; row<height; row++)
        for(int col=0; col<width; col++)
        {
            long id = segment[width*row+col];
            seed_Node *t_node = node;
            uchar I, J, K;
            while(t_node)
            {
                if(t_node->seedID == id)   //遍历确定像素所属的区域
                {
                    I = t_node->I;      //分配像素颜色值
                    J = t_node->J;
                    K = t_node->K;
                    break;
                }
                t_node = t_node->next;
            }
            if(!t_node) 
				continue;
            ((uchar *)(pSeg->imageData+row*pSeg->widthStep))[col*pSeg->nChannels] = I;
            ((uchar *)(pSeg->imageData+row*pSeg->widthStep))[col*pSeg->nChannels+1] = J;
            ((uchar *)(pSeg->imageData+row*pSeg->widthStep))[col*pSeg->nChannels+2] = K;
        }
}

void CColorSegDlg::print_segment(int width, int height)
{
	
	
    for(int row=0; row<height; row++)
    {
        for(int col=0; col<width; col++)
        {
            //printf("%ld ", segment[width*row + col]);
			m_Test = segment[width*row + col];
			UpdateData(false);
        }
        //printf("\n");
    }
}
//

三 实现:

空间转换

2011051519235018.png

边缘检测

2011051519243588.png

提取种子点后区域生长结果

2011051519251043.png

Author:         SKySeraph

Email/GTalk: zgzhaobo@gmail.com    QQ:452728574

From:         http://www.cnblogs.com/skyseraph/

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,请尊重作者的劳动成果

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值