少女祈祷中...

基本要求

Hough transform is a very classic and useful method for the detection of curves or lines. It uses the transformation of two coordinate spaces (the image space and the Hough space) to map curves or straight lines to points in another coordinate space to vote and form peaks, thus transforming the problem of detecting arbitrary shapes into a statistical peak problem.
In this task, you can draw some lines or circles by yourself and use Hough Transform to detect the figure you drew (you can try for lines firstly and then for circles). MUST implement it yourself!

代码框架

CPP
1
2
3
4
5
6
7
8
9
10
class ToughTransform{
public:
void LineDetection(Mat& Image);
void CircleDetection(Mat& Image);
private:
void ShowParameterSpace(vector<vector<int>>& PS_AfterFilter, string file_name, int thetaScale);
void DrawLineDetectedImage(Mat& Image, vector<vector<int>>& PS_AfterFilter, int row_max, int theta_max , int second_filter_threshold);
void ShowParameterSpace_Circle(vector<vector<vector<int>>>& PS_AfterFilter, string file_name, int thetaScale);
void DrawCircleDetectedImage(Mat& Image,vector<vector<vector<int>>>& PS_AfterFilter, int max_a, int max_b, int max_r, int second_filter_threshold);
};

直线检测

  • void LineDetection(Mat& Image): 算法主流程。包括将图像转化到二维参数空间(row,theta),对参数空间矩阵进行初次阈值过滤,以及调用ShowParameterSpace和DrawLineDetectedImage方法。
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
/*
LineDetection: ShowParameterSpace + DrawLineDetectedImage
*/
void ToughTransform::LineDetection(Mat& Image){
int img_width = Image.size().width;
int img_height = Image.size().height;
//ParameterSpace[row][theta]
double row_max = sqrt(img_width * img_width + img_height * img_height);
int theta_max = 180; //theta: step_length = 1
vector<vector<int>> PatameterSpace(2 * static_cast<int>(ceil(row_max)),
vector<int>(theta_max, 0));

cout << "ImageSize:" << img_height << "*" << img_width << endl;
cout << "PatameterSpace:" << 2*row_max << "*" << theta_max << " --> " << 2 * static_cast<int>(ceil(row_max)) << "*" << theta_max << endl;

for (int y = 0; y < img_height; y++){
for (int x = 0; x < img_width; x++){
int B = Image.at<Vec3b>(y,x)[0];
int G = Image.at<Vec3b>(y,x)[1];
int R = Image.at<Vec3b>(y,x)[2];
if(B == 0 && G == 0 && R == 0){ //Edge is black-pixel
//!!!look through theta, calc row than write into parameter-space!!!
//遍历theta,计算row,存入参数空间
for(int theta = 0; theta <= theta_max; theta++){
double theta_rad = theta * PI / 180.0;
double row = x * cos(theta_rad) + y * sin(theta_rad);
//row可能是负的,row_max作为偏移量. 将偏移量row_max加到row上,转为非负索引
//cout << "row = " << row << " ---> " << row + row_max << " ---> " << ceil(row) + ceil(row_max) << endl;
PatameterSpace[static_cast<int>(ceil(row) + ceil(row_max))][theta]++;
}
}
}
}

int threshold = 10;
vector<vector<int>> PS_AfterFilter(2 * static_cast<int>(ceil(row_max)),
vector<int>(theta_max, 0));
//threshold-filter
for (int i = 0; i < PatameterSpace.size(); i++){
for (int j = 0; j < PatameterSpace[0].size(); j++){
if(PatameterSpace[i][j] >= threshold)
PS_AfterFilter[i][j] = PatameterSpace[i][j];
else
PS_AfterFilter[i][j] = 0;
}
}
ShowParameterSpace(PS_AfterFilter,"source/line-result/EnhancedHoughSpaceImage-FirstFilter.png");

int second_filter_threshold = 500;
DrawLineDetectedImage(Image,PS_AfterFilter,static_cast<int>(ceil(row_max)),theta_max,second_filter_threshold);
}
  • ShowParameterSpace(vector<vector>& PS_AfterFilter, string file_name, int thetaScale): 在LineDetection和DrawLineDetectedImage中被调用,可以描绘出二维参数空间PS_AfterFilter(row,theta)的点分布。其中file_name是图片保存地址;thetaScale将theta轴拉伸,以达到更好的可视化效果。
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
/* 
ShowParameterSpace:
描绘(row,theta)参数空间的图像。此时的参数矩阵PS_AfterFilter只经历了初步的阈值筛选。
*/
void ToughTransform::ShowParameterSpace(vector<vector<int>>& PS_AfterFilter, string file_name, int thetaScale = 15)
{
int rows = PS_AfterFilter.size();
int cols = PS_AfterFilter[0].size();

Mat houghSpaceImage(rows, cols, CV_8UC1, Scalar(0));
int maxVal = 0;
for (const auto& row : PS_AfterFilter) {
maxVal = max(maxVal, *max_element(row.begin(), row.end()));
}
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
houghSpaceImage.at<uchar>(i, j) = static_cast<uchar>(255.0 * PS_AfterFilter[i][j] / maxVal);
}
}
Mat enlargedHoughSpace;
resize(houghSpaceImage, enlargedHoughSpace, Size(cols * thetaScale, rows), 0, 0, INTER_LINEAR);
Mat colorHoughSpace;
applyColorMap(enlargedHoughSpace, colorHoughSpace, COLORMAP_JET);

