本章我们看下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到dst
if(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;
}
//图像边框像素值为0
if(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,则是孤立点,此时标记3
if(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 0
if((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

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

  1. OpenCV学习(16) 细化算法(4)

    本章我们学习Rosenfeld细化算法,参考资料:http://yunpan.cn/QGRjHbkLBzCrn 在开始学习算法之前,我们先看下连通分量,以及4连通性,8连通性的概念: http://w ...

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

    本章我们在学习一下基于索引表的细化算法. 假设要处理的图像为二值图,前景值为1,背景值为0. 索引表细化算法使用下面的8邻域表示法: 一个像素的8邻域,我们可以用8位二进制表示,比如下面的8邻域,表示 ...

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

          本章我们学习一下Hilditch算法的基本原理,从网上找资料的时候,竟然发现两个有很大差别的算法描述,而且都叫Hilditch算法.不知道那一个才是正宗的,两个算法实现的效果接近,第一种算 ...

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

          前面一篇教程中,我们实现了Zhang的快速并行细化算法,从算法原理上,我们可以知道,算法是基于像素8邻域的形状来决定是否删除当前像素.还有很多与此算法相似的细化算法,只是判断的条件不一样. ...

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

    程序编码参考经典的细化或者骨架算法文章: T. Y. Zhang and C. Y. Suen, "A fast parallel algorithm for thinning digita ...

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

    最后再来看一种通过形态学腐蚀和开操作得到骨架的方法.http://felix.abecassis.me/2011/09/opencv-morphological-skeleton/ 代码非常简单: v ...

  7. c++opencv中线条细化算法

    要达到的效果就是将线条尽量细化成单像素,按照论文上的Hilditch算法试了一下,发现效果不好,于是自己尝试着写了一下细化的算法,基本原理就是从上下左右四个方向向内收缩. 1.先是根据图片中的原则确定 ...

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

    本教程我学习一下opencv中分水岭算法的具体实现方式. 原始图像和Mark图像,它们的大小都是32*32,分水岭算法的结果是得到两个连通域的轮廓图. 原始图像:(原始图像必须是3通道图像) Mark ...

  9. OpenCV学习(21) Grabcut算法详解

    grab cut算法是graph cut算法的改进.在理解grab cut算之前,应该学习一下graph cut算法的概念及实现方式. 我搜集了一些graph cut资料:http://yunpan. ...

随机推荐

  1. 基于 Laravel 开发博客应用系列 —— 项目必备软件安装

    1.概述 通过本项目我们将会构建一个简单.清爽.优雅的博客系统,以及维护管理该博客的后台. 本项目源码公开在GitHub上:https://github.com/ChuckHeintzelman/l5 ...

  2. 使用chrales抓包IOS的https(pc+手机)

    1.安装SSL证书到手机 点击 Help -> SSL Proxying -> Install Charles Root Certificate on a Mobile Device 2. ...

  3. Windows10怎么架设局域网DNS服务器?

    已采纳 需要安装Windows组件进行设置.最好是安装服务器版本的Windows. 1. 安装DNS服务 开始—〉设置—〉控制面板—〉添加/删除程序—〉添加/删除Windows组件—〉“网络服务”—〉 ...

  4. python获取文件

    第一种:使用os.walk: # -*- coding: utf-8 -*- import os def Test1(rootDir): list_dirs = os.walk(rootDir) fo ...

  5. SKLearn数据集API(二)

    注:本文是人工智能研究网的学习笔记 计算机生成的数据集 用于分类任务和聚类任务,这些函数产生样本特征向量矩阵以及对应的类别标签集合. 数据集 简介 make_blobs 多类单标签数据集,为每个类分配 ...

  6. CentOS7.4 关闭firewall防火墙,改用iptables

    1.关闭默认的firewall防火墙 systemctl stop firewalld.service #停止firewall systemctl disable firewalld.service ...

  7. [BZOJ3507][CQOI2014]通配符匹配(DP+Hash)

    显然f[i][j]表示S匹配到第i个通配符,T匹配到第j个字符,是否可行. 一次一起转移两个通配符之间的所有字符,Hash判断. 稍微有点细节.常数极大卡时过排名倒数,可能是没自然溢出的原因. #in ...

  8. HTML5区块和大纲算法

    原文链接: Using HTML sections and outlines - Mozilla Developer Network 每集HTML5+CSS3网页布局教程-2大纲算法 HTML5标准带 ...

  9. Gunicorn配置部分的翻译

    写在前面,虽然翻译得很烂,但也是我的劳动成果,转载请注明出处,谢谢. Gunicorn版本号19.7.1 Gunicorn配置 概述 三种配置方式 优先级如下,越后的优先级越大 1.框架的设置(现在只 ...

  10. UVA 2474 - Balloons in a Box 爆搜

    2474 - Balloons in a Box 题目连接: https://icpcarchive.ecs.baylor.edu/index.php?option=com_onlinejudge&a ...