图像细化针对的是二值图像 或者用阀值处理的二值图像。基于索引表的细化算法大致是遍历被二值化图像的边缘,根据边缘点的八连通域情况查找索引表以确定该边缘点是否能够被删除。根据一些细化规则我们可以建立索引表,因此我们的主要工作就是不断地遍历边缘进行是否删除判断,直至边缘的每一个点都不能再被细化(删除)。
(1):例子 左边为输入图像 右边为细化的效果图
(2)思想:
公式: y = p0*2^0 + p1*2^1+ p2*2^2 + p3*2^3 + p4*2^4 + p5*2^5 + p6*2^6 +p7*2^7
这八个点的取值范围为{0,1} (1表示255)
对于这八个点的不同取值情况,按如下顺序表示的二进制的值也将有不同的取值
P7 | P6 | P5 | P4 | P3 | P2 | P1 | P0 | Sum | Delete Enable |
|
|
|
|
|
|
|
|
|
|
其中Sum的取值从0~255,Delete Enable为1表示可以删除,为0表示不能删除,这样我们就建立了一个索引表deletemark[256]
前辈们对此作出了总结,得出每个点周围8领域的256种情况,放在一个char data[256]的数组中,不可以删除用0来表示,能被删除的用1来表示。然后对图像进行处理得到二值图像<0和1>,扫描图像,根据公式得出y,依次用data[y]判断该点是否可以被删除,直到所有的点都不可以被删除为止。
(3)算法步骤:
(4)代码:
#include <stdlib.h>
#include <string.h>#include "cv.h"
#include "highgui.h"
#include "cxcore.h"//基于索引表的细化细化算法
//功能:对图象进行细化
//参数:lpDIBBits:代表图象的一维数组
// lWidth:图象高度
// lHeight:图象宽度
// 无返回值
bool ThiningDIBSkeleton (unsigned char* lpDIBBits, int lWidth, int lHeight)
{ //循环变量long i;long j;long lLength;unsigned char deletemark[256] = { // 这个即为前人据8领域总结的是否可以被删除的256种情况0,0,0,0,0,0,0,1, 0,0,1,1,0,0,1,1,0,0,0,0,0,0,0,0, 0,0,1,1,1,0,1,1,0,0,0,0,0,0,0,0, 1,0,0,0,1,0,1,1,0,0,0,0,0,0,0,0, 1,0,1,1,1,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,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 1,0,0,0,1,0,1,1,1,0,0,0,0,0,0,0, 1,0,1,1,1,0,1,1,0,0,1,1,0,0,1,1, 0,0,0,1,0,0,1,1,0,0,0,0,0,0,0,0, 0,0,0,1,0,0,1,1,1,1,0,1,0,0,0,1, 0,0,0,0,0,0,0,0,1,1,0,1,0,0,0,1, 1,1,0,0,1,0,0,0,0,1,1,1,0,0,1,1, 0,0,0,1,0,0,1,1,0,0,0,0,0,0,0,0, 0,0,0,0,0,1,1,1,1,1,1,1,0,0,1,1, 1,1,0,0,1,1,0,0,1,1,1,1,0,0,1,1, 1,1,0,0,1,1,0,0};//索引表unsigned char p0, p1, p2, p3, p4, p5, p6, p7;unsigned char *pmid, *pmidtemp; // pmid 用来指向二值图像 pmidtemp用来指向存放是否为边缘unsigned char sum;bool bStart = true;lLength = lWidth * lHeight;unsigned char *pTemp = new uchar[sizeof(unsigned char) * lWidth * lHeight](); //动态创建数组 并且初始化// P0 P1 P2// P7 P3// P6 P5 P4while(bStart){bStart = false;//首先求边缘点pmid = (unsigned char *)lpDIBBits + lWidth + 1;memset(pTemp, 0, lLength);pmidtemp = (unsigned char *)pTemp + lWidth + 1; // 如果是边缘点 则将其设为1for(i = 1; i < lHeight -1; i++) {for(j = 1; j < lWidth - 1; j++){if( *pmid == 0) //是0 不是我们需要考虑的点{pmid++;pmidtemp++;continue;}p3 = *(pmid + 1);p2 = *(pmid + 1 - lWidth);p1 = *(pmid - lWidth);p0 = *(pmid - lWidth -1);p7 = *(pmid - 1);p6 = *(pmid + lWidth - 1);p5 = *(pmid + lWidth);p4 = *(pmid + lWidth + 1); sum = p0 & p1 & p2 & p3 & p4 & p5 & p6 & p7;if(sum == 0){*pmidtemp = 1; // 这样周围8个都是1的时候 pmidtemp==1 表明是边缘 }pmid++;pmidtemp++;}pmid++;pmid++;pmidtemp++;pmidtemp++;}//现在开始删除pmid = (unsigned char *)lpDIBBits + lWidth + 1;pmidtemp = (unsigned char *)pTemp + lWidth + 1;for(i = 1; i < lHeight -1; i++) // 不考虑图像第一行 第一列 最后一行 最后一列{for(j = 1; j < lWidth - 1; j++){if( *pmidtemp == 0) //1表明是边缘 0--周围8个都是1 即为中间点暂不予考虑{pmid++;pmidtemp++;continue;}p3 = *(pmid + 1);p2 = *(pmid + 1 - lWidth);p1 = *(pmid - lWidth);p0 = *(pmid - lWidth -1);p7 = *(pmid - 1);p6 = *(pmid + lWidth - 1);p5 = *(pmid + lWidth);p4 = *(pmid + lWidth + 1);p1 *= 2;p2 *= 4;p3 *= 8;p4 *= 16;p5 *= 32;p6 *= 64;p7 *= 128;sum = p0 | p1 | p2 | p3 | p4 | p5 | p6 | p7;// sum = p0 + p1 + p2 + p3 + p4 + p5 + p6 + p7;if(deletemark[sum] == 1){*pmid = 0;bStart = true; // 表明本次扫描进行了细化}pmid++;pmidtemp++;}pmid++;pmid++;pmidtemp++;pmidtemp++;}}delete []pTemp;return true;
}int main(int argc, char* argv[])
{IplImage* src = cvLoadImage("E:\\study_opencv_video\\testthin\\char2.png",0);cvThreshold(src,src,100,255,CV_THRESH_BINARY);unsigned char* imagedata ;cvNamedWindow("s",0);cvShowImage("s" , src);imagedata = new uchar[sizeof(char)*src->width*src->height]();int x , y;for(y=0;y<src->height;y++){unsigned char* ptr = (unsigned char*)(src->imageData + y*src->widthStep);for(x=0;x<src->width;x++){imagedata[y*src->width+x] = ptr[x] > 0 ? 1 : 0;}}ThiningDIBSkeleton(imagedata,src->width,src->height);for(y=0;y<src->height;y++){unsigned char* ptr = (unsigned char*)(src->imageData + y*src->widthStep);for(x=0;x<src->width;x++){ptr[x] = imagedata[y*src->width + x]>0? 255 : 0;}}cvNamedWindow("src",0);cvShowImage("src" , src);cvWaitKey(0);delete []imagedata;return 0;
}
//
转载:https://blog.csdn.net/lu597203933/article/details/14397605