Ex3:霍夫变换算法实现A4纸直线检测和硬币圆形检测(使用CImg库)

计算机视觉作业三

A4纸直线检测

输入图像

普通 A 4 打印纸,上面可能有手写笔记或者打印内容,但是拍照时可能角度不正。

输出图像

  1. 图像的边缘
  2. 计算 A4 纸边缘的各直线方程
  3. 提取 A4 纸的四个角点

如下图:由于原图太大,这里我截图放上来
这里写图片描述

这里写图片描述

代码文件

A4纸直线检测网上可以找到很多相关的内容,这里我就不做算法解释了,直接贴上代码。

Hough.h文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
#include "CImg.h"
#include <iostream>
#include <vector>
using namespace cimg_library;
using namespace std;

struct Point {
int x, y, cnt;
Point(int _x, int _y, int _cnt): x(_x), y(_y), cnt(_cnt) {}
};

struct Line {
double k, b;
Line(double _k, double _b): k(_k), b(_b) {}
};
class Hough {
private:
CImg<float> src; // 输入的原图
CImg<float> blurred_img; // 高斯滤波平滑得到的图
CImg<float> houghspace; // 霍夫空间图
CImg<float> result; // 最后得到的结果图
vector<Point> peaks; // 霍夫空间直线经过最多的点
vector<Line> lines; // 直线
vector<Point> intersections; // 直线交点
double sigma;
double gradient_threshold;
double vote_threshold;
double peak_dis;
int x_min, x_max, y_min, y_max;
public:
Hough(CImg<float> srcImg, double sigma, double gradient_threshold, double vote_threshold, double peak_dis);
CImg<float> houghProcess(CImg<float> srcImg);
CImg<float> RGBtoGray(const CImg<float>& srcImg); // 转灰度图
CImg<float> initHoughSpace(); // 初始化霍夫空间
void findPeaks(); // 投票算法
void drawLines(); // 寻找并画出直线
void drawIntersections(); // 寻找并画出直线交点
};

Hough.cpp文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
#include "Hough.h"