namedWindow("Enhanced Hough Space", WINDOW_NORMAL);
imshow("Enhanced Hough Space", colorHoughSpace);
imwrite(file_name, colorHoughSpace);
waitKey(0);
}
  • DrawLineDetectedImage(Mat& Image, vector<vector>& PS_AfterFilter, int row_max, int theta_max , int second_filter_threshold): 将二维参数空间矩阵PS_AfterFilter进行更深层次的阈值筛选(阈值更大,筛选剩下的点更少),然后反变换到”x,y直角坐标系”,描绘出检测出的直线。
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
/*
DrawDetectedImage:
二次过滤参数空间(row,theta)矩阵,然后在原图image上描绘出直线。
*/
void ToughTransform::DrawLineDetectedImage(Mat& Image, vector<vector<int>>& PS_AfterFilter, int row_max, int theta_max = 180, int second_filter_threshold = 100)
{
//threshold-filter-second
vector<vector<int>> PS_FinalFilter(2 * static_cast<int>(ceil(row_max)),
vector<int>(theta_max, 0));

for (int i = 0; i < PS_AfterFilter.size(); i++){
for (int j = 0; j < PS_AfterFilter[0].size(); j++){
if(PS_AfterFilter[i][j] >= second_filter_threshold)
PS_FinalFilter[i][j] = PS_AfterFilter[i][j];
else
PS_FinalFilter[i][j] = 0;
}
}
ShowParameterSpace(PS_FinalFilter,"source/line-result/EnhancedHoughSpaceImage-FinalFilter.png");

int rows = PS_FinalFilter.size();
int cols = PS_FinalFilter[0].size();
for (int row = 0; row < rows; row++) {
for (int theta = 0; theta < cols; theta++) {
if (PS_FinalFilter[row][theta] != 0)
{
double this_theta_rad = theta * PI / 180.0;
double this_row = row - row_max;

Point pt1, pt2;
double a = cos(this_theta_rad);
double b = sin(this_theta_rad);
double x0 = a * this_row;
double y0 = b * this_row;
pt1.x = cvRound(x0 + 1000 * (-b));
pt1.y = cvRound(y0 + 1000 * (a));
pt2.x = cvRound(x0 - 1000 * (-b));
pt2.y = cvRound(y0 - 1000 * (a));

line(Image, pt1, pt2, Scalar(0, 0, 255), 2, LINE_AA);
}
}
}

namedWindow("Detected Lines", WINDOW_NORMAL);
imshow("Detected Lines", Image);
imwrite("source/line-result/DetectedLinesImage.png", Image);
waitKey(0);
}
  • 调试方法: 调整阈值second_filter_threshold直到合适。如果阈值太低,描绘的直线就会过多,图上一片混乱;如果阈值太高,则不会有检测结果。

