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

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

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

     好的细化算法一定要满足:

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

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

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

1原图像

opencv锯齿边缘去除 opencv边缘细化_迭代

2.运行效果

opencv锯齿边缘去除 opencv边缘细化_opencv锯齿边缘去除_02