OpenCV学习(17) 细化算法(5)

本章我们看下Pavlidis细化算法,参考资料http://www.imageprocessingplace.com/downloads_V3/root_downloads/tutorials/contour_tracing_Abeer_George_Ghuneim/theo.html

Computer VisiAlgorithms in Image Algebra,second edition

该算法最初是做前景轮廓跟踪的。

假设使用下面的8邻域,且前景像素值为1,背景像素值为0。

下面是该算法的描述:

1. 求出前景像素的轮廓,并用2表示,如果轮廓点是孤立点,端点或者其它不可删除的点,标记其为3。

2. 在第1步求出的轮廓中判断那些是可以删除的,那些是不可删除的,不可删除的点标记为4。

3. 再次扫描值为2的轮廓点,标记可以删除的点为5。

4. 对值为2和5的点执行删除操作。

重复上述步骤,直到图像中没有可以删除的像素为止。结果如想就是我们要的骨架结构。

      第一步是求轮廓的过程,对于一个值为1的像素点。如果它的p0,p2,p4,p6四个点都为1,则该点是内部点,继续循环,判断其它像素。

 如果该像素是孤立点或端点,则其像素值标记为3。

     如果像素是其它可能改变8连通性的点。比如以下的情况: p3, p7为0,但p4,p5,p6和p0,p1,p2中有非零值,如果删除p点,则连通性会改变。此时都标记当前像素值为3。

第一步后,我们会得到轮廓

第2步对不等于0的像素进行处理

如果像素周围全是2,则标记其为4(不删除)。

还有对于其它不可删除情况,比如下面这种情况,置当前像素为4。

第三步对于值为2的轮廓点再次进行判断,对于可删除的点,标记为5。

第四步删除值为2和5的点。

最终值为4的点为细化后的轮廓点。

算法实现的代码:

void gThin::cvPavlidis(cv::Mat& src, cv::Mat& dst)    {

if(src.type()!=CV_8UC1)        {        printf("只能处理二值或灰度图像\n");return;        }//非原地操作时候,copy src到dstif(dst.data!=src.data)        {        src.copyTo(dst);        }

char erase, n[8];unsigned char bdr1,bdr2,bdr4,bdr5;short k,b;unsigned long i,j;

int width, height;    width=dst.cols;    height= dst.rows;

//把不能于0的值转化为1,便于后面处理for(i=0; i< height; i++)        {for(j=0; j<width; j++)            {if(dst.at<uchar>(i,j)!=0)                {                dst.at<uchar>(i,j) = 1;                }//图像边框像素值为0if(i==0||i==(height-1)||j==0||j==(width-1))                dst.at<uchar>(i,j) = 0;            }        }

erase =1;    width = width - 1;    height = height - 1;    uchar* img;int step = dst.step;while(erase)         {

img = dst.data;//第一个循环,取得前景轮廓,轮廓用2表示for(i=1; i< height; i++)            {            img += step;for(j=1; j < width; j++)                {                uchar* p= img+j;

if(p[0]!= 1)continue;

n[0]=p[1];                n[1]=p[-step+1];                n[2]=p[-step];                n[3]=p[-step-1];                n[4]=p[-1];                n[5]=p[step-1];                n[6]=p[step];                n[7]=p[step+1];

//bdr1是2进制表示的p0...p6p7排列,10000011,p0=1,p6=p7=1                bdr1 =0;for(k=0; k<8; k++)                    {if(n[k]>=1)                        bdr1|=0x80>>k;                    }//内部点,p0, p2, p4, p6都是为1, 非边界点,所以继续循环//0xaa 10101010//  0   1   0   //  1         1//   0   1    0

if((bdr1&0xaa)== 0xaa)continue;//不是内部点,则是边界点,对于边界点,我们标记为2,是轮廓                p[0] = 2;

b=0;

for(k=0; k<=7; k++)                    {                    b+=bdr1&(0x80>>k);                    }//在边界点中,等于1,则是端点,等于0,则是孤立点,此时标记3if(b<=1 )                    p[0] = 3;

//此条件说明p点是中间点,如果移去会引起断裂// 0x70        0x7         0x88      0xc1        0x1c      0x22      0x82     0x1      0xa0     0x40     0x28    0x10       0xa      0x4// 0 0 0     0  1  1     1  0   0    0   0   0    1  1  0    0   0   1  0  0  1  0 0 0    0  0  0   0 0 0    1  0  0   0  0  0  1  0  1   0 1 0// 1    0     0      1     0       0    0        1    1      0    0        0  0      0  0    1    0      0   0    0    0      0   1      0  0     0    0    0// 1 1 0     0  0  0     0  0   1    0   1   1    0  0  0    1   0   1  0  0  1  0 0 0    1  0  1   0 1 0    1  0  0   0  0  0  0  0  0   0 0 0if((bdr1&0x70)!=0&&(bdr1&0x7)!=0&&(bdr1&0x88)==0)                    p[0] = 3;else if((bdr1&&0xc1)!=0&&(bdr1&0x1c)!=0&&(bdr1&0x22)==0)                    p[0] = 3;  else if((bdr1&0x82)==0 && (bdr1&0x1)!=0)                    p[0] = 3; else if((bdr1&0xa0)==0 && (bdr1&0x40)!=0)                    p[0] = 3; else if((bdr1&0x28)==0 && (bdr1&0x10)!=0)                    p[0] = 3; else if((bdr1&0xa)==0 && (bdr1&0x4)!=0)                    p[0] = 3; 

}            }//printf("------------------------------\n");//PrintMat(dst);        img = dst.data;for(i=1; i<height; i++)            {            img += step;for(j=1; j<width; j++)                {                uchar* p= img+j;