测试

输入测试图片,主函数调用LineDetection(Mat& Image)即可。

  • 测试图像如下。
  • 霍夫变换并初步阈值过滤(threshold=10)后,检测出的直线二维参数空间(row,theta)图像。
  • 二次深度阈值过滤(threshold=500)后,检测出的直线二维参数空间(row,theta)图像。
  • 反变换到直角坐标,原图上描绘出检测到的直线。

圆检测

  • CircleDetection(Mat& Image): 算法主流程。包括将图像转化到三维参数空间(a,b,r),其中圆心坐标为(a,b),圆的半径是r。对参数空间矩阵进行初次阈值过滤,以及调用ShowParameterSpace_Circle和DrawCircleDetectedImage方法。
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
/*
CircleDetection: ShowParameterSpace_Circle + DrawCircleDetectedImage
*/
void ToughTransform::CircleDetection(Mat& Image){
//在霍夫变换中,我们通过三个参数来描述一个圆:圆心的坐标(a,b)和半径r.
int img_width = Image.size().width;
int img_height = Image.size().height;
//ParameterSpace[b][a][r] b--->height--->y | a--->width--->x
int max_a = img_width;
int max_b = img_height;
int max_r = static_cast<int>(0.5* min(max_a,max_b));
vector<vector<vector<int>>> ParameterSpace(max_b,
vector<vector<int>>(max_a,
vector<int>(max_r)
));

for (int y = 0; y < img_height; y++){
for (int x = 0; x < img_width; x++){
int B = Image.at<Vec3b>(y,x)[0];
int G = Image.at<Vec3b>(y,x)[1];
int R = Image.at<Vec3b>(y,x)[2];
if(B == 0 && G == 0 && R == 0){ //Edge is black-pixel
//遍历r,计算row,存入参数空间: min_r = 20 --> max_r
for(int r = 20; r <= max_r; r++){
//遍历每个r情况下的theta(一周),计算a&b,存入参数空间
for(int theta = 0; theta < 360; theta++){
double theta_rad = theta * PI / 180.0;
int a = static_cast<int>(x - r * cos(theta_rad));
int b = static_cast<int>(y - r * sin(theta_rad));
if(a >= 0 && a < max_a && b >= 0 && b < max_b){
ParameterSpace[b][a][r]++;
}
}
}
}
}
}

//threshold-filter
int threshold = 10;
vector<vector<vector<int>>> PS_AfterFilter(max_b,
vector<vector<int>>(max_a,
vector<int>(max_r)
));
//threshold-filter
for (int i = 0; i < ParameterSpace.size(); i++){
for (int j = 0; j < ParameterSpace[0].size(); j++){
for (int k = 0; k < ParameterSpace[0][0].size(); k++){
if(ParameterSpace[i][j][k] >= threshold)
PS_AfterFilter[i][j][k] = ParameterSpace[i][j][k];
else
PS_AfterFilter[i][j][k] = 0;
}
}
}

ShowParameterSpace_Circle(PS_AfterFilter,"source/circle-result/EnhancedHoughSpaceImage-FirstFilter.png");

const int second_filter_threshold = 300;
DrawCircleDetectedImage(Image,PS_AfterFilter,max_a,max_b,max_r,second_filter_threshold);
}
  • ShowParameterSpace_Circle(vector<vector<vector>>& PS_AfterFilter, string file_name, int thetaScale): 传入三维参数矩阵PS_AfterFilter,描绘图像。其中file_name是图片保存地址;thetaScale将theta轴拉伸,以达到更好的可视化效果。
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
void ToughTransform::ShowParameterSpace_Circle(vector<vector<vector<int>>>& PS_AfterFilter, string file_name, int thetaScale = 1){
int height = PS_AfterFilter.size(); //row的范围
int width = PS_AfterFilter[0].size(); // theta的范围
int num_r = PS_AfterFilter[0][0].size(); // 半径r的数量
Mat parameter_space_image(height, width, CV_8UC1, cv::Scalar(0));

for (int b = 0; b < height; b++) {
for (int a = 0; a < width; a++) {
for (int r = 0; r < num_r; r++) {
int accumulator_value = PS_AfterFilter[b][a][r];

// 将累积值映射到0-255的范围(可以根据实际情况调整比例)
int intensity = static_cast<int>(255.0 * accumulator_value / 255.0);
parameter_space_image.at<uchar>(b, a) = intensity;
}
}
}

Mat enlargedHoughSpace;
resize(parameter_space_image, enlargedHoughSpace, Size(width * thetaScale, height), 0, 0, INTER_LINEAR);
Mat colorHoughSpace;
applyColorMap(enlargedHoughSpace, colorHoughSpace, COLORMAP_JET);

namedWindow("Detected Circles", WINDOW_NORMAL);
imshow("Detected Circles", colorHoughSpace);
imwrite(file_name, colorHoughSpace);
waitKey(0);
}
  • DrawCircleDetectedImage(Mat& Image,vector<vector<vector>>& PS_AfterFilter, int max_a, int max_b, int max_r, int second_filter_threshold): 传入三维参数空间PS_AfterFilter,利用second_filter_threshold进行二次阈值筛选后,反变换到直角坐标。由于是圆检测,只需要取出圆心(x,y)和半径r的值,然后进行描绘即可。
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
void ToughTransform::DrawCircleDetectedImage(Mat& Image,vector<vector<vector<int>>>& PS_AfterFilter, int max_a, int max_b, int max_r, int second_filter_threshold){
//threshold-filter-second
vector<vector<vector<int>>> PS_FinalFilter(max_b,
vector<vector<int>>(max_a,
vector<int>(max_r)
));

for (int i = 0; i < PS_AfterFilter.size(); i++){
for (int j = 0; j < PS_AfterFilter[0].size(); j++){
for (int k = 0; k < PS_AfterFilter[0][0].size(); k++){
if(PS_AfterFilter[i][j][k] >= second_filter_threshold)
PS_FinalFilter[i][j][k] = PS_AfterFilter[i][j][k];
else
PS_FinalFilter[i][j][k] = 0;
}
}
}
ShowParameterSpace_Circle(PS_FinalFilter,"source/circle-result/EnhancedHoughSpaceImage-FinalFilter.png");

for (int i = 0; i < PS_FinalFilter.size(); i++){
for (int j = 0; j < PS_FinalFilter[0].size(); j++){
for (int k = 0; k < PS_FinalFilter[0][0].size(); k++){
//draw circle
if(PS_FinalFilter[i][j][k] != 0){
Point center(j, i);
circle(Image, center, k, cv::Scalar(0, 0, 255), 2); //红色圆圈
}
}
}
}

namedWindow("Detected Circles", WINDOW_NORMAL);
imshow("Detected Circles", Image);
imwrite("source/circle-result/DrawCircleDetectedImage.png", Image);
waitKey(0);
}
  • 调试方法:
    • 调整阈值second_filter_threshold直到合适。如果阈值太低,描绘的圆就会过多,图上一片混乱;如果阈值太高,则不会有检测结果。
    • 调整参数计算时,半径的遍历上下限的r_min和r_max。r_max一般直接取图像height和width中的较小值。r_min如果取0,那么有宽度的直线也会被检测成一长串的迷你圆;r_min取得太小,可能会把交点也检测成小圆。

