//Mat 类型 骨架算法 查索引表法
//20141009
//opencv 2.4.9
vs2012
Mat 类型图像
原图
细化结果
示例代码: 原理及 iplimage 类型实现详见后文
//xx.h
using namespace std;
using namespace cv;
//std::string
Mat VThin( Mat &tmp, int array[256] );
Mat HThin( Mat &XihuaTmp, int array[256] );
//XX.cpp
int array[256] = {0,0,1,1,0,0,1,1,1,1,0,1,1,1,0,1,
1,1,0,0,1,1,1,1,0,0,0,0,0,0,0,1,
0,0,1,1,0,0,1,1,1,1,0,1,1,1,0,1,
1,1,0,0,1,1,1,1,0,0,0,0,0,0,0,1,
1,1,0,0,1,1,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
1,1,0,0,1,1,0,0,1,1,0,1,1,1,0,1,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,1,1,0,0,1,1,1,1,0,1,1,1,0,1,
1,1,0,0,1,1,1,1,0,0,0,0,0,0,0,1,
0,0,1,1,0,0,1,1,1,1,0,1,1,1,0,1,
1,1,0,0,1,1,1,1,0,0,0,0,0,0,0,0,
1,1,0,0,1,1,0,0,0,0,0,0,0,0,0,0,
1,1,0,0,1,1,1,1,0,0,0,0,0,0,0,0,
1,1,0,0,1,1,0,0,1,1,0,1,1,1,0,0,
1,1,0,0,1,1,1,0,1,1,0,0,1,0,0,0};
**********************
//细化
Mat iXihua(gray_img.size(),CV_8UC1);
Mat XihuaTmp(gray_img.size(),CV_8UC1);
//20141009 索引表法
//取反
for(int h=0;h(h,w)==0)
Close_morphologyEx_img.at(h,w)=255;
else
Close_morphologyEx_img.at(h,w)=0;
{
}
}
//注意这里取反为什么不是 0-1,而是 0<—>255 呢?因为 opencv 里面的图像深度是 8 位的,并不仅仅是//0,1
两位
//
for (int i = 0;i<20;i++)
//阈值 20 是可变的,根据每个区域的大小改变,若最大区域
较大,则将阈值增大,反之变小,可通过测试看出效果
{
}
XihuaTmp = VThin(Close_morphologyEx_img,array);
iXihua = HThin(XihuaTmp,array);
//水平细化
//垂直细化
imwrite("音孔检测\\08-1 音孔_dTF1_xihua1.bmp",iXihua);
*************************
Mat Cglass_locateDlg::VThin( Mat &tmp, int array[256] ) //细化主程序调用垂直细化
{
int NEXT = 1;
int M =0;
int a[9];
int sum;
int width = tmp.size().width;//int width = Close_morphologyEx_img.cols
int height = tmp.size().height;////int width = Close_morphologyEx_img.rows
for (int h = 0;h0 &&wimageData + tmp->widthStep*(h)))[w-1]+((uchar*)(tmp->imageData +
tmp->widthStep*(h)))[w]+((uchar*)(tmp->imageData + tmp->widthStep*(h)))[w+1];
M=tmp.at(h,w-1)+tmp.at(h,w)+tmp.at(h,w+1);
}
else
{
M = 1;
}
if((tmp.at(h,w)) == 0 && M!=0)
{
a[0] = a[1] = a[2] = a[3] = a[4] = a[5] = a[6] = a[7] = a[8] = 0;
for(int i = 0;i<3;i++)
{
for(int j = 0;j<3;j++)
{
if((h-1+i) > -1 &&(h-1+i) -1&&(w-1+j)(h-1+i,w-1+j))==255)
{
a[i*3+j] = 1;
}
}
}
}
}
sum = a[0]*1+a[1]*2+a[2]*4+a[3]*8+a[5]*16+a[6]*32+a[7]*64+a[8]*128;
//(uchar*)(tmp->imageData + tmp->widthStep*h))[w] = array[sum]*255;
tmp.at(h,w)=array[sum]*255;
if(array[sum]==1)
{
NEXT = 0;
}
}
}
}
}
return tmp;
}
Mat Cglass_locateDlg::HThin( Mat &XihuaTmp, int array[256] ) //细化主程序调用水平细化
{
int NEXT = 1;
int M =0;
int A[9];
int sum;
int width = XihuaTmp.size().width;//int width = Close_morphologyEx_img.cols
int height = XihuaTmp.size().height;////int width = Close_morphologyEx_img.rows
for (int w = 0;w0 &&himageData +
XihuaTmp->widthStep*(h-1)))[w]+((uchar*)(XihuaTmp->imageData +
XihuaTmp->widthStep*(h)))[w]+((uchar*)(XihuaTmp->imageData + XihuaTmp->widthStep*(h+1)))[w];
M=XihuaTmp.at(h-1,w)+XihuaTmp.at(h,w)+XihuaTmp.at(h+1,w);
}
else
{
M = 1;
}
//if(((uchar*)(XihuaTmp->imageData + XihuaTmp->widthStep*(h)))[w] == 0 && M!=0)
if(XihuaTmp.at(h,w)==0 && M!=0)
{
A[0] = A[1] = A[2] = A[3] = A[4] = A[5] = A[6] = A[7] = A[8] = 0;
for(int i = 0;i<3;i++)
{
for(int j = 0;j<3;j++)
{
if((h-1+i) > -1 &&(h-1+i) -1&&(w-1+j)imageData +
XihuaTmp->widthStep*(h-1+i)))[w-1+j] == 255)
if(XihuaTmp.at(h-1+i,w-1+j)==255)
{
}
A[i*3+j] = 1;
}
}
}
}
sum = A[0]*1+A[1]*2+A[2]*4+A[3]*8+A[5]*16+A[6]*32+A[7]*64+A[8]*128;
//((uchar*)(XihuaTmp->imageData + XihuaTmp->widthStep*h))[w] = array[sum]*255;
XihuaTmp.at(h,w)=array[sum]*255;
if(array[sum]==1)
{
NEXT = 0;
}
}
}
}
}
return XihuaTmp;
}
原理
像的细化主要是针对二值图而言
所谓骨架,可以理解为图像的中轴,,一个长方形的骨架,是它的长方向上的中轴线,
圆的骨架是它的圆心,直线的骨架是它自身,孤立点的骨架也是自身。
骨架的获取主要有两种方法:
(1)基于烈火模拟
设想在同一时刻,将目标的边缘线都点燃,火的前沿以匀速向内部蔓延,当前沿相交时火焰熄灭,
火焰熄灭点的结合就是骨架。
(2)基于最大圆盘
目标的骨架是由目标内所有内切圆盘的圆心组成
我们来看看典型的图形的骨架(用粗线表示)
细化的算法有很多种,但比较常用的算法是查表法
细化是从原来的图中去掉一些点,但仍要保持原来的形状。
实际上是保持原图的骨架。
判断一个点是否能去掉是以 8 个相邻点(八连通)的情况来作为判据的,具体判据为:
1,内部点不能删除
2,鼓励点不能删除
3,直线端点不能删除
4,如果 P 是边界点,去掉 P 后,如果连通分量不增加,则 P 可删除
看看上面那些点(中心点,八领域)。
第一幅图中心点不能去除,因为它是内部点
第二个点不能去除,它也是内部点
第三个点不能去除,删除后会使原来相连的部分断开
第四个点可以去除,这个点不是骨架
第五个点不可以去除,它是直线的端点
第六个点不可以去除,它是直线的端点
对于所有的这样的点,我们可以做出一张表,来判断这样的点能不能删除
我们对于黑色的像素点,对于它周围的 8 个点,我们赋予不同的价值,
若周围某黑色,我们认为其价值为 0,为白色则取九宫格中对应的价值
对于前面那幅图中第一个点,也就是内部点,它周围的点都是黑色,所以他的总价值是 0,对应
于索引表的第一项
前面那幅图中第二点,它周围有三个白色点,它的总价值为 1+4+32=37,对应于索引表中第
三十八项
我们用这种方法,把所有点的情况映射到 0~255 的索引表中
我们扫描原图,对于黑色的像素点,根据周围八点的情况计算它的价值,然后查看索引表中对应
项来决定是否要保留这一点
我们很容易写出程序
import cv
def Thin(image,array):
h = image.height
w = image.width
iThin = cv.CreateImage(cv.GetSize(image),8,1)
cv.Copy(image,iThin)
for i in range(h):
for j in range(w):
if image[i,j] == 0:
a = [1]*9
for k in range(3):
for l in range(3):
if -1<(i-1+k)