if(p[0]== 0)continue;

n[0]=p[1];                n[1]=p[-step+1];                n[2]=p[-step];                n[3]=p[-step-1];                n[4]=p[-1];                n[5]=p[step-1];                n[6]=p[step];                n[7]=p[step+1];

bdr1 = bdr2 =0;

//bdr1是2进制表示的当前点p的8邻域连通情况,hdr2是当前点周围轮廓点的连接情况for(k=0; k<=7; k++)                    {if(n[k]>=1)                        bdr1|=0x80>>k;if(n[k]>=2)                        bdr2|=0x80>>k;                    }

//相等,就是周围全是值为2的像素,继续if(bdr1==bdr2)                    {                    p[0] = 4; continue;                    }

//p0不为2,继续if(p[0]!=2) continue;//=4都是不可删除的轮廓点//     0x80       0xa     0x40        0x1      0x30   0x6//   0 0 0      1  0 1    0  0  0    0  0  0   0 0 0   0 1 1//   0    0      0     0    0      0    0      1   1    0   0    0//   0 0 1      0  0 0    0  1  0    0  0  0   1 0 0   0 0 0

if(                       (bdr2&0x80)!=0 && (bdr1&0xa)==0 &&//    ((bdr1&0x40)!=0 &&(bdr1&0x1)!=0 ||     ((bdr1&0x40)!=0 ||(bdr1 & 0x1)!=0) &&(bdr1&0x30)!=0 &&(bdr1&0x6)!=0 )                    (    ((bdr1&0x40)!=0 ||(bdr1 & 0x1)!=0) &&(bdr1&0x30)!=0 &&(bdr1&0x6)!=0 )                    )                    {                    p[0]= 4;                     }//else if((bdr2&0x20)!=0 && (bdr1&0x2)==0 &&//((bdr1&0x10)!=0 && (bdr1&0x40)!=0 || ((bdr1&0x10)!=0 || (bdr1&0x40)!=0) &&    (bdr1&0xc)!=0 && (bdr1&0x81)!=0)                    ( ((bdr1&0x10)!=0 || (bdr1&0x40)!=0) &&    (bdr1&0xc)!=0 && (bdr1&0x81)!=0)                    )                    {                    p[0]= 4;                    }

else if((bdr2&0x8)!=0 && (bdr1&0x80)==0 &&//((bdr1&0x4)!=0 && (bdr1&0x10)!=0 || ((bdr1&0x4)!=0 || (bdr1&0x10)!=0) &&(bdr1&0x3)!=0 && (bdr1&0x60)!=0)                    ( ((bdr1&0x4)!=0 || (bdr1&0x10)!=0) &&(bdr1&0x3)!=0 && (bdr1&0x60)!=0)                    )                    {                    p[0]= 4;                    }

else if((bdr2&0x2)!=0 && (bdr1&0x20)==0 &&//((bdr1&0x1)!=0 && (bdr1&0x4)!=0 ||((bdr1&0x1)!=0 || (bdr1&0x4)!=0) &&(bdr1&0xc0)!=0 && (bdr1&0x18)!=0)                    (((bdr1&0x1)!=0 || (bdr1&0x4)!=0) &&(bdr1&0xc0)!=0 && (bdr1&0x18)!=0)                    )                    {                    p[0]= 4;                    }                }            }//printf("------------------------------\n");//PrintMat(dst);        img = dst.data;for(i=1; i<height; i++)            {            img += step;for(j=1; j<width; j++)                {                uchar* p= img+j;

if(p[0]!= 2)continue;

n[0]=p[1];                n[1]=p[-step+1];                n[2]=p[-step];                n[3]=p[-step-1];                n[4]=p[-1];                n[5]=p[step-1];                n[6]=p[step];                n[7]=p[step+1];

bdr4 = bdr5 =0;for(k=0; k<=7; k++)                    {if(n[k]>=4)                        bdr4|=0x80>>k;if(n[k]>=5)                        bdr5|=0x80>>k;                    }//值为4和5的像素if((bdr4&0x8) == 0)                    {                    p[0]=5;continue;                    }if((bdr4&0x20) == 0 && bdr5 ==0)                    {                    p[0]=5;continue;                    }

}            }        erase = 0;//printf("------------------------------\n");//PrintMat(dst);        img = dst.data;for(i=1; i<height; i++)            {            img += step;for(j=1; j<width; j++)                {                uchar* p= img+j;if(p[0]==2||p[0]==5)                    {                    erase = 1;                    p[0] = 0;                    }                }            }//printf("------------------------------\n");//PrintMat(dst);//printf("------------------------\n");        }

}

 

