普通 A 4 打印纸,上面可能有手写笔记或者打印内容,但是拍照时可能角度不正。
- 图像的边缘
- 计算 A4 纸边缘的各直线方程
- 提取 A4 纸的四个角点
A4纸直线检测网上可以找到很多相关的内容,这里我就不做算法解释了,直接贴上代码。
Hough.h文件:
#include "CImg.h"
#include
#include
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 peaks; // 霍夫空间直线经过最多的点
vector lines; // 直线
vector 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文件:
#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文件:
#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得到结果。
- 图像的边缘
- 把图像中边缘拟合成圆, 圆周像素用红色像素标出
- 输出图像中硬币的数量
说实话,这个比较难,在作业ddl我并没做出来,所以我“犯规了”,用了python版本的opencv圆形霍夫变换函数进行检测,但调参(半径范围)也是一件比较痛苦的事情2333。
后面有大佬在作业ddl后完成了CImg检测圆形硬币这部分工作,献上我的膝盖。
运行指示:
在cmd中运行如下命令:
python circle_hough_transform.py 1.bmp
也可以修改文件名,改用其他文件,例如2.bmp
circle_hough_transform.py文件:
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:])
在Ex2作业(可见我前面的文章)的基础上进行修改,在这里我只把霍夫变换的代码给出来,代码包含了直线检测和圆形检测,只需要修改一下传入参数即可,全部代码文件可见github地址:
EdgeDetect.h文件:
#pragma once
#ifndef EDGE_DETECT_H
#define EDGE_DETECT_H
#include
#include
#include
#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; // 角点数
vectorint , int>> lines; // 检测到的直线集合
vector<int> lineWeight; // 累加矩阵
vector<int> sortLineWeight; // 从大到小排序的累加矩阵
CImg<float> edge; // 边缘直线
int circleNumber; // 检测圆个数
int minRadius; // 圆周最小半径
int maxRadius; // 圆周最大半径
vectorint , int>> circles; // 检测到的圆心集合
vectorint , int>> voteSet; // 投票集合
vectorint , int>> center; // 存放累加值最大的圆心对应坐标
vector<int> circleWeight; // 累加矩阵
vector<int> sortCircleWeight; // 从大到小排序的累加矩阵
};
#endif // !EDGE_DETECT_H
EdgeDetect.cpp文件:
#include "EdgeDetect.h"
#include "CANNY.h"
#include
#include
#include
#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>()); // 将累加矩阵从大到小进行排序
vectorint , 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文件:
#include "EdgeDetect.cpp"
#include "CANNY.cpp"
#include
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得到结果。
完。