图像细化
总的来说,图像细化有不少方法,但是,都有所不足。主要体现在,字符细化后产生多余的毛刺,细化结构出现误差,而且大部分都是对垂直水平方向细化效果好,而对曲线等效果不理想。
(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
前辈们对此作出了总结,得出每个点周围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 P4
- while(bStart)
- {
- bStart = false;
- //首先求边缘点
- pmid = (unsigned char *)lpDIBBits + lWidth + 1;
- memset(pTemp, 0, lLength);
- pmidtemp = (unsigned char *)pTemp + lWidth + 1; // 如果是边缘点 则将其设为1
- for(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;
- }
转载自:http://blog.csdn.net/lu597203933/article/details/14397605
参考论文 A fast parallel algorithm for thinning digital patterns
- #include <opencv2/opencv.hpp>
- #include <iostream>
- #include <vector>
- #include <limits>
- using namespace cv;
- using namespace std;
- /**
- * @brief 对输入图像进行细化
- * @param[in] src为输入图像,用cvThreshold函数处理过的8位灰度图像格式,元素中只有0与1,1代表有元素,0代表为空白
- * @param[out] dst为对src细化后的输出图像,格式与src格式相同,调用前需要分配空间,元素中只有0与1,1代表有元素,0代表为空白
- * @param[in] maxIterations限制迭代次数,如果不进行限制,默认为-1,代表不限制迭代次数,直到获得最终结果
- */
- void thinImage(IplImage* src,IplImage* dst,int maxIterations = -1 )
- {
- CvSize size = cvGetSize(src);
- cvCopy(src,dst);//将src中的内容拷贝到dst中
- int count = 0; //记录迭代次数
- while (true)
- {
- count++;
- if(maxIterations!=-1 && count > maxIterations) //限制次数并且迭代次数到达
- break;
- //std::cout << count << ' ';输出迭代次数
- vector<pair<int,int> > mFlag; //用于标记需要删除的点
- //对点标记
- for (int i=0; i<size.height; ++i)
- {
- for (int j=0; j<size.width; ++j)
- {
- //如果满足四个条件,进行标记
- // p9 p2 p3
- // p8 p1 p4
- // p7 p6 p5
- int p1 = CV_IMAGE_ELEM(dst,uchar,i,j);
- int p2 = (i==0)?0:CV_IMAGE_ELEM(dst,uchar,i-1,j);
- int p3 = (i==0 || j==size.width-1)?0:CV_IMAGE_ELEM(dst,uchar,i-1,j+1);
- int p4 = (j==size.width-1)?0:CV_IMAGE_ELEM(dst,uchar,i,j+1);
- int p5 = (i==size.height-1 || j==size.width-1)?0:CV_IMAGE_ELEM(dst,uchar,i+1,j+1);
- int p6 = (i==size.height-1)?0:CV_IMAGE_ELEM(dst,uchar,i+1,j);
- int p7 = (i==size.height-1 || j==0)?0:CV_IMAGE_ELEM(dst,uchar,i+1,j-1);
- int p8 = (j==0)?0:CV_IMAGE_ELEM(dst,uchar,i,j-1);
- int p9 = (i==0 || j==0)?0:CV_IMAGE_ELEM(dst,uchar,i-1,j-1);
- if ((p2+p3+p4+p5+p6+p7+p8+p9)>=2 && (p2+p3+p4+p5+p6+p7+p8+p9)<=6)
- {
- int ap=0;
- if (p2==0 && p3==1) ++ap;
- if (p3==0 && p4==1) ++ap;
- if (p4==0 && p5==1) ++ap;
- if (p5==0 && p6==1) ++ap;
- if (p6==0 && p7==1) ++ap;
- if (p7==0 && p8==1) ++ap;
- if (p8==0 && p9==1) ++ap;
- if (p9==0 && p2==1) ++ap;
- if (ap==1)
- {
- if (p2*p4*p6==0)
- {
- if (p4*p6*p8==0)
- {
- //标记
- mFlag.push_back(make_pair(i,j));
- }
- }
- }
- }
- }
- }
- //将标记的点删除
- for (vector<pair<int,int> >::iterator i=mFlag.begin(); i!=mFlag.end(); ++i)
- {
- CV_IMAGE_ELEM(dst,uchar,i->first,i->second) = 0;
- }
- //直到没有点满足,算法结束
- if (mFlag.size()==0)
- {
- break;
- }
- else
- {
- mFlag.clear();//将mFlag清空
- }
- //对点标记
- for (int i=0; i<size.height; ++i)
- {
- for (int j=0; j<size.width; ++j)
- {
- //如果满足四个条件,进行标记
- // p9 p2 p3
- // p8 p1 p4
- // p7 p6 p5
- int p1 = CV_IMAGE_ELEM(dst,uchar,i,j);
- if(p1!=1) continue;
- int p2 = (i==0)?0:CV_IMAGE_ELEM(dst,uchar,i-1,j);
- int p3 = (i==0 || j==size.width-1)?0:CV_IMAGE_ELEM(dst,uchar,i-1,j+1);
- int p4 = (j==size.width-1)?0:CV_IMAGE_ELEM(dst,uchar,i,j+1);
- int p5 = (i==size.height-1 || j==size.width-1)?0:CV_IMAGE_ELEM(dst,uchar,i+1,j+1);
- int p6 = (i==size.height-1)?0:CV_IMAGE_ELEM(dst,uchar,i+1,j);
- int p7 = (i==size.height-1 || j==0)?0:CV_IMAGE_ELEM(dst,uchar,i+1,j-1);
- int p8 = (j==0)?0:CV_IMAGE_ELEM(dst,uchar,i,j-1);
- int p9 = (i==0 || j==0)?0:CV_IMAGE_ELEM(dst,uchar,i-1,j-1);
- if ((p2+p3+p4+p5+p6+p7+p8+p9)>=2 && (p2+p3+p4+p5+p6+p7+p8+p9)<=6)
- {
- int ap=0;
- if (p2==0 && p3==1) ++ap;
- if (p3==0 && p4==1) ++ap;
- if (p4==0 && p5==1) ++ap;
- if (p5==0 && p6==1) ++ap;
- if (p6==0 && p7==1) ++ap;
- if (p7==0 && p8==1) ++ap;
- if (p8==0 && p9==1) ++ap;
- if (p9==0 && p2==1) ++ap;
- if (ap==1)
- {
- if (p2*p4*p8==0)
- {
- if (p2*p6*p8==0)
- {
- //标记
- mFlag.push_back(make_pair(i,j));
- }
- }
- }
- }
- }
- }
- //删除
- for (vector<pair<int,int> >::iterator i=mFlag.begin(); i!=mFlag.end(); ++i)
- {
- CV_IMAGE_ELEM(dst,uchar,i->first,i->second) = 0;
- }
- //直到没有点满足,算法结束
- if (mFlag.size()==0)
- {
- break;
- }
- else
- {
- mFlag.clear();//将mFlag清空
- }
- }
- }
- int main(int argc, char*argv[])
- {
- //获取图像
- if (argc!=2)
- {
- cout << "参数个数错误!"<<endl;
- return -1;
- }
- IplImage *pSrc = cvLoadImage(argv[1],CV_LOAD_IMAGE_GRAYSCALE);
- if (!pSrc)
- {
- cout << "读取文件失败!" << endl;
- return -1;
- }
- IplImage *pTemp = cvCreateImage(cvGetSize(pSrc),pSrc->depth,pSrc->nChannels);
- IplImage *pDst = cvCreateImage(cvGetSize(pSrc),pSrc->depth,pSrc->nChannels);
- //将原图像转换为二值图像
- cvThreshold(pSrc,pTemp,128,1,CV_THRESH_BINARY);
- //图像细化
- thinImage(pTemp,pDst);
- for (int i=0; i<pDst->height; ++i)
- {
- for (int j=0; j<pDst->width; ++j)
- {
- if(CV_IMAGE_ELEM(pDst,uchar,i,j)==1)
- CV_IMAGE_ELEM(pDst,uchar,i,j)= 255;
- }
- }
- namedWindow("src",CV_WINDOW_AUTOSIZE);
- namedWindow("dst",CV_WINDOW_AUTOSIZE);
- cvShowImage("src",pSrc);
- cvShowImage("dst",pDst);
- waitKey(0);
- }
转载自:http://blog.csdn.net/qianchenglenger/article/details/19332011