程序源代码:参加工程FirstOpenCV11

时间: 2024-08-22 15:07:02

OpenCV学习(17) 细化算法(5)的相关文章

OpenCV学习(18) 细化算法(6)

本章我们在学习一下基于索引表的细化算法. 假设要处理的图像为二值图,前景值为1,背景值为0. 索引表细化算法使用下面的8邻域表示法: 一个像素的8邻域,我们可以用8位二进制表示,比如下面的8邻域,表示为00111000=0x38=56 我们可以枚举出各种情况下,当前像素能否删除的表,该表大小为256.它的索引即为8邻域表示的值,表中存的值为0或1,0表示当前像素不能删除,1表示可以删除.deletemark[256] 比如下图第一个表示,索引值为0,它表示孤立点,不能删除,所以deletemar

OpenCV学习(15) 细化算法(3)

      本章我们学习一下Hilditch算法的基本原理,从网上找资料的时候,竟然发现两个有很大差别的算法描述,而且都叫Hilditch算法.不知道那一个才是正宗的,两个算法实现的效果接近,第一种算法更好一些. 第一种算法描述参考paper和代码: Linear Skeletons from Square Cupboards Speedup Method for Real-Time Thinning Algorithm http://cis.k.hosei.ac.jp/~wakahara/Hi

OpenCV学习(14) 细化算法(2)

      前面一篇教程中,我们实现了Zhang的快速并行细化算法,从算法原理上,我们可以知道,算法是基于像素8邻域的形状来决定是否删除当前像素.还有很多与此算法相似的细化算法,只是判断的条件不一样.在综述文章, Thinning Methodologies-A Comprehensive Survey中描述了各种细化算法的实现原理,有兴趣可以阅读一下.       下面看看图像细化的定义以及细化算法的分类: 图像细化(Image Thinning),一般指二值图像的骨架化(Image Skel

OpenCV学习(13) 细化算法(1)

程序编码参考经典的细化或者骨架算法文章: T. Y. Zhang and C. Y. Suen, "A fast parallel algorithm for thinning digital patterns," Comm. ACM, vol. 27, no. 3, pp. 236-239, 1984. 它的原理也很简单:       我们对一副二值图像进行骨架提取,就是删除不需要的轮廓点,只保留其骨架点.假设一个像素点,我们定义该点为p1,则它的八邻域点p2->p9位置如下图

OpenCV学习(19) 细化算法(7)

最后再来看一种通过形态学腐蚀和开操作得到骨架的方法.http://felix.abecassis.me/2011/09/opencv-morphological-skeleton/ 代码非常简单: void gThin::cvmorphThin(cv::Mat& src, cv::Mat& dst)     {     if(src.type()!=CV_8UC1)         {         printf("只能处理二值或灰度图像\n");         r

OpenCV学习(9) 分水岭算法(3)

本教程我学习一下opencv中分水岭算法的具体实现方式. 原始图像和Mark图像,它们的大小都是32*32,分水岭算法的结果是得到两个连通域的轮廓图. 原始图像:(原始图像必须是3通道图像) Mark图像: 结果图像:       初始的mark图像数据如下,黄色的部分为我们的第一个mark区域,值为255,第二个区域为褐红色的区域,值为128,第三个绿色的区域,值为64.   opencv分水岭算法描述如下: 初始化mark矩阵,生成最初的注水区域. 1.设置mark图像的边框值为-1 2.

OpenCV学习(8) 分水岭算法(2)

    现在我们看看OpenCV中如何使用分水岭算法.     首先我们打开一副图像:    // 打开另一幅图像   cv::Mat    image= cv::imread("../tower.jpg");     if (!image.data)         {         cout<<"不能打开图像!"<<endl;         return 0;         }      接下来,我们要创建mark图像.mark图像

OpenCV学习(21) Grabcut算法详解

grab cut算法是graph cut算法的改进.在理解grab cut算之前,应该学习一下graph cut算法的概念及实现方式. 我搜集了一些graph cut资料:http://yunpan.cn/QGDVdBXwkXutH      grab cut算法详细描述见资料中的pdf文件:"GrabCut" - Interactive Foreground Extraction using Iterated Graph Cuts      grab cut算法是一种基于图论的图像分

(转) OpenCV学习笔记大集锦 与 图像视觉博客资源2之MIT斯坦福CMU

      首页 视界智尚 算法技术 每日技术 来打我呀 注册     OpenCV学习笔记大集锦 整理了我所了解的有关OpenCV的学习笔记.原理分析.使用例程等相关的博文.排序不分先后,随机整理的.如果有好的资源,也欢迎介绍和分享. 1:OpenCV学习笔记 作者:CSDN数量:55篇博文网址:http://blog.csdn.net/column/details/opencv-manual.html 2:部分OpenCV的函数解读和原理解读 作者:梦想腾飞数量:20篇博文网址:http:/