计算机视觉作业三
A4纸直线检测
输入图像
普通 A 4 打印纸,上面可能有手写笔记或者打印内容,但是拍照时可能角度不正。
输出图像
- 图像的边缘
- 计算 A4 纸边缘的各直线方程
- 提取 A4 纸的四个角点
如下图:由于原图太大,这里我截图放上来
代码文件
A4纸直线检测网上可以找到很多相关的内容,这里我就不做算法解释了,直接贴上代码。
Hough.h文件:
1 | #include "CImg.h" |
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 | #include "Hough.cpp" |
编译运行指示
可以在main.cpp文件中修改要输入的bmp图像,然后在cmd中运行如下命令:
g++ -o main.exe main.cpp -O2 -lgdi32
进行编译,最后再运行main.exe得到结果。
硬币圆形检测
输出图像
- 图像的边缘
- 把图像中边缘拟合成圆, 圆周像素用红色像素标出
- 输出图像中硬币的数量
如下图:由于图片太大,这里我是截图放上来的
代码文件
说实话,这个比较难,在作业ddl我并没做出来,所以我“犯规了”,用了python版本的opencv圆形霍夫变换函数进行检测,但调参(半径范围)也是一件比较痛苦的事情2333。
后面有大佬在作业ddl后完成了CImg检测圆形硬币这部分工作,献上我的膝盖。
Python版本:
运行指示:
在cmd中运行如下命令:python circle_hough_transform.py 1.bmp
也可以修改文件名,改用其他文件,例如2.bmp
circle_hough_transform.py文件:
1 | import sys |
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 | #include "EdgeDetect.h" |
main.cpp文件:
1 | #include "EdgeDetect.cpp" |
编译运行指示
在cmd窗口下使用如下命令进行编译:
g++ -std=c++11 -o main.exe main.cpp -O2 -lgdi32
然后再运行main.exe得到结果。
完。