MATLAB 中求解包含所有數據點的最小凸多邊形

最近所做的一個項目中,需要求解包含所有已知點的最小凸多邊形,該問題可以通過遞歸地尋找最大內角從而尋找到最小凸多邊形的邊界,相信只要會一兩種語言,都可以順利地寫出代碼,所以這裏不對該算法思路進行探討,而是從 MATLAB 已有的 Delaunay 三角剖分算法出發,尋找所有剖分出的三角形中僅出現了一次的邊界,這些邊界就是其最小凸多邊形的邊界,最後再通過邊界跟蹤,即可鎖定最小凸多邊形的邊界順序,從而求解該問題。


該算法的總體思路如下:

1、利用 delaunay 函數,對所有數據點進行 Delaunay 三角剖分處理,delaunay 函數的返回值是一個 N * 3 的矩陣,其中 N 爲剖分出的三角形個數,3 爲每個三角形的三個端點的序號。例如,執行如下語句,可直觀地觀察剖分結果。

points = [0, 15, 30, 26, 26, 0; 0, 3, 10, 20, 12, 0];
triangles = delaunay(points(1, :), points(2, :));
triplot(triangles, points(1, :), points(2, :));

其中,triangles 是一個 4 * 3 矩陣:

triangles =

     5     3     4
     1     2     4
     2     5     4
     2     3     5



2、根據 triangles 矩陣,提取出所有 delaunay 三角剖分時所連接的邊,例如 triangles(1, :) = [5, 3, 4], 則其連接的邊有 [3, 4], [3, 5], [4,5]。依次掃描 triangles 矩陣的每一行,將 delaunay 三角剖分時所連接的邊添加到一個新的矩陣中,最後構成一個 M * 2 的矩陣,其中 M 是一共所連接的邊的條數。

3、顯然,直觀可見,最小凸多邊形上的邊應該僅在以上矩陣中出現一次,因此,將以上矩陣中那些出現次數超過一次的邊全部去掉,最後保留的便是最小凸多邊形的邊。

4、根據最小凸多邊形的邊,很容易得到構成最小凸多邊形的結點的順序,從而解決問題。


以下代碼綜合了以上所有步驟,輸入參數 points 是一個 2 * P 矩陣, P 爲數據點的個數,第一行是這些數據點對應的 x 座標,第二行是對應的 y 座標;輸出參數 polygon 是一個 2 * Q 矩陣, Q 爲凸多邊形的頂點個數(首尾相連),第一行是這些頂點對應的 x 座標,第二行是對應的 y 座標。可以在 這裏 下載相應文件。

function polygon = minimal_convex_polygon(points)
    % 進行 delaunay 三角剖分,將所有連接了的邊保存在矩陣 lines 中
	triangles = sort(delaunay(points(1, :), points(2, :)), 2);
	lines = zeros(size(triangles, 1) * 3, 2);
	for i = 1:size(triangles, 1)
		lines(3 * i - 2,:) = [triangles(i, 1), triangles(i, 2)];
		lines(3 * i - 1,:) = [triangles(i, 1), triangles(i, 3)];
		lines(3 * i,:) = [triangles(i, 2), triangles(i, 3)];
    end
    % 去掉 lines 中出現次數超過一次的邊
	[~, IA] = unique(lines, 'rows');
	lines = setdiff(lines(IA, :), lines(setdiff(1:size(lines, 1), IA), :), 'rows');
    % 跟蹤 lines 中的數據點,將凸多邊形的頂點編號保存在 seqs 中
	seqs = zeros(size(lines, 1) + 1,1);
	seqs(1:2) = lines(1, :);
	lines(1, :) = [];
	for i = 3:size(seqs)
		pos = find(lines == seqs(i - 1));
		row = rem(pos - 1, size(lines, 1)) + 1;
		col = ceil(pos / size(lines, 1));
		seqs(i) = lines(row, 3 - col);
		lines(row, :) = [];
    end
    % 根據 seqs , 得到凸多邊形頂點座標
	polygon = points(:, seqs);
end


測試:

points = [0, 15, 30, 26, 26, 0, 20, 10; 0, 3, 10, 20, 12, 0, 12, 4];
plot(points(1, :), points(2, :), '*r', 'LineWidth', 4);
polygon = minimal_convex_polygon(points);
hold on;
plot(polygon(1, :), polygon(2, :), 'LineWidth', 2);



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章