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

A4纸直线检测

输入图像

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

输出图像

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

如下图:由于原图太大,这里我截图放上来
Ex3:霍夫变换算法实现A4纸直线检测和硬币圆形检测(使用CImg库)_第1张图片

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

代码文件

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得到结果。

硬币圆形检测

输出图像

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

如下图:由于图片太大,这里我是截图放上来的
Ex3:霍夫变换算法实现A4纸直线检测和硬币圆形检测(使用CImg库)_第3张图片

代码文件

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

Python版本:

运行指示:
在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:])

CImg版本

在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得到结果。


完。

你可能感兴趣的:(计算机视觉)