Hough::Hough(CImg<float> srcImg, double sigma, double gradient_threshold, double vot_threshold, double peak_dis) {
this->result = srcImg;
this->sigma = sigma;
this->gradient_threshold = gradient_threshold;
this->vote_threshold = vot_threshold;
this->peak_dis = peak_dis;
this->x_min = 0;
this->x_max = srcImg._width - 1; // 图像宽度
this->y_min = 0;
this->y_max = srcImg._height - 1; // 图像高度
}
CImg<float> Hough::houghProcess(CImg<float> srcImg) {
this->src = RGBtoGray(srcImg); // 转灰度图
this->blurred_img = src.get_blur(sigma); // 高斯滤波平滑
this->houghspace = initHoughSpace(); // 初始化霍夫空间
findPeaks(); // 找出霍夫空间中直线经过最多的点
drawLines(); // 寻找并画出直线
drawIntersections(); // 寻找并画出直线交点
return result;
}
// 转灰度图
CImg<float> Hough::RGBtoGray(const CImg<float>& srcImg) {
CImg<float> grayImage = CImg<float>(srcImg._width, srcImg._height, 1, 1, 0);
cimg_forXY(grayImage, x, y) {
grayImage(x, y, 0) = (int)round((double)srcImg(x, y, 0, 0) * 0.299 +
(double)srcImg(x, y, 0, 1) * 0.587 +
(double)srcImg(x, y, 0, 2) * 0.114);
}
return grayImage;
}
// 初始化霍夫空间
CImg<float> Hough::initHoughSpace() {
// sobel算子
CImg<float> sobelx(3, 3, 1, 1, 0);
CImg<float> sobely(3, 3, 1, 1, 0);
sobelx(0, 0) = -1, sobely(0, 0) = 1;
sobelx(0, 1) = 0, sobely(0, 1) = 2;
sobelx(0, 2) = 1, sobely(0, 2) = 1;
sobelx(1, 0) = -2, sobely(1, 0) = 0;
sobelx(1, 1) = 0, sobely(1, 1) = 0;
sobelx(1, 2) = 2, sobely(1, 2) = 0;
sobelx(2, 0) = -1, sobely(2, 0) = -1;
sobelx(2, 1) = 0, sobely(2, 1) = -2;
sobelx(2, 2) = 1, sobely(2, 2) = -1;

CImg<float> gradient_x = blurred_img;
gradient_x = gradient_x.get_convolve(sobelx); // 计算x方向上的梯度
CImg<float> gradient_y = blurred_img;
gradient_y = gradient_y.get_convolve(sobely); // 计算y方向上的梯度

int maxp = (int)sqrt(src._width*src._width + src._height*src._height);
CImg<float> hough_space(360, maxp, 1, 1, 0); // 初始化hough space

cimg_forXY(src, i, j) {
double grad = sqrt(gradient_x(i, j)*gradient_x(i, j) + gradient_y(i, j)*gradient_y(i, j));
if (grad > gradient_threshold) {
src(i, j) = grad;
cimg_forX(hough_space, alpha) {
double theta = ((double)alpha*cimg::PI) / 180;
int p = (int)(i*cos(theta) + j*sin(theta));
if (p >= 0 && p < maxp) {
hough_space(alpha, p)++; // 累加矩阵
}
}
}
}
return hough_space;
}
// 投票算法找出霍夫空间中直线经过最多的点
void Hough::findPeaks() {
peaks.clear();
cimg_forXY(houghspace, theta, p) {
if (houghspace(theta, p) > vote_threshold) {
bool flag = true;
double alpha = (double)theta*cimg::PI / 180;
// y的范围
const int y0 = ((double)p / (sin(alpha))) - double(x_min)*(1 / tan(alpha));
const int y1 = ((double)p / (sin(alpha))) - double(x_max)*(1 / tan(alpha));
// x的范围
const int x0 = ((double)p / (cos(alpha))) - double(y_min)*(tan(alpha));
const int x1 = ((double)p / (cos(alpha))) - double(y_max)*(tan(alpha));

if (x0 >= x_min && x0 <= x_max || x1 >= x_min && x1 <= x_max ||
y0 >= y_min && y0 <= y_max || y1 >= y_min && y1 <= y_max) {
for (int i = 0; i < peaks.size(); i++) {
if (sqrt((peaks[i].x - theta)*(peaks[i].x - theta)
+ (peaks[i].y - p)*(peaks[i].y - p)) < peak_dis) {
flag = false;
if (peaks[i].cnt < houghspace(theta, p)) {
Point temp(theta, p, houghspace(theta, p));
peaks[i] = temp;
}
}
}
if (flag) {
Point temp(theta, p, houghspace(theta, p));
peaks.push_back(temp);
}
}
}
}
}
// 寻找并画出直线
void Hough::drawLines() {
lines.clear();
for (int i = 0; i < peaks.size(); i++) {
double theta = double(peaks[i].x)*cimg::PI / 180;
double k = -cos(theta) / sin(theta); // 直线斜率
double b = double(peaks[i].y) / sin(theta);
Line templine(k, b);
lines.push_back(templine);
cout << "Line " << i << ": y = " << k << "x + " << b << endl;
}

const double lines_color[] = { 255, 0, 0 };
for (int i = 0; i < lines.size(); i++) {
const int x0 = (double)(y_min - lines[i].b) / lines[i].k;
const int x1 = (double)(y_max - lines[i].b) / lines[i].k;
const int y0 = x_min*lines[i].k + lines[i].b;
const int y1 = x_max*lines[i].k + lines[i].b;

if (abs(lines[i].k) > 1) {
result.draw_line(x0, y_min, x1, y_max, lines_color);
}
else {
result.draw_line(x_min, y0, x_max, y1, lines_color);
}
}

}
// 寻找并画出直线交点
void Hough::drawIntersections() {
intersections.clear();
int k = 0;
for (int i = 0; i < lines.size(); i++) {
for (int j = i + 1; j < lines.size(); j++) {
double k0 = lines[i].k;
double k1 = lines[j].k;
double b0 = lines[i].b;
double b1 = lines[j].b;

double x = (b1 - b0) / (k0 - k1);
double y = (k0*b1 - k1*b0) / (k0 - k1);

if (x >= 0 && x < src._width && y >= 0 && y < src._height) {
Point tempPoint(x, y, 0);
intersections.push_back(tempPoint);
cout << "Intersection " << k++ << ": x = " << x << ", y = " << y << endl;
}
}
}

const double intersections_color[] = { 255, 0, 0 };
for (int i = 0; i < intersections.size(); i++) {
result.draw_circle(intersections[i].x, intersections[i].y, 50, intersections_color);
}
}

