zoukankan      html  css  js  c++  java
  • 【图像算法】彩色图像分割专题三:边缘检测+区域生长 法

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

      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/

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

  • 相关阅读:
    linux下c语言实现搜索根目录下所有文件(转-wangxiangshang)
    windows编程之GDI基础--获取设备内容属性(三)
    windows编程之GDI基础--设备内容(二)
    windows编程之GDI基础(一)
    windows编程之滚动条(新式滚动条函数)
    window编程之滚动条(老式滚动条)
    来博客园混了...
    实现一个4位加减法与或运算选择器(作业)
    黑客初级知识(四)
    黑客初级知识(三)
  • 原文地址:https://www.cnblogs.com/skyseraph/p/2047049.html
Copyright © 2011-2022 走看看