8.霍夫變換:線條——動手編碼、霍夫演示_4
目錄
動手編碼
霍夫演示
動手編碼
我們將在這裡花一分鐘來演示一下,如何使用Matlab構建霍夫變換。
我再重複一遍,在你們的習題集上,你們要做一些Hough程式碼。
你不能使用已經存在的Hough實現,也不能使用其他任何人的Hough實現。
因為事實證明,當你去寫你的Hough實現的時候某些東西會失效。
在這種經歷中,你會了解到它的重要元素是什麼。
趕快自己編寫一個吧。
程式碼如下:
>> function [ Hough, theta_range, rho_range ] = naiveHough(I) % NAIVEHOUGH Peforms the Hough transform in a straightforward way. [rows, cols] = size(I); theta_maximum = 180; rho_maximum = floor(sqrt(rows^2 + cols^2)) - 1; theta_range = -theta_maximum:theta_maximum - 1; rho_range = -rho_maximum:rho_maximum; Hough = zeros(length(rho_range), length(theta_range)); for row = 1:rows for col = 1:cols if I(row, col) > 0 %only find: pixel > 0 x = col - 1; y = row - 1; for theta = theta_range rho = round((x * cosd(theta)) + (y * sind(theta))); %approximate rho_index = rho + rho_maximum + 1; theta_index = theta + theta_maximum + 1; Hough(rho_index, theta_index) = Hough(rho_index, theta_index) + 1; end end end end
霍夫演示
為了檢測影象中的線條,我們首先需要找到邊緣畫素。
讓我們載入一個影象,把它轉換成灰度,然後使用Canny運算找到邊緣畫素。
>> %% Load image, convert to grayscale and apply Canny operator to find edge pixels >> img = imread('shapes.png'); >> grays = rgb2gray(img); >> edge = edge(grays, 'canny'); >> >> figure , imshow(img), title('original image'); >> figure , imshow(grays), title('Grayscale'); >> figure , imshow(edge), title('Edge pixels');
讓我們看看這些是什麼樣子。
這是原始影象:
如你所見,它有一些線條。
灰度版本:
邊緣畫素:
現在我們將使用霍夫變換方法來找到直線。為此,我們將使用Matlab中的霍夫函式。
>> %% Apply Hough transform to find candidate lines
>> [accum theta rho] = hough(edges); % Matlab
在Matlab文件中可以找到關於這個函式的更多資訊:
Octave中的houghtf函式也是同等作用:
第一個返回的值是累加器陣列。(一般我們命名為:accum)
第二個是值或角度的向量。(一般我們命名為:theta)
第三個是半徑的向量。(一般我們命名為:rho)
我們看看這是什麼樣子。
>> figure, imagesc(accum, 'XDdata', theta, 'YDdata', rho), title('Hough accumulator');
我們通過 theta 和 rho 值來正確標記每個軸,rho或從原點的距離是沿著Y軸:
角度在x軸上,從 -90° 到 +90°:
好了,我們來找出這個累加器陣列中的最大值,我們通過100作為我們感興趣的最大值:
>> %% Find peaks in the Hough accumulator matrix
>> peaks = houghpeaks(accum, 100); % Matlab
注意,在Octave中需要使用immaximize函式。
讓我們在霍夫累加器陣列上,畫出這些最大值:
>> hold on; plot(theta(peaks(:, 2)), rho(peaks(: ,1)), 'rs'); hold off;
請注意,我們需要使用 和 ρ 值來正確地繪製最大值:
最大值由紅色小框標記。最大值向量的大小是13 * 2:
>> size(peaks);
執行結果:13 2
發現了13個最大值,每行包含一個最大值的位置。
第一列具有行值或 y 值,第二列具有 x 值。
使用這些最大值,我們可以找到線段,在Matlab中使用Hough線函式:
>> %% Find lines (segments) in the image.
>> line_segs = houghlines(edges, theta, rho, peaks); % Matlab
>> lin_segs;
程式碼執行,結果如下:
看起來發現了28條線段。
LangySeg中的每個元素是一個結構,其中兩個端點 和 ρ 值。讓我們畫出這些線段:
>>
figure, imshow(img), title('Line segments');
hold on;
for k = 1:length(line_segs)
endpoints = [line_segs(k).point; line_segs(k).point2];
plot(endpoints(:, 1), endpoints(:, 2), 'LineWidth', 2, 'Color', 'green');
end
hold off;
程式碼執行結果:
正如你所看到的,大多數較長的線段已經被檢測到,但是很多雜散的線段也出現了。
那麼,我們如何才能得到更好的結果呢?
讓我們再看一下邊緣畫素:
我們注意到,在一些區域,在較長的線路上有中斷:
還有一組密集的曲線可能會讓霍夫探測器偏離軌道:
為了找到一組更清晰或更有意義的線條,我們可以做很多事情。
例如,我們可以增加霍夫最大值的閾值引數:
>> %% Alt.:More precise lines
>> peaks = houghpeaks(accum, 100, 'Threshold', cell(0.6*max(accum(:))), 'NHoodSize', [5,5]);
>> size(peaks);
為了理解這些引數的含義,讓我們看一下這個函式的文件。
因此,闕值是累加器陣列中的最小值,即支援一行的最小畫素數,該行需要作為有效候選。
不考慮具有較少畫素的任何可能的線。
這裡我們將其設定為0.6乘以累加器陣列中的最大值,預設值為0.5乘以最大值。
鄰域大小定義了計算區域性最大值的區域。('NHoodSize', [5,5])
注意,這不是影象中的區域。
我們在累加器陣列中計算區域性最大值,因此在Rho和θ維中定義鄰域的大小。
如果鄰域大小為5度,則表示一條較強的線將會抑制其他相似但方向稍微偏離的線。
回想一下,我們在最後一次嘗試中發現了13個最大值。
這次我們只有7個最大值。程式碼結果如下:
讓我們看看這些峰值在哪裡。
>> figure, imagesc(theta, rho, accum), title('Hough accumulator');
>> hold on; plot(theta(peaks(:,2)), rho(peaks(:, 1)), 'rs'); hold off;
程式碼執行結果:
看起來我們可能會有更乾淨的結果。
讓我們將它與之前的累加器峰值進行比較:
我們看到以前在這一密集區域發現的許多最大值現在都消失了。
新的最大值聚集在三個主要的位置。
Okay,我們還能做什麼?
>> line_segs = houghlines(edges, theta, rho, peaks, 'FillGap', 50, 'MinLength', 100);
我們可以利用houghlines的引數。
我們把填充間隙引數增加到50('FillGap', 50),看看怎麼樣?
這是兩個段之間允許的最大畫素數,如果它們位於同一條線上,則計為一個畫素。
為了關注更長的線條,我們將最小長度增加到100畫素。('MinLength', 100)
要更好地理解這些引數,請參閱houghlines的文件。
好,讓我們看看新的片段是什麼樣的:
>>
figure, imshow(img), title('Line segments');
hold on;
for k = 1:length(line_segs)
endpoints = [line_segs(k).point1; line_segs(k).point2];
plot(endpoints(:, 1), endpoints(:, 2), 'LineWidth', 2, 'Color', 'green');
end
hold off;
程式碼執行,結果:
與之前的結果相比,我們發現雜散的已經基本被排除。(一些先前破碎的部分也被連線在一起。)
顯然,你可以更好地處理引數,特別是在這裡:
大家可以隨意使用huff變換函式,調整到最好的結果。
——學會編寫自己的程式碼,才能練出真功夫。