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

A4纸直线检测

输入图像

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

输出图像

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

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

这里写图片描述

代码文件

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

Hough.h文件:

#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文件:

#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); // 直线斜率
        
  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值