main.cpp文件:

1
2
3
4
5
6
7
8
9
10
11
#include "Hough.cpp"

int main() {
CImg<float> src("2.bmp");
// args: src sigma gradient_threshold vote_threshold peak_dis
Hough hough(src, 10.5f, 30, 1000, 60);
CImg<float> result = hough.houghProcess(src);
result.display();
result.save("result/2.bmp");
return 0;
}

编译运行指示

可以在main.cpp文件中修改要输入的bmp图像,然后在cmd中运行如下命令:

g++ -o main.exe main.cpp -O2 -lgdi32

进行编译,最后再运行main.exe得到结果。

硬币圆形检测

输出图像

  1. 图像的边缘
  2. 把图像中边缘拟合成圆, 圆周像素用红色像素标出
  3. 输出图像中硬币的数量

如下图:由于图片太大,这里我是截图放上来的
这里写图片描述

这里写图片描述

代码文件

说实话,这个比较难,在作业ddl我并没做出来,所以我“犯规了”,用了python版本的opencv圆形霍夫变换函数进行检测,但调参(半径范围)也是一件比较痛苦的事情2333。
后面有大佬在作业ddl后完成了CImg检测圆形硬币这部分工作,献上我的膝盖。

Python版本:

运行指示:
在cmd中运行如下命令:
python circle_hough_transform.py 1.bmp
也可以修改文件名,改用其他文件,例如2.bmp

circle_hough_transform.py文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
import sys
import cv2 as cv
import numpy as np
def main(argv):

default_file = "2.bmp"
filename = argv[0] if len(argv) > 0 else default_file
# Loads an image
src = cv.imread(filename, cv.IMREAD_COLOR)
# Check if image is loaded fine
if src is None:
print ('Error opening image!')
print ('Usage: hough_circle.py [image_name -- default ' + default_file + '] \n')
return -1

gray = cv.cvtColor(src, cv.COLOR_BGR2GRAY)

gray = cv.medianBlur(gray, 5)

rows = gray.shape[0]
"""
各张图的最小半径minRadius和最大半径maxRadius
1.bmp: 150 230
2.bmp: 190 230
3.bmp: 146 230
4.bmp: 146 200
5.bmp: 480 525
6.bmp: 40 60
"""
circles = cv.HoughCircles(gray, cv.HOUGH_GRADIENT, 1, rows / 8,
param1=100, param2=30,
minRadius=40, maxRadius=60)

if circles is not None:
circles = np.uint16(np.around(circles))
print("硬币数量:", circles.shape[1])
for i in circles[0, :]:
center = (i[0], i[1])
print("圆心:", center)
# circle center
cv.circle(src, center, 1, (0, 0, 255), 6)
# circle outline
radius = i[2]
print("半径:", radius)
cv.circle(src, center, radius, (0, 0, 255), 3)

cv.imshow("detected circles", src)
cv.imwrite("result/" + filename, src);
cv.waitKey(0)

return 0
if __name__ == "__main__":
main(sys.argv[1:])

CImg版本

在Ex2作业(可见我前面的文章)的基础上进行修改,在这里我只把霍夫变换的代码给出来,代码包含了直线检测和圆形检测,只需要修改一下传入参数即可,全部代码文件可见github地址

EdgeDetect.h文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
#pragma once
#ifndef EDGE_DETECT_H
#define EDGE_DETECT_H

#include <iostream>
#include <string>
#include <vector>
#include "CImg.h"

using namespace std;
using namespace cimg_library;

