opencv图像细化

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了opencv图像细化相关的知识,希望对你有一定的参考价值。

【opencv】图像细化

 

【opencv】图像细化

技术分享 分类:
 

     在我们进行图像处理的时候,有可能需要对图像进行细化,提取出图像的骨架信息,进行更加有效的分析。

     图像细化(Image Thinning),一般指二值图像的骨架化(Image Skeletonization) 的一种操作运算。

     所谓的细化就是经过一层层的剥离,从原来的图中去掉一些点,但仍要保持原来的形状,直到得到图像的骨架。骨架,可以理解为图象的中轴。

     好的细化算法一定要满足:
  • 收敛性;
  • 保证细化后细线的连通性;
  • 保持原图的基本形状;
  • 减少笔画相交处的畸变;
  • 细化结果是原图像的中心线;
  • 细化的快速性和迭代次数少;

    这里,我们对“Zhang并行快速细化算法”进行了实现(注意,该算法为并行算法,而我们在实现过程中并没有并行化处理,所以,效率并没有达到最好)。

    参考资料

  • 细化算法
  • 论文 A fast parallel algorithm for thinning digital patterns
    [cpp] view plain copy
     
     技术分享技术分享
    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);  
    190. }  

        运行效果

    1原图像

    技术分享

    2.运行效果

    技术分享

     

 
 

以上是关于opencv图像细化的主要内容,如果未能解决你的问题,请参考以下文章

youcans 的 OpenCV 例程200篇127. 形态算法之细化

OpenCV—python 图片细化(骨架提取)二

基于OpenCV实现二值图细化,骨骼化并求出端点和交叉点

在opencv中清理扫描的图像

OpenCV 边缘检测

youcans 的 OpenCV 例程200篇121. 击中-击不中用于特征识别