测试

输入测试图片,主函数调用CircleDetection(Mat& Image)即可。

  • 测试图像如下。
  • 霍夫变换并初步阈值(threshold=10)过滤后,检测出的参数空间图像。
  • 二次深度阈值过滤(threshold=300)后,检测出的圆参数空间图像。
  • 反变换到直角坐标,原图上描绘出检测到的圆。

完整代码

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
#include <iostream> 
#include <string>
#include <sstream>
#include <ctime>
#include <vector>
#include <cmath>

#include <opencv2/core.hpp> // Basic OpenCV structures (cv::Mat)
#include <opencv2/videoio.hpp> // Video write
#include <opencv2/imgcodecs.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/videoio.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/video.hpp>

using namespace std;
using namespace cv;

const double PI = 3.14159265358979323846;

class ToughTransform{
public:
void LineDetection(Mat& Image);
void CircleDetection(Mat& Image);
private:
void ShowParameterSpace(vector<vector<int>>& PS_AfterFilter, string file_name, int thetaScale);
void DrawLineDetectedImage(Mat& Image, vector<vector<int>>& PS_AfterFilter, int row_max, int theta_max , int second_filter_threshold);
void ShowParameterSpace_Circle(vector<vector<vector<int>>>& PS_AfterFilter, string file_name, int thetaScale);
void DrawCircleDetectedImage(Mat& Image,vector<vector<vector<int>>>& PS_AfterFilter, int max_a, int max_b, int max_r, int second_filter_threshold);
};