class EdgeDetect {
public:
EdgeDetect(string, string, string, int, int minR = 0, int maxR = 0); // 构造函数:输入图像,并输出图像边缘检测结果
void toGrayScale(); // 灰度化处理
vector<vector<float>> createFilter(int, int, float); // 产生高斯滤波器
CImg<float> useFilter(CImg<float>&, vector<vector<float>>&); // 进行高斯滤波
CImg<float> sobel(CImg<float>&, CImg<float>&); // 产生sobel算子
CImg<float> nonMaxSupp(CImg<float>&, CImg<float>&); // 进行非最大化抑制
CImg<float> threshold(CImg<float>&, int, int); // 双阈值处理

void houghLinesTransform(CImg<float>&); // 霍夫直线变换
void houghLinesDetect(); // 霍夫直线检测
int getMaxHough(CImg<float>&, int&, int&, int&); // 计算霍夫空间直线交点
void drawEdge(); // 描绘检测出的边缘
void drawPoint(); // 描绘检测出的角点

void houghCirclesTransform(CImg<float>&, int, int); // 霍夫圆变换
void houghCirclesDetect(); // 霍夫圆检测
void drawCircle(int); // 描绘检测出的圆形
private:
CImg<float> image; // 原图像
CImg<float> grayImage; // 灰度图像
CImg<float> thresholdImage; // 经过阈值处理后的图像
CImg<float> houghImage; // 霍夫空间图像
CImg<float> outputImage; // 霍夫变换检测出来的图像
vector<vector<float>> filter; // 滤波器
vector<float> setSin; // sin集合
vector<float> setCos; // cos集合

int pointNumber; // 角点数
vector<pair<int, int>> lines; // 检测到的直线集合
vector<int> lineWeight; // 累加矩阵
vector<int> sortLineWeight; // 从大到小排序的累加矩阵
CImg<float> edge; // 边缘直线

int circleNumber; // 检测圆个数
int minRadius; // 圆周最小半径
int maxRadius; // 圆周最大半径
vector<pair<int, int>> circles; // 检测到的圆心集合
vector<pair<int, int>> voteSet; // 投票集合
vector<pair<int, int>> center; // 存放累加值最大的圆心对应坐标
vector<int> circleWeight; // 累加矩阵
vector<int> sortCircleWeight; // 从大到小排序的累加矩阵
};

#endif // !EDGE_DETECT_H

EdgeDetect.cpp文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
#include "EdgeDetect.h"
#include "CANNY.h"
#include <cmath>
#include <algorithm>
#include <functional>

#define PI 3.141592653
#define gFilterX 5
#define gFilterY 5
#define sigma 1
#define thresholdLow 120
#define thresholdHigh 140
#define thetaSize 360
#define windowSize 60

EdgeDetect::EdgeDetect(string input, string output, string mode, int number, int minR, int maxR) {
for (int i = 0; i < thetaSize; i++) {
setSin.push_back(sin(2 * PI * i / thetaSize));
setCos.push_back(cos(2 * PI * i / thetaSize));
}

if (mode == "line") {
image.load(input.c_str());

int width = image._width, height = image._height;
image.resize(600, 800);
if (width == 0 || height == 0) {
cout << "Cannot open or find the image" << endl;
}
image.display("Origin Image"); // 显示原图像
outputImage = image;

toGrayScale(); // 进行灰度化处理
filter = createFilter(gFilterX, gFilterY, sigma); // 产生高斯滤波器
CImg<float> gFiltered = useFilter(grayImage, filter); // 进行高斯滤波

/*Canny检测*/
CImg<float> angles;
CImg<float> sFiltered = sobel(gFiltered, angles); // 产生sobel算子并计算梯度幅值和角度图像
CImg<float> nms = nonMaxSupp(sFiltered, angles); // 非最大化抑制处理
thresholdImage = threshold(nms, thresholdLow, thresholdHigh); // 双阈值处理
thresholdImage.display("Threshold Image"); // 显示canny检测出的边缘

for (int i = 0; i < thetaSize; i++) {
setSin.push_back(sin(2 * PI * i / thetaSize));
setCos.push_back(cos(2 * PI * i / thetaSize));
}

pointNumber = number;
houghLinesTransform(thresholdImage); // 霍夫直线变换
houghLinesDetect(); // 霍夫直线检测

drawEdge(); // 描绘霍夫变换检测出的边缘
drawPoint(); // 描绘霍夫变换检测出的角点
outputImage.resize(width, height);
outputImage.save(output.c_str());
}
else if (mode == "circle") {
image.load(input.c_str());

int width = image._width, height = image._height;
if (width == 0 || height == 0) {
cout << "Cannot open or find the image" << endl;
}
image.display("Origin Image"); // 显示原图像
outputImage = image;

CImg<unsigned char> crImage(image);
CANNY myCanny;
crImage = myCanny.toGrayScale(crImage);
unsigned char* grey = crImage._data;
myCanny.canny(grey, crImage.width(), crImage.height(), 2.5f, 7.5f, 4.5f, 16);
CImg<double> edge(myCanny.result, image.width(), image.height());
thresholdImage = edge;
thresholdImage.display("Threshold Image"); // 显示canny检测出的边缘

circleNumber = number;
minRadius = minR;
maxRadius = maxR;

houghCirclesTransform(thresholdImage, minRadius, maxRadius); // 霍夫圆变换
outputImage.save(output.c_str());
}
}

