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 }