图像细化

图像细化


总的来说,图像细化有不少方法,但是,都有所不足。主要体现在,字符细化后产生多余的毛刺,细化结构出现误差,而且大部分都是对垂直水平方向细化效果好,而对曲线等效果不理想。


(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)代码:

  1. #include <stdlib.h>  
  2. #include <string.h>  
  3.   
  4. #include "cv.h"  
  5. #include "highgui.h"  
  6. #include "cxcore.h"  
  7.   
  8. //基于索引表的细化细化算法  
  9. //功能:对图象进行细化  
  10. //参数:lpDIBBits:代表图象的一维数组  
  11. //      lWidth:图象高度  
  12. //      lHeight:图象宽度  
  13. //      无返回值  
  14. bool ThiningDIBSkeleton (unsigned char* lpDIBBits, int lWidth, int lHeight)  
  15. {     
  16.     //循环变量  
  17.     long i;  
  18.     long j;  
  19.     long lLength;  
  20.   
  21.     unsigned char deletemark[256] = {      // 这个即为前人据8领域总结的是否可以被删除的256种情况  
  22.         0,0,0,0,0,0,0,1,    0,0,1,1,0,0,1,1,  
  23.         0,0,0,0,0,0,0,0,    0,0,1,1,1,0,1,1,  
  24.         0,0,0,0,0,0,0,0,    1,0,0,0,1,0,1,1,  
  25.         0,0,0,0,0,0,0,0,    1,0,1,1,1,0,1,1,  
  26.         0,0,0,0,0,0,0,0,    0,0,0,0,0,0,0,0,  
  27.         0,0,0,0,0,0,0,0,    0,0,0,0,0,0,0,0,  
  28.         0,0,0,0,0,0,0,0,    1,0,0,0,1,0,1,1,  
  29.         1,0,0,0,0,0,0,0,    1,0,1,1,1,0,1,1,  
  30.         0,0,1,1,0,0,1,1,    0,0,0,1,0,0,1,1,  
  31.         0,0,0,0,0,0,0,0,    0,0,0,1,0,0,1,1,  
  32.         1,1,0,1,0,0,0,1,    0,0,0,0,0,0,0,0,  
  33.         1,1,0,1,0,0,0,1,    1,1,0,0,1,0,0,0,  
  34.         0,1,1,1,0,0,1,1,    0,0,0,1,0,0,1,1,  
  35.         0,0,0,0,0,0,0,0,    0,0,0,0,0,1,1,1,  
  36.         1,1,1,1,0,0,1,1,    1,1,0,0,1,1,0,0,  
  37.         1,1,1,1,0,0,1,1,    1,1,0,0,1,1,0,0  
  38.     };//索引表  
  39.   
  40.     unsigned char p0, p1, p2, p3, p4, p5, p6, p7;  
  41.     unsigned char *pmid, *pmidtemp;    // pmid 用来指向二值图像  pmidtemp用来指向存放是否为边缘  
  42.     unsigned char sum;  
  43.     bool bStart = true;  
  44.     lLength = lWidth * lHeight;  
  45.     unsigned char *pTemp = new uchar[sizeof(unsigned char) * lWidth * lHeight]();  //动态创建数组 并且初始化  
  46.       
  47.     //    P0 P1 P2  
  48.     //    P7    P3  
  49.     //    P6 P5 P4  
  50.   
  51.     while(bStart)  
  52.     {  
  53.         bStart = false;  
  54.   
  55.         //首先求边缘点  
  56.         pmid = (unsigned char *)lpDIBBits + lWidth + 1;  
  57.         memset(pTemp,  0, lLength);  
  58.         pmidtemp = (unsigned char *)pTemp + lWidth + 1; //  如果是边缘点 则将其设为1  
  59.         for(i = 1; i < lHeight -1; i++)       
  60.         {  
  61.             for(j = 1; j < lWidth - 1; j++)  
  62.             {  
  63.                 if( *pmid == 0)                   //是0 不是我们需要考虑的点  
  64.                 {  
  65.                     pmid++;  
  66.                     pmidtemp++;  
  67.                     continue;  
  68.                 }  
  69.                 p3 = *(pmid + 1);  
  70.                 p2 = *(pmid + 1 - lWidth);  
  71.                 p1 = *(pmid - lWidth);  
  72.                 p0 = *(pmid - lWidth -1);  
  73.                 p7 = *(pmid - 1);  
  74.                 p6 = *(pmid + lWidth - 1);  
  75.                 p5 = *(pmid + lWidth);  
  76.                 p4 = *(pmid + lWidth + 1);                
  77.                 sum = p0 & p1 & p2 & p3 & p4 & p5 & p6 & p7;  
  78.                 if(sum == 0)  
  79.                 {  
  80.                     *pmidtemp = 1;       // 这样周围8个都是1的时候  pmidtemp==1 表明是边缘                           
  81.                 }  
  82.   
  83.                 pmid++;  
  84.                 pmidtemp++;  
  85.             }  
  86.             pmid++;  
  87.             pmid++;  
  88.             pmidtemp++;  
  89.             pmidtemp++;  
  90.         }  
  91.           
  92.         //现在开始删除  
  93.         pmid = (unsigned char *)lpDIBBits + lWidth + 1;  
  94.         pmidtemp = (unsigned char *)pTemp + lWidth + 1;  
  95.   
  96.         for(i = 1; i < lHeight -1; i++)   // 不考虑图像第一行 第一列 最后一行 最后一列  
  97.         {  
  98.             for(j = 1; j < lWidth - 1; j++)  
  99.             {  
  100.                 if( *pmidtemp == 0)     //1表明是边缘 0--周围8个都是1 即为中间点暂不予考虑  
  101.                 {  
  102.                     pmid++;  
  103.                     pmidtemp++;  
  104.                     continue;  
  105.                 }  
  106.   
  107.                 p3 = *(pmid + 1);  
  108.                 p2 = *(pmid + 1 - lWidth);  
  109.                 p1 = *(pmid - lWidth);  
  110.                 p0 = *(pmid - lWidth -1);  
  111.                 p7 = *(pmid - 1);  
  112.                 p6 = *(pmid + lWidth - 1);  
  113.                 p5 = *(pmid + lWidth);  
  114.                 p4 = *(pmid + lWidth + 1);  
  115.                   
  116.                 p1 *= 2;  
  117.                 p2 *= 4;  
  118.                 p3 *= 8;  
  119.                 p4 *= 16;  
  120.                 p5 *= 32;  
  121.                 p6 *= 64;  
  122.                 p7 *= 128;  
  123.   
  124.                 sum = p0 | p1 | p2 | p3 | p4 | p5 | p6 | p7;  
  125.             //  sum = p0 + p1 + p2 + p3 + p4 + p5 + p6 + p7;  
  126.                 if(deletemark[sum] == 1)  
  127.                 {  
  128.                     *pmid = 0;  
  129.                     bStart = true;      //  表明本次扫描进行了细化  
  130.                 }  
  131.                 pmid++;  
  132.                 pmidtemp++;  
  133.             }  
  134.   
  135.             pmid++;  
  136.             pmid++;  
  137.             pmidtemp++;  
  138.             pmidtemp++;  
  139.         }  
  140.     }  
  141.     delete []pTemp;  
  142.     return true;  
  143. }  
  144.   
  145. int main(int argc, char* argv[])  
  146. {  
  147.     IplImage* src = cvLoadImage("E:\\study_opencv_video\\testthin\\char2.png",0);  
  148.     cvThreshold(src,src,100,255,CV_THRESH_BINARY);  
  149.     unsigned char* imagedata ;  
  150.   
  151.     cvNamedWindow("s",0);  
  152.     cvShowImage("s" , src);  
  153.     imagedata = new uchar[sizeof(char)*src->width*src->height]();  
  154.     int x , y;  
  155.     for(y=0;y<src->height;y++)  
  156.     {  
  157.         unsigned char* ptr  = (unsigned char*)(src->imageData + y*src->widthStep);  
  158.         for(x=0;x<src->width;x++)  
  159.         {  
  160.             imagedata[y*src->width+x] = ptr[x] > 0 ? 1 : 0;  
  161.         }  
  162.     }  
  163.     ThiningDIBSkeleton(imagedata,src->width,src->height);  
  164.   
  165.     for(y=0;y<src->height;y++)  
  166.     {  
  167.         unsigned char* ptr  = (unsigned char*)(src->imageData + y*src->widthStep);  
  168.         for(x=0;x<src->width;x++)  
  169.         {  
  170.             ptr[x] = imagedata[y*src->width + x]>0? 255 : 0;  
  171.         }  
  172.   
  173.     }  
  174.     cvNamedWindow("src",0);  
  175.     cvShowImage("src" , src);  
  176.     cvWaitKey(0);  
  177.     delete []imagedata;  
  178.     return 0;  
  179. }  