void EdgeDetect::toGrayScale() {
grayImage = CImg<float>(image._width, image._height, 1, 1); //新建一个灰度图像
//彩色图像转为灰度图像的公式:R * 0.2989 + G * 0.5870 + B * 0.1140
cimg_forXY(image, x, y) {
grayImage(x, y) = image(x, y, 0) * 0.2989 + image(x, y, 1) * 0.5870 + image(x, y, 2) * 0.1140;
}
}

/*高斯滤波器*/
vector<vector<float>> EdgeDetect::createFilter(int row, int col, float tempSigma) {
float sum = 0, temp = 2.0 * tempSigma * tempSigma;
/*初始化*/
for (int i = 0; i < row; i++) {
vector<float> v(col, 0);
filter.push_back(v);
}

for (int i = -row / 2; i <= row / 2; i++) {
for (int j = -col / 2; j <= col / 2; j++) {
filter[i + row / 2][j + col / 2] = exp(-(i * i + j * j) / temp) / sqrt(PI * temp); // 高斯函数
sum += filter[i + row / 2][j + col / 2];
}
}
// 归一化
for (int i = 0; i < row; i++) {
for (int j = 0; j < col; j++) {
filter[i][j] /= sum;
}
}
return filter;
}

/*进行高斯滤波*/
CImg<float> EdgeDetect::useFilter(CImg<float>& img, vector<vector<float>>& filt) {
int size = filt.size() / 2;
CImg<float> filtered(img._width - 2 * size, img._height - 2 * size, 1, 1);
for (int i = size; i < img._width - size; i++) {
for (int j = size; j < img._height - size; j++) {
float sum = 0;
for (int x = 0; x < filt.size(); x++) {
for (int y = 0; y < filt.size(); y++) {
sum += filt[x][y] * (float)(img(i + x - size, j + y - size)); // 高斯滤波
}
}
filtered(i - size, j - size) = sum;
}
}
return filtered;
}

/*利用sobel算子计算梯度幅值和角度图像*/
CImg<float> EdgeDetect::sobel(CImg<float>& gFiltered, CImg<float>& angles) {
/*sobel算子*/
vector<vector<float>> xFilter(3, vector<float>(3, 0)), yFilter(3, vector<float>(3, 0));
xFilter[0][0] = xFilter[2][0] = yFilter[0][0] = yFilter[0][2] = -1;
xFilter[0][2] = xFilter[2][2] = yFilter[2][0] = yFilter[2][2] = 1;
xFilter[1][0] = yFilter[0][1] = -2;
xFilter[1][2] = yFilter[2][1] = 2;

int size = xFilter.size() / 2;
CImg<float> filteredImage(gFiltered._width - 2 * size, gFiltered._height - 2 * size, 1, 1);
angles = filteredImage;

for (int i = size; i < gFiltered._width - size; i++) {
for (int j = size; j < gFiltered._height - size; j++) {
/*计算梯度幅度gx,gy*/
float sumX = 0, sumY = 0;
for (int x = 0; x < xFilter.size(); x++) {
for (int y = 0; y < yFilter.size(); y++) {
sumX += xFilter[y][x] * (float)(gFiltered(i + x - size, j + y - size));
sumY += yFilter[y][x] * (float)(gFiltered(i + x - size, j + y - size));
}
}
if (sqrt(sumX * sumX + sumY * sumY) > 255) {
filteredImage(i - size, j - size) = 255;
}
else {
filteredImage(i - size, j - size) = sqrt(sumX * sumX + sumY * sumY);
}

/*计算梯度方向*/
if (sumX == 0) {
angles(i - size, j - size) = 90;
}
else {
angles(i - size, j - size) = atan(sumY / sumX);
}
}
}
return filteredImage;
}