/*
ShowParameterSpace:
描绘(row,theta)参数空间的图像。此时的参数矩阵PS_AfterFilter只经历了初步的阈值筛选。
*/
void ToughTransform::ShowParameterSpace(vector<vector<int>>& PS_AfterFilter, string file_name, int thetaScale = 15)
{
int rows = PS_AfterFilter.size();
int cols = PS_AfterFilter[0].size();

Mat houghSpaceImage(rows, cols, CV_8UC1, Scalar(0));
int maxVal = 0;
for (const auto& row : PS_AfterFilter) {
maxVal = max(maxVal, *max_element(row.begin(), row.end()));
}
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
houghSpaceImage.at<uchar>(i, j) = static_cast<uchar>(255.0 * PS_AfterFilter[i][j] / maxVal);
}
}
Mat enlargedHoughSpace;
resize(houghSpaceImage, enlargedHoughSpace, Size(cols * thetaScale, rows), 0, 0, INTER_LINEAR);
Mat colorHoughSpace;
applyColorMap(enlargedHoughSpace, colorHoughSpace, COLORMAP_JET);

namedWindow("Enhanced Hough Space", WINDOW_NORMAL);
imshow("Enhanced Hough Space", colorHoughSpace);
imwrite(file_name, colorHoughSpace);
waitKey(0);
}

/*
DrawDetectedImage:
二次过滤参数空间(row,theta)矩阵,然后在原图image上描绘出直线。
*/
void ToughTransform::DrawLineDetectedImage(Mat& Image, vector<vector<int>>& PS_AfterFilter, int row_max, int theta_max = 180, int second_filter_threshold = 100)
{
//threshold-filter-second
vector<vector<int>> PS_FinalFilter(2 * static_cast<int>(ceil(row_max)),
vector<int>(theta_max, 0));

for (int i = 0; i < PS_AfterFilter.size(); i++){
for (int j = 0; j < PS_AfterFilter[0].size(); j++){
if(PS_AfterFilter[i][j] >= second_filter_threshold)
PS_FinalFilter[i][j] = PS_AfterFilter[i][j];
else
PS_FinalFilter[i][j] = 0;
}
}
ShowParameterSpace(PS_FinalFilter,"source/line-result/EnhancedHoughSpaceImage-FinalFilter.png");

int rows = PS_FinalFilter.size();
int cols = PS_FinalFilter[0].size();
for (int row = 0; row < rows; row++) {
for (int theta = 0; theta < cols; theta++) {
if (PS_FinalFilter[row][theta] != 0)
{
double this_theta_rad = theta * PI / 180.0;
double this_row = row - row_max;

Point pt1, pt2;
double a = cos(this_theta_rad);
double b = sin(this_theta_rad);
double x0 = a * this_row;
double y0 = b * this_row;
pt1.x = cvRound(x0 + 1000 * (-b));
pt1.y = cvRound(y0 + 1000 * (a));
pt2.x = cvRound(x0 - 1000 * (-b));
pt2.y = cvRound(y0 - 1000 * (a));

line(Image, pt1, pt2, Scalar(0, 0, 255), 2, LINE_AA);
}
}
}

namedWindow("Detected Lines", WINDOW_NORMAL);
imshow("Detected Lines", Image);
imwrite("source/line-result/DetectedLinesImage.png", Image);
waitKey(0);
}

