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

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

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


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

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

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

    参考资料

  • 细化算法
  • 论文 A fast parallel algorithm for thinning digital patterns[cpp]view plaincopy
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. //将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. 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清空  
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清空  
149.         }  
150.     }  
151. }  
152.   
153. int main(int argc, char*argv[])  
154. {  
155. //获取图像  
156. if (argc!=2)  
157.     {  
158. "参数个数错误!"<<endl;  
159. return -1;  
160.     }  
161.     IplImage *pSrc = cvLoadImage(argv[1],CV_LOAD_IMAGE_GRAYSCALE);  
162. if (!pSrc)  
163.     {  
164. "读取文件失败!" << 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. "src",CV_WINDOW_AUTOSIZE);  
185. "dst",CV_WINDOW_AUTOSIZE);  
186. "src",pSrc);  
187. "dst",pDst);  
188.   
189.     waitKey(0);  
190. }

    运行效果

1原图像

经典的opencv 锐化算法 opencv细化_二值图像


2.运行效果

经典的opencv 锐化算法 opencv细化_经典的opencv 锐化算法_02