/*对梯度幅值图像应用非最大化抑制*/
CImg<float> EdgeDetect::nonMaxSupp(CImg<float>& sFiltered, CImg<float>& angles) {
CImg<float> nms(sFiltered._width - 2, sFiltered._height - 2, 1, 1);
for (int i = 1; i < sFiltered._width - 1; i++) {
for (int j = 1; j < sFiltered._height - 1; j++) {
float angle = angles(i, j);
nms(i - 1, j - 1) = sFiltered(i, j);

/*水平边缘*/
if ((angle > -22.5 && angle <= 22.5) || (angle > 157.5 && angle <= -157.5)) {
if (sFiltered(i, j) < sFiltered(i, j + 1) || sFiltered(i, j) < sFiltered(i, j - 1)) {
nms(i - 1, j - 1) = 0;
}
}
/*+45度边缘*/
if ((angle > -67.5 && angle <= -22.5) || (angle > 112.5 && angle <= 157.5)) {
if (sFiltered(i, j) < sFiltered(i - 1, j + 1) || sFiltered(i, j) < sFiltered(i + 1, j - 1)) {
nms(i - 1, j - 1) = 0;
}
}
/*垂直边缘*/
if ((angle > -112.5 && angle <= -67.5) || (angle > 67.5 && angle <= 112.5)) {
if (sFiltered(i, j) < sFiltered(i + 1, j) || sFiltered(i, j) < sFiltered(i - 1, j)) {
nms(i - 1, j - 1) = 0;
}
}
/*-45度边缘*/
if ((angle > -157.5 && angle <= -112.5) || (angle > 22.5 && angle <= 67.5)) {
if (sFiltered(i, j) < sFiltered(i + 1, j + 1) || sFiltered(i, j) < sFiltered(i - 1, j - 1)) {
nms(i - 1, j - 1) = 0;
}
}
}
}
return nms;
}

/*用双阈值处理和连接分析来检测并连接边缘*/
CImg<float> EdgeDetect::threshold(CImg<float>& img, int low, int high) {
low = (low > 255) ? 255 : low;
high = (high > 255) ? 255 : high;

CImg<float> edgeMatch(img._width, img._height, 1, 1);
for (int i = 0; i < img._width; i++) {
for (int j = 0; j < img._height; j++) {
edgeMatch(i, j) = img(i, j);
if (edgeMatch(i, j) > high) {
edgeMatch(i, j) = 255; // 如果高于高阈值,赋值为255
}
else if (edgeMatch(i, j) < low) {
edgeMatch(i, j) = 0; // 如果低于低阈值,赋值为0
}
else {
bool ifHigh = false, ifBetween = false;
for (int x = i - 1; x < i + 2; x++) {
for (int y = j - 1; y < j + 2; y++) {
if (x > 0 && x <= edgeMatch._height && y > 0 && y <= edgeMatch._width) {
if (edgeMatch(x, y) > high) {
edgeMatch(i, j) = 255;
ifHigh = true;
break;
}
else if (edgeMatch(x, y) <= high && edgeMatch(x, y) >= low) {
ifBetween = true;
}
}
}
if (ifHigh) {
break;
}
}
if (!ifHigh && ifBetween) {
for (int x = i - 2; x < i + 3; x++) {
for (int y = j - 1; y < j + 3; y++) {
if (x > 0 && x <= edgeMatch._height && y > 0 && y <= edgeMatch._width) {
if (edgeMatch(x, y) > high) {
edgeMatch(i, j) = 255;
ifHigh = true;
break;
}
}
}
if (ifHigh) {
break;
}
}
}
if (!ifHigh) {
edgeMatch(i, j) = 0;
}
}
}
}
return edgeMatch;
}

/*霍夫直线变换*/
void EdgeDetect::houghLinesTransform(CImg<float>& img) {
int width = img._width, height = img._height, maxLength, row, col;
maxLength = sqrt(pow(width / 2, 2) + pow(height / 2, 2)); // 进行霍夫空间极坐标变换
row = thetaSize;
col = maxLength;

houghImage = CImg<float>(col, row);
houghImage.fill(0);

cimg_forXY(img, x, y) {
int value = img(x, y), p = 0;
if (value != 0) {
int x0 = x - width / 2, y0 = height / 2 - y;
for (int i = 0; i < thetaSize; i++) {
/*进行voting投票*/
p = x0 * setCos[i] + y0 * setSin[i];
if (p >= 0 && p < maxLength) {
houghImage(p, i)++;
}
}
}
}
}