/*
LineDetection: ShowParameterSpace + DrawLineDetectedImage
*/
void ToughTransform::LineDetection(Mat& Image){
int img_width = Image.size().width;
int img_height = Image.size().height;
//ParameterSpace[row][theta]
double row_max = sqrt(img_width * img_width + img_height * img_height);
int theta_max = 180; //theta: step_length = 1
vector<vector<int>> PatameterSpace(2 * static_cast<int>(ceil(row_max)),
vector<int>(theta_max, 0));

cout << "ImageSize:" << img_height << "*" << img_width << endl;
cout << "PatameterSpace:" << 2*row_max << "*" << theta_max << " --> " << 2 * static_cast<int>(ceil(row_max)) << "*" << theta_max << endl;

for (int y = 0; y < img_height; y++){
for (int x = 0; x < img_width; x++){
int B = Image.at<Vec3b>(y,x)[0];
int G = Image.at<Vec3b>(y,x)[1];
int R = Image.at<Vec3b>(y,x)[2];
if(B == 0 && G == 0 && R == 0){ //Edge is black-pixel
//!!!look through theta, calc row than write into parameter-space!!!
//遍历theta,计算row,存入参数空间
for(int theta = 0; theta <= theta_max; theta++){
double theta_rad = theta * PI / 180.0;
double row = x * cos(theta_rad) + y * sin(theta_rad);
//row可能是负的,row_max作为偏移量. 将偏移量row_max加到row上,转为非负索引
//cout << "row = " << row << " ---> " << row + row_max << " ---> " << ceil(row) + ceil(row_max) << endl;
PatameterSpace[static_cast<int>(ceil(row) + ceil(row_max))][theta]++;
}
}
}
}

int threshold = 10;
vector<vector<int>> PS_AfterFilter(2 * static_cast<int>(ceil(row_max)),
vector<int>(theta_max, 0));
//threshold-filter
for (int i = 0; i < PatameterSpace.size(); i++){
for (int j = 0; j < PatameterSpace[0].size(); j++){
if(PatameterSpace[i][j] >= threshold)
PS_AfterFilter[i][j] = PatameterSpace[i][j];
else
PS_AfterFilter[i][j] = 0;
}
}
ShowParameterSpace(PS_AfterFilter,"source/line-result/EnhancedHoughSpaceImage-FirstFilter.png");

int second_filter_threshold = 500;
DrawLineDetectedImage(Image,PS_AfterFilter,static_cast<int>(ceil(row_max)),theta_max,second_filter_threshold);
}

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

void ToughTransform::ShowParameterSpace_Circle(vector<vector<vector<int>>>& PS_AfterFilter, string file_name, int thetaScale = 1){
int height = PS_AfterFilter.size(); //row的范围
int width = PS_AfterFilter[0].size(); // theta的范围
int num_r = PS_AfterFilter[0][0].size(); // 半径r的数量
Mat parameter_space_image(height, width, CV_8UC1, cv::Scalar(0));

for (int b = 0; b < height; b++) {
for (int a = 0; a < width; a++) {
for (int r = 0; r < num_r; r++) {
int accumulator_value = PS_AfterFilter[b][a][r];

// 将累积值映射到0-255的范围(可以根据实际情况调整比例)
int intensity = static_cast<int>(255.0 * accumulator_value / 255.0);
parameter_space_image.at<uchar>(b, a) = intensity;
}
}
}

Mat enlargedHoughSpace;
resize(parameter_space_image, enlargedHoughSpace, Size(width * thetaScale, height), 0, 0, INTER_LINEAR);
Mat colorHoughSpace;
applyColorMap(enlargedHoughSpace, colorHoughSpace, COLORMAP_JET);

namedWindow("Detected Circles", WINDOW_NORMAL);
imshow("Detected Circles", colorHoughSpace);
imwrite(file_name, colorHoughSpace);
waitKey(0);
}

