【图像算法】彩色图像分割专题三:边缘检测+区域生长 法
【图像算法】彩色图像分割专题三:边缘检测+区域生长法
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
边缘检测:
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");
}
}
//////////////////////////////////////////////////////////////////////////
三 实现:
空间转换

边缘检测

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

Author: SKySeraph
Email/GTalk: zgzhaobo@gmail.com QQ:452728574
From: http://www.cnblogs.com/skyseraph/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,请尊重作者的劳动成果
作者:skyseraph
出处:http://www.cnblogs.com/skyseraph/
更多精彩请直接访问SkySeraph个人站点:http://skyseraph.com//
Email/GTalk: zgzhaobo@gmail.com
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。




浙公网安备 33010602011771号