/*霍夫直线检测*/
void EdgeDetect::houghLinesDetect() {
int width = houghImage._width, height = houghImage._height, size = windowSize, max;
for (int i = 0; i < height; i += size / 2) {
for (int j = 0; j < width; j += size / 2) {
max = getMaxHough(houghImage, size, i, j);
for (int y = i; y < i + size; ++y) {
for (int x = j; x < j + size; ++x) {
if (houghImage._atXY(x, y) < max) {
houghImage._atXY(x, y) = 0; // 把不是边缘点的点去掉
}
}
}
}
}
/*将霍夫图像中所有不为0的点对应直线的斜率和截距存入数组*/
cimg_forXY(houghImage, x, y) {
if (houghImage(x, y) != 0) {
lines.push_back(make_pair(y, x));
lineWeight.push_back(houghImage(x, y));
}
}
}

/*计算霍夫空间直线交点*/
int EdgeDetect::getMaxHough(CImg<float>& img, int& size, int& y, int& x) {
int width = (x + size > img._width) ? img._width : x + size;
int height = (y + size > img._height) ? img._height : y + size;
int max = 0;
for (int j = x; j < width; j++) {
for (int i = y; i < height; i++) {
max = (img(j, i) > max) ? img(j, i) : max;
}
}
return max;
}

/*描绘所检测出的边缘*/
void EdgeDetect::drawEdge() {
int width = image._width, height = image._height, maxLength;
maxLength = sqrt(pow(width / 2, 2) + pow(height / 2, 2));

edge = CImg<float>(width, height, 1, 1, 0);
sortLineWeight = lineWeight;
sort(sortLineWeight.begin(), sortLineWeight.end(), greater<int>()); // 将累加矩阵从大到小进行排序

vector<pair<int, int>> result; // 存放累加值最大的边缘直线对应斜率和截距
for (int i = 0; i < pointNumber; i++) {
int weight = sortLineWeight[i], index;
vector<int>::iterator iter = find(lineWeight.begin(), lineWeight.end(), weight);
index = iter - lineWeight.begin();
result.push_back(lines[index]);
}
for (int i = 0; i < result.size(); i++) {
int theta = result[i].first, p = result[i].second;
/*根据theta和p求出斜率和截距*/
cimg_forXY(edge, x, y) {
int x0 = x - width / 2, y0 = height / 2 - y;
if (p == (int)(x0 * setCos[theta] + y0 * setSin[theta])) {
edge(x, y) += 255.0 / 2;
outputImage(x, y, 0, 2) = 255;
}
}
}
}

/*描绘所检测出的角点*/
void EdgeDetect::drawPoint() {
unsigned char red[3] = { 255, 0, 0 };
for (int y = 0; y < outputImage._height - 1; y++) {
for (int x = 0; x < outputImage._width - 1; x++) {
int arr[4];
arr[0] = edge(x, y);
arr[1] = edge(x + 1, y);
arr[2] = edge(x, y + 1);
arr[3] = edge(x + 1, y + 1);
if (arr[0] + arr[1] + arr[2] + arr[3] >= 255.0 * 3 / 2) {
outputImage.draw_circle(x, y, 3, red);
}
}
}
outputImage.display("Point Detect");
}

/*霍夫圆变换*/
void EdgeDetect::houghCirclesTransform(CImg<float>& img, int minR, int maxR) {
int width = img._width, height = img._height, max = 0;

for (int r = minR; r < maxR; r += 5) {
max = 0;
houghImage = CImg<float>(width, height);
houghImage.fill(0);
cimg_forXY(img, x, y) {
int value = img(x, y);
if (value != 0) {
for (int i = 0; i < thetaSize; i++) {
int x0 = x - r * setCos[i];
int y0 = y - r * setSin[i];
/*进行voting投票*/
if (x0 > 0 && x0 < width && y0 > 0 && y0 < height) {
houghImage(x0, y0)++;
}
}
}
}

/*每次遍历完r后,找到hough里面的最大投票数,这个投票数表示当前r的吻合程度,然后用投票数最大的r作为最好的r*/
for (int x = 0; x < width; x++) {
for (int y = 0; y < height; y++) {
if (houghImage(x, y) > max) {
max = houghImage(x, y);
}
}
}
voteSet.push_back(make_pair(max, r));
}
sort(voteSet.begin(), voteSet.end(), [](const pair<int, int>& x, const pair<int, int>& y) -> int {
return x.first > y.first;
});

for (int i = 0; i < circleNumber; i++) {
houghImage = CImg<float>(width, height);
houghImage.fill(0);
cimg_forXY(img, x, y) {
int value = img(x, y);
if (value != 0) {
for (int j = 0; j < thetaSize; j++) {
int x0 = x - voteSet[i].second * setCos[j];
int y0 = y - voteSet[i].second * setSin[j];
/*进行voting投票*/
if (x0 > 0 && x0 < width && y0 > 0 && y0 < height) {
houghImage(x0, y0)++;
}
}
}
}
cout << "The radius is " << voteSet[i].second << endl;
houghCirclesDetect();
drawCircle(voteSet[i].second);
}
outputImage.display("Circle Detect");
}