void ToughTransform::DrawCircleDetectedImage(Mat& Image,vector<vector<vector<int>>>& PS_AfterFilter, int max_a, int max_b, int max_r, int second_filter_threshold){
//threshold-filter-second
vector<vector<vector<int>>> PS_FinalFilter(max_b,
vector<vector<int>>(max_a,
vector<int>(max_r)
));

for (int i = 0; i < PS_AfterFilter.size(); i++){
for (int j = 0; j < PS_AfterFilter[0].size(); j++){
for (int k = 0; k < PS_AfterFilter[0][0].size(); k++){
if(PS_AfterFilter[i][j][k] >= second_filter_threshold)
PS_FinalFilter[i][j][k] = PS_AfterFilter[i][j][k];
else
PS_FinalFilter[i][j][k] = 0;
}
}
}
ShowParameterSpace_Circle(PS_FinalFilter,"source/circle-result/EnhancedHoughSpaceImage-FinalFilter.png");

for (int i = 0; i < PS_FinalFilter.size(); i++){
for (int j = 0; j < PS_FinalFilter[0].size(); j++){
for (int k = 0; k < PS_FinalFilter[0][0].size(); k++){
//draw circle
if(PS_FinalFilter[i][j][k] != 0){
Point center(j, i);
circle(Image, center, k, cv::Scalar(0, 0, 255), 2); //红色圆圈
}
}
}
}

namedWindow("Detected Circles", WINDOW_NORMAL);
imshow("Detected Circles", Image);
imwrite("source/circle-result/DrawCircleDetectedImage.png", Image);
waitKey(0);
}

/*
CircleDetection: ShowParameterSpace_Circle + DrawCircleDetectedImage
*/
void ToughTransform::CircleDetection(Mat& Image){
//在霍夫变换中,我们通过三个参数来描述一个圆:圆心的坐标(a,b)和半径r.
int img_width = Image.size().width;
int img_height = Image.size().height;
//ParameterSpace[b][a][r] b--->height--->y | a--->width--->x
int max_a = img_width;
int max_b = img_height;
int max_r = static_cast<int>(0.5* min(max_a,max_b));
vector<vector<vector<int>>> ParameterSpace(max_b,
vector<vector<int>>(max_a,
vector<int>(max_r)
));

for (int y = 0; y < img_height; y++){
for (int x = 0; x < img_width; x++){
int B = Image.at<Vec3b>(y,x)[0];
int G = Image.at<Vec3b>(y,x)[1];
int R = Image.at<Vec3b>(y,x)[2];
if(B == 0 && G == 0 && R == 0){ //Edge is black-pixel
//遍历r,计算row,存入参数空间: min_r = 20 --> max_r
for(int r = 20; r <= max_r; r++){
//遍历每个r情况下的theta(一周),计算a&b,存入参数空间
for(int theta = 0; theta < 360; theta++){
double theta_rad = theta * PI / 180.0;
int a = static_cast<int>(x - r * cos(theta_rad));
int b = static_cast<int>(y - r * sin(theta_rad));
if(a >= 0 && a < max_a && b >= 0 && b < max_b){
ParameterSpace[b][a][r]++;
}
}
}
}
}
}

//threshold-filter
int threshold = 10;
vector<vector<vector<int>>> PS_AfterFilter(max_b,
vector<vector<int>>(max_a,
vector<int>(max_r)
));
//threshold-filter
for (int i = 0; i < ParameterSpace.size(); i++){
for (int j = 0; j < ParameterSpace[0].size(); j++){
for (int k = 0; k < ParameterSpace[0][0].size(); k++){
if(ParameterSpace[i][j][k] >= threshold)
PS_AfterFilter[i][j][k] = ParameterSpace[i][j][k];
else
PS_AfterFilter[i][j][k] = 0;
}
}
}

ShowParameterSpace_Circle(PS_AfterFilter,"source/circle-result/EnhancedHoughSpaceImage-FirstFilter.png");

const int second_filter_threshold = 300;
DrawCircleDetectedImage(Image,PS_AfterFilter,max_a,max_b,max_r,second_filter_threshold);
}



int main(){

ToughTransform handle;
//Mat img = imread("source/line.png");
//LineDetection(img);

//Mat img2 = imread("source/testline_cross.png");
//LineDetection(img2);

Mat img3 = imread("source/line_circle.png");
//handle.LineDetection(img3);
handle.CircleDetection(img3);

return 0;
}
评论
  • 按正序
  • 按倒序
  • 按热度
Powered by Waline v2.15.8