转载自:http://blog.csdn.net/lu597203933/article/details/14397605

参考论文 A fast parallel algorithm for thinning digital patterns

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. #include <opencv2/opencv.hpp>  
  2. #include <iostream>  
  3. #include <vector>  
  4. #include <limits>  
  5.   
  6. using namespace cv;  
  7. using namespace std;  
  8.   
  9. /** 
  10.  * @brief 对输入图像进行细化 
  11.  * @param[in] src为输入图像,用cvThreshold函数处理过的8位灰度图像格式,元素中只有0与1,1代表有元素,0代表为空白 
  12.  * @param[out] dst为对src细化后的输出图像,格式与src格式相同,调用前需要分配空间,元素中只有0与1,1代表有元素,0代表为空白 
  13.  * @param[in] maxIterations限制迭代次数,如果不进行限制,默认为-1,代表不限制迭代次数,直到获得最终结果 
  14.  */  
  15. void thinImage(IplImage* src,IplImage* dst,int maxIterations = -1 )  
  16. {  
  17.     CvSize size = cvGetSize(src);  
  18.     cvCopy(src,dst);//将src中的内容拷贝到dst中  
  19.     int count = 0;  //记录迭代次数  
  20.     while (true)  
  21.     {  
  22.         count++;  
  23.         if(maxIterations!=-1 && count > maxIterations) //限制次数并且迭代次数到达  
  24.             break;  
  25.         //std::cout << count << ' ';输出迭代次数  
  26.         vector<pair<int,int> > mFlag; //用于标记需要删除的点  
  27.         //对点标记  
  28.         for (int i=0; i<size.height; ++i)  
  29.         {  
  30.             for (int j=0; j<size.width; ++j)  
  31.             {  
  32.                 //如果满足四个条件,进行标记  
  33.                 //  p9 p2 p3  
  34.                 //  p8 p1 p4  
  35.                 //  p7 p6 p5  
  36.                 int p1 = CV_IMAGE_ELEM(dst,uchar,i,j);  
  37.                 int p2 = (i==0)?0:CV_IMAGE_ELEM(dst,uchar,i-1,j);  
  38.                 int p3 = (i==0 || j==size.width-1)?0:CV_IMAGE_ELEM(dst,uchar,i-1,j+1);  
  39.                 int p4 = (j==size.width-1)?0:CV_IMAGE_ELEM(dst,uchar,i,j+1);  
  40.                 int p5 = (i==size.height-1 || j==size.width-1)?0:CV_IMAGE_ELEM(dst,uchar,i+1,j+1);  
  41.                 int p6 = (i==size.height-1)?0:CV_IMAGE_ELEM(dst,uchar,i+1,j);  
  42.                 int p7 = (i==size.height-1 || j==0)?0:CV_IMAGE_ELEM(dst,uchar,i+1,j-1);  
  43.                 int p8 = (j==0)?0:CV_IMAGE_ELEM(dst,uchar,i,j-1);  
  44.                 int p9 = (i==0 || j==0)?0:CV_IMAGE_ELEM(dst,uchar,i-1,j-1);  
  45.   
  46.                 if ((p2+p3+p4+p5+p6+p7+p8+p9)>=2 && (p2+p3+p4+p5+p6+p7+p8+p9)<=6)  
  47.                 {  
  48.                     int ap=0;  
  49.                     if (p2==0 && p3==1) ++ap;  
  50.                     if (p3==0 && p4==1) ++ap;  
  51.                     if (p4==0 && p5==1) ++ap;  
  52.                     if (p5==0 && p6==1) ++ap;  
  53.                     if (p6==0 && p7==1) ++ap;  
  54.                     if (p7==0 && p8==1) ++ap;  
  55.                     if (p8==0 && p9==1) ++ap;  
  56.                     if (p9==0 && p2==1) ++ap;  
  57.                       
  58.                     if (ap==1)  
  59.                     {  
  60.                         if (p2*p4*p6==0)  
  61.                         {  
  62.                             if (p4*p6*p8==0)  
  63.                             {  
  64.                                 //标记  
  65.                                 mFlag.push_back(make_pair(i,j));  
  66.                             }  
  67.                         }  
  68.                     }  
  69.                 }  
  70.             }  
  71.         }  
  72.   
  73.         //将标记的点删除  
  74.         for (vector<pair<int,int> >::iterator i=mFlag.begin(); i!=mFlag.end(); ++i)  
  75.         {  
  76.             CV_IMAGE_ELEM(dst,uchar,i->first,i->second) = 0;  
  77.         }  
  78.   
  79.         //直到没有点满足,算法结束  
  80.         if (mFlag.size()==0)  
  81.         {  
  82.             break;  
  83.         }  
  84.         else  
  85.         {  
  86.             mFlag.clear();//将mFlag清空  
  87.         }  
  88.   
  89.         //对点标记  
  90.         for (int i=0; i<size.height; ++i)  
  91.         {  
  92.             for (int j=0; j<size.width; ++j)  
  93.             {  
  94.                 //如果满足四个条件,进行标记  
  95.                 //  p9 p2 p3  
  96.                 //  p8 p1 p4  
  97.                 //  p7 p6 p5  
  98.                 int p1 = CV_IMAGE_ELEM(dst,uchar,i,j);  
  99.                 if(p1!=1) continue;  
  100.                 int p2 = (i==0)?0:CV_IMAGE_ELEM(dst,uchar,i-1,j);  
  101.                 int p3 = (i==0 || j==size.width-1)?0:CV_IMAGE_ELEM(dst,uchar,i-1,j+1);  
  102.                 int p4 = (j==size.width-1)?0:CV_IMAGE_ELEM(dst,uchar,i,j+1);  
  103.                 int p5 = (i==size.height-1 || j==size.width-1)?0:CV_IMAGE_ELEM(dst,uchar,i+1,j+1);  
  104.                 int p6 = (i==size.height-1)?0:CV_IMAGE_ELEM(dst,uchar,i+1,j);  
  105.                 int p7 = (i==size.height-1 || j==0)?0:CV_IMAGE_ELEM(dst,uchar,i+1,j-1);  
  106.                 int p8 = (j==0)?0:CV_IMAGE_ELEM(dst,uchar,i,j-1);  
  107.                 int p9 = (i==0 || j==0)?0:CV_IMAGE_ELEM(dst,uchar,i-1,j-1);  
  108.   
  109.                 if ((p2+p3+p4+p5+p6+p7+p8+p9)>=2 && (p2+p3+p4+p5+p6+p7+p8+p9)<=6)  
  110.                 {  
  111.                     int ap=0;  
  112.                     if (p2==0 && p3==1) ++ap;  
  113.                     if (p3==0 && p4==1) ++ap;  
  114.                     if (p4==0 && p5==1) ++ap;  
  115.                     if (p5==0 && p6==1) ++ap;  
  116.                     if (p6==0 && p7==1) ++ap;  
  117.                     if (p7==0 && p8==1) ++ap;  
  118.                     if (p8==0 && p9==1) ++ap;  
  119.                     if (p9==0 && p2==1) ++ap;  
  120.   
  121.                     if (ap==1)  
  122.                     {  
  123.                         if (p2*p4*p8==0)  
  124.                         {  
  125.                             if (p2*p6*p8==0)  
  126.                             {  
  127.                                 //标记  
  128.                                 mFlag.push_back(make_pair(i,j));  
  129.                             }  
  130.                         }  
  131.                     }  
  132.                 }  
  133.             }  
  134.         }  
  135.         //删除  
  136.         for (vector<pair<int,int> >::iterator i=mFlag.begin(); i!=mFlag.end(); ++i)  
  137.         {  
  138.             CV_IMAGE_ELEM(dst,uchar,i->first,i->second) = 0;  
  139.         }  
  140.   
  141.         //直到没有点满足,算法结束  
  142.         if (mFlag.size()==0)  
  143.         {  
  144.             break;  
  145.         }  
  146.         else  
  147.         {  
  148.             mFlag.clear();//将mFlag清空  
  149.         }  
  150.     }  
  151. }  
  152.   
  153. int main(int argc, char*argv[])  
  154. {  
  155.     //获取图像  
  156.     if (argc!=2)  
  157.     {  
  158.         cout << "参数个数错误!"<<endl;  
  159.         return -1;  
  160.     }  
  161.     IplImage *pSrc = cvLoadImage(argv[1],CV_LOAD_IMAGE_GRAYSCALE);  
  162.     if (!pSrc)  
  163.     {  
  164.         cout << "读取文件失败!" << endl;  
  165.         return -1;  
  166.     }  
  167.     IplImage *pTemp = cvCreateImage(cvGetSize(pSrc),pSrc->depth,pSrc->nChannels);  
  168.     IplImage *pDst = cvCreateImage(cvGetSize(pSrc),pSrc->depth,pSrc->nChannels);  
  169.       
  170.     //将原图像转换为二值图像  
  171.     cvThreshold(pSrc,pTemp,128,1,CV_THRESH_BINARY);   
  172.     //图像细化  
  173.     thinImage(pTemp,pDst);  
  174.   
  175.     for (int i=0; i<pDst->height; ++i)  
  176.     {  
  177.         for (int j=0; j<pDst->width; ++j)  
  178.         {  
  179.             if(CV_IMAGE_ELEM(pDst,uchar,i,j)==1)  
  180.                 CV_IMAGE_ELEM(pDst,uchar,i,j)= 255;  
  181.         }  
  182.     }  
  183.   
  184.     namedWindow("src",CV_WINDOW_AUTOSIZE);  
  185.     namedWindow("dst",CV_WINDOW_AUTOSIZE);  
  186.     cvShowImage("src",pSrc);  
  187.     cvShowImage("dst",pDst);  
  188.   
  189.     waitKey(0);  


转载自:http://blog.csdn.net/qianchenglenger/article/details/19332011

  • 1
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值