void EdgeDetect::houghCirclesDetect() {
/*将霍夫图像中所有不为0的点对应圆心的坐标存入数组*/
cimg_forXY(houghImage, x, y) {
if (houghImage(x, y) != 0) {
circles.push_back(make_pair(x, y));
circleWeight.push_back(houghImage(x, y));
}
}
}

void EdgeDetect::drawCircle(int r) {
int width = image._width, height = image._height, count = 0;
unsigned char red[3] = { 255, 0, 0 };

sortCircleWeight = circleWeight;
sort(sortCircleWeight.begin(), sortCircleWeight.end(), greater<int>()); // 将累加矩阵从大到小进行排序

while (1) {
int weight = sortCircleWeight[count], index;
vector<int>::iterator iter = find(circleWeight.begin(), circleWeight.end(), weight);
index = iter - circleWeight.begin();
int a = circles[index].first, b = circles[index].second;
count++;

int i;
for (i = 0; i < center.size(); i++) {
if (sqrt(pow((center[i].first - a), 2) + pow((center[i].second - b), 2)) < minRadius) {
break; // 判断检测出来的圆心坐标是否跟已检测的圆心坐标的距离,如果距离过小,默认是同个圆
}
}
if (i == center.size()) {
center.push_back(make_pair(a, b));
outputImage.draw_circle(a, b, r, red, 5.0f, 1);
break;
}
}
}

main.cpp文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include "EdgeDetect.cpp"
#include "CANNY.cpp"
#include <cstdlib>

int main() {
EdgeDetect *edgeDetect1 = new EdgeDetect("Dataset/Dataset1/1.bmp", "result/result1/1.bmp", "line", 4);
EdgeDetect *edgeDetect2 = new EdgeDetect("Dataset/Dataset1/2.bmp", "result/result1/2.bmp", "line", 4);
EdgeDetect *edgeDetect3 = new EdgeDetect("Dataset/Dataset1/3.bmp", "result/result1/3.bmp", "line", 4);
EdgeDetect *edgeDetect4 = new EdgeDetect("Dataset/Dataset1/4.bmp", "result/result1/4.bmp", "line", 6);
EdgeDetect *edgeDetect5 = new EdgeDetect("Dataset/Dataset1/5.bmp", "result/result1/5.bmp", "line", 4);
EdgeDetect *edgeDetect6 = new EdgeDetect("Dataset/Dataset1/6.bmp", "result/result1/6.bmp", "line", 4);
EdgeDetect *edgeDetect7 = new EdgeDetect("Dataset/Dataset2/1.bmp", "result/result2/1.bmp", "circle", 1, 150, 170);
EdgeDetect *edgeDetect8 = new EdgeDetect("Dataset/Dataset2/2.bmp", "result/result2/2.bmp", "circle", 4, 180, 250);
EdgeDetect *edgeDetect9 = new EdgeDetect("Dataset/Dataset2/3.bmp", "result/result2/3.bmp", "circle", 7, 120, 200);
EdgeDetect *edgeDetect10 = new EdgeDetect("Dataset/Dataset2/4.bmp", "result/result2/4.bmp", "circle", 3, 150, 250);
EdgeDetect *edgeDetect11 = new EdgeDetect("Dataset/Dataset2/5.bmp", "result/result2/5.bmp", "circle", 2, 420, 540);
EdgeDetect *edgeDetect12 = new EdgeDetect("Dataset/Dataset2/6.bmp", "result/result2/6.bmp", "circle", 5, 40, 70);
system("pause");
return 0;
}

编译运行指示

在cmd窗口下使用如下命令进行编译:
g++ -std=c++11 -o main.exe main.cpp -O2 -lgdi32
然后再运行main.exe得到结果。


完。

------本文结束感谢阅读------