参考MATLAB内置的霍夫变换函数hough实现。(《数字图像处理》课程实验4)
(公式待修改,一般第一个参数为rho,第二个参数为theta)
为实现彩色图像的直线检测,主要步骤可以概括为如下几步:
(1) 读取RGB三通道的彩色图像;
(2) 将RGB图像转化为灰度图像(可以将RGB转为HSI然后使用I通道作为灰度图像值);
(3) 使用prewitt、sobel等算子对灰度图像进行边缘检测,将图像二值化;
(4) 使用Hough变换检测法,统计Hough变换后的每个数据点出现次数,取最大的一些数据点对应的(rho,theta)作为直线方程参数,计算得到直线方程(公式为 xcos ysin);
(5) 在原图中绘制直线方程图像,实现彩色图像直线检测。
其中前三步已在实验一、实验二中完成,本实验可以直接调用相关代码。具体到Hough变换原理,可以用自然语言描述为:
(1) 根据公式 xcos ysin,将已经二值化的边缘检测图的每一个值为1(或255)的像素点(x, y)变换到(, );
(2) 确定与的分布间隔(“分辨率”,例如0.5个单位)以及分布范围,在-空间上绘制网格,并建立对应网格每个交点的数组;
(3) 对(1)中提到的每一个像素,对每一个,通过公式 xcos ysin计算出对应的,四舍五入后纳入交点,交点对应的数组位置递增1;
(4) 统计完成后,在统计使用的数组中根据需要选取一个阈值,取阈值之上的(, )结点。每个这样的结点就对应x-y空间的一条直线。
可参考(https://blog.csdn.net/kateyabc/article/details/79974622):每一个x-y空间的像素点代入公式 xcos ysin后,转而把看作纵坐标,看作横坐标。每取一个值,就能对应得到一个,一组(, )可以唯一确定x-y空间的经过点(x, y)的一条直线。如果若干条-空间中的曲线经过同一点(, ),则证明该点对应的x-y空间有一条直线穿过若干个像素点,(, )计算后 就可以得到这条直线的参数。
参照MATLAB内置函数hough实现。myhough与hough参数意义大体一致:BW为边缘图像(例如edge函数得到的结果),rhoResolution和thetaResolution分别为rho和theta的分辨率(myhough的rhoResolution和thetaResolution仅支持输入一个数字代表间隔)。具体使用可参照“help hough”或者2.2中的mydemo.m脚本文件。
function [h, theta, rho] = myhough(bw, rhoResolution, thetaResolution)
%MYHOUGH Hough transform.
% MYHOUGH is a weakened version of MATLAB built-in function hough. It
% implements the Standard Hough Transform using pure MATLAB code (using
% .m rather than .mex). For more infomation about hough transform, please
% enter "help hough" in the MATLAB command line. Besides, mydemo.m is a
% script demo for myhough (as well as hough).
[M,N] = size(bw);
theta = linspace(-90, 0, ceil(90/thetaResolution) + 1);
theta = [theta -fliplr(theta(2:end - 1))];
D = sqrt((M - 1)^2 + (N - 1)^2);
q = ceil(D/rhoResolution);
nrho = 2*q + 1;
rho = linspace(-q*rhoResolution, q*rhoResolution, nrho);
h = zeros(length(rho), length(theta));
t = theta*pi/180;
cost = cos(t);
sint = sin(t);
for y = 1:M
for x = 1:N
if bw(y, x)
r = (x-1)*cost + (y-1)*sint;
r = round(r/rhoResolution) + q + 1;
for k = 1:length(t)
h(r(k), k) = h(r(k), k) + 1;
end
end
end
end
参照《MATLAB中的Hough变换》(https://blog.csdn.net/dyq1995/article/details/86541518)实现。其中Hough变换函数、彩色图像转灰度图像、边缘检测等均提供了自主实现版本(直接调用了实验1、实验2编写的相关函数。这些函数也已在本次实验文件中附上,但调用前需要使用addpath命令将其添加到MATLAB的搜索路径)。
%% temporarily add custom functions needed to MATLAB search path
addpath('../experiment1/functions');
addpath('../experiment2/functions');
%% FILE -> RGB -> GRAY -> EDGE
RGB = imread('hf.jpg');
% using custom functions
% HSI = rgb2hsi(RGB);
% GRAY = HSI(:, :, 3);
% BW = myedge(GRAY, 'prewitt', 50);
% or using MATLAB built-in functions
GRAY = rgb2gray(RGB);
BW = edge(GRAY, 'prewitt');
% show RGB
subplot(2, 3, 1);
imshow(RGB);
title('Original image');
axis on;
% show GRAY
subplot(2, 3, 2);
imshow(GRAY);
title('Grayscale image (or intensity image)');
axis on;
% show EDGE
subplot(2, 3, 3);
imshow(BW);
title('Edges in intensity image (using prewitt operators with a threshold of 50)');
axis on;
%% Hough transform
[H, T, R] = myhough(BW, 0.5, 0.5);
% [H, T, R] = hough(BW, 'rhoResolution', 0.5, 'thetaResolution', 0.5);
% all(all(H==h));
subplot(2, 3, 4);
imshow(H, [], 'XData', T, 'YData', R, 'InitialMagnification', 'fit');
title('Hough transform');
xlabel('\theta'),ylabel('\rho');
axis on , axis normal, hold on;
colormap(gca, hot);
%% Hough peaks and Hough lines
% Hough peaks
P = houghpeaks(H, 20, 'threshold', ceil(0.3*max(H(:))));
x = T(P(:, 2));
y = R(P(:, 1));
% plot(x, y, 's', 'color', 'white');
plot(x, y, 'ws');
% Hough lines
lines = houghlines(BW, T, R, P, 'FillGap', 5, 'MinLength', 7);
% show Hough lines on the intensity image
subplot(2, 3, 5);
imshow(GRAY);
title('Straight line detection using Hough transform');
axis on;
hold on;
max_len = 0;
for k = 1:length(lines)
xy = [lines(k).point1;lines(k).point2];
plot(xy(:, 1), xy(:, 2), 'LineWidth', 2, 'Color', 'green');
plot(xy(1, 1), xy(1, 2), 'x', 'LineWidth', 2, 'Color', 'yellow');
plot(xy(2, 1), xy(2, 2), 'x', 'LineWidth', 2, 'Color', 'red');
len = norm(lines(k).point1 - lines(k).point2);
if(len > max_len)
max_len = len;
xy_long = xy;
end
end
plot(xy_long(:, 1), xy_long(:,2), 'LineWidth', 2, 'Color', 'cyan');
% show Hough lines on the original image
subplot(2, 3, 6);
imshow(RGB);
title('Straight line detection using Hough transform');
axis on;
hold on;
max_len = 0;
for k = 1:length(lines)
xy = [lines(k).point1;lines(k).point2];
plot(xy(:, 1), xy(:, 2), 'LineWidth', 2, 'Color', 'green');
plot(xy(1, 1), xy(1, 2), 'x', 'LineWidth', 2, 'Color', 'yellow');
plot(xy(2, 1), xy(2, 2), 'x', 'LineWidth', 2, 'Color', 'red');
len = norm(lines(k).point1 - lines(k).point2);
if(len > max_len)
max_len = len;
xy_long = xy;
end
end
plot(xy_long(:, 1), xy_long(:,2), 'LineWidth', 2, 'Color', 'cyan');
在输入均使用MATLAB内置函数生成的情况下(如边缘检测使用edge函数),通过使用如下代码对比自定义函数与MATLAB内置函数的计算结果:
[H, T, R] = myhough(BW, 0.5, 0.5);
[H, T, R] = hough(BW, 'rhoResolution', 0.5, 'thetaResolution', 0.5);
cmp = (H==h);
length(cmp(cmp==0))
输出结果为:
ans =
0
也可使用all命令:
all(all(H==h))
输出结果为:
ans =
logical
1
T和R(即Theta和Rho)的比较也是同理。
Hough变换本身原理并不难理解,从实现的代码行数来说也相对容易。对我个人而言,其主要消耗时间的地方在于考虑不同输入间隔时坐标的变换与映射。
根据前述几次实验的经验,本次实验之初便搜索查询Hough变换对应的MATLAB内置函数如何调用以及结果如何,以作为“标准答案”验证自己的实验结果。找到了一篇完全使用MATLAB内置函数实现Hough变换直线检测的博客(https://blog.csdn.net/dyq1995/article/details/86541518),于是意图将其中使用到的主要内置函数替换为自定义函数。
在具体的实现过程中,也参考了MATLAB内置函数hough的源代码(使用edit hough命令查看)。研读过后发现,除了输入参数处理外,源代码用MATLAB编写了theta与rho的间隔预处理,但具体的Hough变换实现使用了.mex(经查询为C或C++编译得到,无法直接访问)以加速计算。于是预处理部分参考了源代码,自己编写了Hough变换实现。然而初版代码运行结果与内置hough函数运行结果有较大差异(120万左右数据点有30万左右不同)。
经过各种修改仍然不起作用。于是临近实验提交时又拜读了Gonzalez教材关于Hough变换的部分,重新考虑源代码预处理的逻辑,使用诸如[1,0;1,0]的简单矩阵作为输入比照自己代码与内置函数的运行结果,最终发现自己代码的问题在于由输入的theta与rho的间隔带来的坐标变换问题。这之中很绕人的一个地方在于,MATLAB中图像的矩阵表示是从1开始的,然而平面直角坐标系仍然是从0开始。如果不作一个变换,rho的范围就会多算一部分。尽管其可能影响并不很大,但想要得到与内置hough函数一样的结果(且不浪费空间),就需要做这些更精细的坐标变换处理。最后也得到了与hough函数完全一致的计算结果。由于时间关系,houghpeaks和houghlines就没有自己实现了。
使用find函数进行包装。
是否可以拿到(rho, theta)对后,把直线数据先算出来,再对直线上的每个点算一下它在边缘检测图对应位置值是否为1(或者255),过滤掉所有的0值。剩下的点连接成若干线段,再根据设定的长度阈值,将超过长度阈值的线段绘制出来。