本文旨在从原理上实现Hough变换的算法流程
及时总结,方便复习,仅供参考…
就是要把直线在图上给画粗来
像这个亚子的
这样的图是不能直接用来做Hough变换的(Hough变换最好是用二值图来做),所以我们需要对它做一些处理。
先晒一下处理效果图
Img = imread('photo.jpg');
Img_gray = rgb2gray(Img);
Img_sobel = mysobel(Img_gray);
注:mysobel函数是自行编写的sobel算子处理函数 (后面给的函数是加上了高斯平滑滤波)
function img = mysobel(img)
Sobel1 = [ 1 2 1;
0 0 0;
-1 -2 -1];
Sobel2 = [-1 0 1;
-2 0 2;
-1 0 1];
img1 = conv2(img,Sobel1,'same');
img2 = conv2(img,Sobel2,'same');
img = sqrt(img1.^2+img2.^2);
img = (img - min(img(:)))./(max(img(:))-min(img(:)));
Img_bin = Binpro(Img_sobel);
注:此处的Binpro函数也是自己编写的二值化函数,可以设置保留的边缘点数目,上图是保留了全图像素数2%的边缘点
function Bin = Binpro(img)
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%% 调参rate,通过设置rate
%%% 可以很好的去除不必要的信息
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
rate = 0.98;
rank_list = sort(img(:));
listlen = length(rank_list);
t = rank_list(fix(rate*listlen));
[w ,h] = size(img);
Bin = img(i,j) >= t;
接下来就是我们的要做的提取直线了
简单的讲一下原理,我们知道在两点可以确定一条直线,过一点有无数条的直线,我们在这里给出角度的量化等级为360个等级,那么可以知道的是过一点会有360条直线(重叠的不算同一条),那么在这里可以给出直线的极坐标定义。
r = x*cos(θ)+y*sin(θ)
不过,这不是为了在平面直角坐标系下表示直线,而是为了通过对 θ 进行 1:361 赋值,得到一个点 (x,y)所能表示的那360条直线,以它的特征点(θ,r)绘画在Hough空间(就是 θ0r 坐标系),可以知道一个点(x,y)绘制出来的 θ – r 曲线,应该是一条三角函数的曲线,对所有二值图内灰度不为零的点进行这样的操作,我们可以得到很多这样的三角函数曲线,而这些曲线各自相交的点(θ0,r0)所对应的直线由下式给定。
y = -x*cos(θ0)/sin(θ0)+r0/sin(θ0)
这样我们就得到了通过在Hough空间相交曲线的交点,得到了过两个点的直线,而有越多的曲线相交于这一点,则表示这条直线穿过了原图越多的点。也就完成了原图直线的检测。
下面我们来实现这个算法
%建立Hough空间将点对应曲线进行映射
f = 361;
%初始化Hough空间
Hough = zeros(f,fix(sqrt(m^2+n^2))+1);
for i = 1:m
for j = 1:n
if Img_bin(i,j) ~= 0
for a = 1:f
%%% 对 r = x*cos(θ)+y*sin(θ) 公式的使用
a0 = a*2*pi/f;
r = i*cos(a0) + j*sin(a0);
if r >0
%%% 在0-360的角度变化下,极径r的值应该是大于0的
r = fix(r)+1;
Hough(a,r) = Hough(a,r) + 1; %%通过遍历绘制曲线
end
end
end
end
end
Hough0 = Hough;
Hough = uint8(Hough); %%% 为了显示Hough空间曲线绘制好的图,我们将它转化为可显示的类型,仅仅是为了显示
变换到Hough空间下,那些亮点就是代表着原图的较长的直线(因为它压过了很多的点),同时可以看出其实亮的一片区域在实际的图像上我们只需要一个点来表示那条直线就够了,所以我们在后面提取点的时候,需要有一个邻域检测的动作,来实现只取一个点的想法。
将高幅值的那些点取出来,这里是使用了空间下各点幅值排序的总数第0.9999倍的那个位置幅值作为标准。
[M,N] = size(Hough);
H.r = [];
H.a = [];
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%% 调参cut_point
%%% 这个是选取在Hough空间高幅值点的标准
%%% 这里选取的是幅值从小到大排序,后0.1%的一些点点
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
cut_point = 0.9999;
temp = sort(Hough0(:));
cut = temp(fix(length(temp)*cut_point));
%取出符合要求的点的r,a
for i = 1:M
for j = 1:N
if Hough0(i,j)>cut && compwith(i,j,H.a,H.r,20000)
H.a = [H.a,i];
H.r = [H.r,j];
end
end
end
注:compwith函数就是在上文提及的邻域检测函数
function compwith = compwith(x,y,listx,listy,focus)
temp = 0;
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%% 调参focus
%%% 这是决定邻域半径的参数
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
for i = 1:length(listx)
if (listx(i) - x)^2 + (listy(i) - y)^2 < focus
temp = temp +1;
end
end
if temp >0
compwith = 0;
else
compwith = 1;
end
至此,我们已经从Hough空间取到了用来绘制直线的 r 和 θ 参数
for i = 1:length(H.a)
for i0 =1:m
a0 = H.a(i)*pi/180;
j0 = -i0*cos(a0)/sin(a0)+H.r(i)/sin(a0);
j0 = ceil(j0)+1;
if j0 < n && j0 > 0 %% 约束 y的取值在矩阵的索引内
for z = 1:4
%绘制显眼的线,这里用红色的
Img(i0+z,j0,1)= 255;
Img(i0+z,j0,2)= 0;
Img(i0+z,j0,3)= 0;
end
end
end
end
imshow(Img)
线有点歪,可能和二值化图有关,后续再优化吧
以下为主程序以及涉及的函数清单
主函数
main.m
高斯滤波+sobel算子卷积
function img = G_mysobel(img)
二值化
function Bin = Binpro(img)
邻域比较
function compwith = compwith(x,y,listx,listy,focus,hough0)
Hough直线检测
function [hough,chackline] = chackline(after_bin,cimg)
完整源码:
https://download.csdn.net/download/qq_45083791/12509421