Hough提取边缘直线

来源:互联网 发布:常见的网络攻击类型有 编辑:程序博客网 时间:2024/06/04 19:59

1.    提取图像的边缘

原理:采用Sobel算子对图像进行卷积运算.

其中Sobel算子为:


分别分为水平方向的边缘提取以及竖直方向上的边缘提取。提取这两个方向进一步通过下面这条公式进行计算该像素点的灰度值。

 

对于几个样例的图像,由于图像高清,像素区间比较大,边缘提取方法步骤如下:

1)           首先对图像进行灰度化处理,即Gray= (R + G + B) / 3;

2)           再次对图像进行均值滤波,模糊处理

3)           采用Sobel算子提取边缘

 

由于原图像还是存在比较多的噪声,而我们的目标是提取图像的边缘,故再设置一个阈值,大于这个阈值则置为255,反之则置为0,使输出的图像为二值图像。

 

样例1输入:


样例1输出:

 

样例2输入:


样例2输出:

 

补充说明:由于原图像为2966x4000,而显示屏的分辨率为1366x768,故处理得到的图像在显示时发生一定的形变。


2.    计算A4纸张边缘的直线方程

方法:采用Hough变换检测直线。

Hough变换原理:对于任意一个点x(x0,y0),假设一条直线穿过该点,则有


那么可以转换为:


构建一个m-b的坐标系,统计对于任意一个(m0 ,b0  )直线,穿过这根直线上的(x,y)点的个数;对于(m0 ,b0  ),我们最终得到的是点的个数的集合(该集合记录了穿过该直线的每个点对应的坐标)。

最后通过设置一个阈值来筛选目标直线,得到对应的点集合,那么该点集合上的所有的点都位于同一条直线上。

 

承接上面样例测试的结果

样例1输出:


样例2输出:



代码如下:

#include "CImg.h"#include<vector>using namespace cimg_library;#define pi 3.1415926// Sobel算子int filter_hor[3][3] = {{-1, 0, 1}, {-2, 0, 2}, {-1, 0, 1}};int filter_ver[3][3] = {{-1, -2, -1}, {0, 0, 0}, {1, 2, 1}};const int threshold = 80; // 图像边缘提取的阈值const int threshold_line = 900; // 图像直线方程提取的阈值const int ave = 4; // 均值滤波的大小const int angle = 180; // 直线角度区间int npp[180][2967]; // 用于记录穿过m-p直线的点的个数// 记录点的坐标struct Pixel {int x;int y;Pixel(int a, int b) {x = a;y = b;}};// 用于存储点的集合std::vector<Pixel> v[180][2967];int main() {CImg<unsigned char> src("test1.bmp");memset(npp, 0, sizeof(npp));int m, n, i, j, temp_hor, a, b, temp_ver, temp;int src_h = src.height();int src_w = src.width();// 图像灰度化处理CImg<unsigned char> src_gray(src_w, src_h, 1, 1, 0);for (i = 0; i < src_w; i++) {for (j = 0; j < src_h; j++) {src_gray(i, j, 0) = (src(i, j, 0) + src(i, j, 1) + src(i, j, 2)) / 3;}}// 图像进行均值滤波CImg<unsigned char> src_gray_ave(src_gray);CImg<unsigned char> edge(src_w, src_h, 1, 1, 0);int ave_double = (2 * ave + 1) * (2 * ave + 1);for (i = ave; i < src_w - ave; i++) {for (j = ave; j < src_h - ave; j++) {temp = 0;a = 0;for (m = i - ave; m <= i + ave; m++, a++) {for (n = j - ave, b = 0; n <= j + ave; n++, b++) {temp += src_gray(m, n, 0);}}temp = (int) temp / ave_double;src_gray_ave(i, j, 0) = temp;}}// 图像通过Sobel算子进行边缘提取for (i = 1; i < src_w - 1; i++) {for (j = 1; j < src_h - 1; j++) {temp_hor = 0;temp_ver = 0;a = 0;for (m = i - 1; m <= i + 1; m++, a++) {for (n = j - 1, b = 0; n <= j + 1; n++, b++) {temp_hor += src_gray_ave(m, n, 0) * filter_hor[a][b];temp_ver += src_gray_ave(m, n, 0) * filter_ver[a][b];}}temp = (int) sqrt(temp_hor * temp_hor + temp_ver * temp_ver);if (temp > threshold) temp = 255;else temp = 0;edge(i, j, 0) = temp;}}double sin_value[angle];double cos_value[angle];for (int i = 0; i < angle; i++) {sin_value[i] = sin(i * pi / angle);cos_value[i] = cos(i * pi / angle);}int p;int mp = (int) (sqrt(src_h * src_h + src_w * src_w) + 0.5); // 计算图像的对角线for (i = 0; i < src_w; i++) {for (j = 0; j < src_h; j++) {if(edge(i, j, 0) == 255) { // 将边缘检测的图像中像素值为255点提取出来for (m = 0; m < angle; m++) {p = (int) (i * cos_value[m] + j * sin_value[m]);p = p / 2 + mp / 2;npp[m][p]++;v[m][p].push_back(Pixel(i, j)); // 对应的m-p直线的点集合,加入该点}}}}CImg<unsigned char> dest(src_w, src_h, 1, 3, 0);for (i = 0; i < angle; i++) {for (j = 0; j < mp; j++) {if (npp[i][j] > threshold_line) { // 如果该直线的点集合超过阈值,则为目标直线.提取直线上的点显示到图像中std::vector<Pixel> temp = v[i][j];for (m = 0; m < temp.size(); m++) {dest(temp[m].x, temp[m].y, 0) = 255;}}}}CImgDisplay main_disp(edge, "src_image"), dest_disp(dest, "dest_img");while(!main_disp.is_closed() && !dest_disp.is_closed()) {main_disp.wait();dest_disp.wait();}return 0;}


1 0
原创粉丝点击