Вот реализация алгоритма Рамера – Дугласа – Пекера, предложенная выше в комментарии Криса Луенго.
Это полное редактирование первой версии ответа , в которой edge
использовалось для определения границ объекта. Как отметил Крис Луенго в комментарии, bwboundaries
- лучший выбор для двоичных изображений. bwboundaries
возвращает отсортированные точки, что значительно упрощает код.
Следующий код выполняет следующие действия:
1) найдите края вашего объекта, используя bwboundaries. Они уже отсортированы.
2) Используйте алгоритм Рамера – Дугласа – Пекера, чтобы упростить список точек.
Поскольку для отладки мне потребовались визуальные подсказки, код открывает рисунок, который показывает, что происходит.
Обратите внимание, что код далек от надлежащего тестирования.
img = [...
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 5 5 5 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 5 5 5 5 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 5 5 5 5 5 5 5 0 0 0 0 0 0 0 0
0 0 0 5 5 5 5 5 5 5 5 5 0 0 0 0 0 0 0
0 0 0 5 5 5 5 5 5 5 5 5 5 5 0 0 0 0 0
0 0 0 5 5 5 5 5 5 5 5 5 5 5 5 5 0 0 0
0 0 0 5 5 5 5 5 5 5 5 5 5 5 5 5 5 0 0
0 0 0 5 5 5 5 5 5 5 5 5 5 5 5 0 0 0 0
0 0 0 5 5 5 5 5 5 5 5 5 0 0 0 0 0 0 0
0 0 0 5 5 5 5 5 5 5 5 0 0 0 0 0 0 0 0
0 0 0 0 5 5 5 5 5 5 5 0 0 0 0 0 0 0 0
0 0 0 0 5 5 5 5 5 5 5 0 0 0 0 0 0 0 0
0 0 0 5 5 5 5 5 5 5 0 0 0 0 0 0 0 0 0
0 0 0 5 5 5 5 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 5 5 5 5 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 5 5 5 5 5 0 0 0 0 0 0 0 0 0 0 0
0 0 0 5 5 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0];
watch = true;
if watch
f = figure;
subplot(1,2,1);
imagesc(img);
end
% first, find the edges
b = bwboundaries(img);
% note that the first and the last point are the same.
% However they are already sorted.
x = b{1}(:,1);
y = b{1}(:,2);
edges = zeros(size(img));
edges(sub2ind(size(e), x,y)) = 1;
if watch
ax = subplot(1,2,2);
img_h = imagesc(edges);
hold on;
end
title('Performing Douglas-Peucker algorithm');
% Omit the last point for the algorithm.
points = l_DouglasPeucker( [x(1:end-1), y(1:end-1)], 1, watch);
title('Final result');
plot([points(:,2); points(1,2)], [points(:,1); points(1,1)]);
function res = l_DouglasPeucker( points, ep, watch )
if nargin < 3
watch = false;
end
if watch
subplot(1,2,2);
hold on;
hp = plot(points(:,2), points(:,1), 'ko-');
hp2 = plot([points(1,2) points(end,2)], [points(1,1) points(end,1)], 'r-');
end
distances = zeros(size(points,1),1);
for i = 1:size(points,1)
distances(i) = l_distance_to_line(points(1,:), points(end,:), points(i,:));
end
idx = find(distances == max(distances),1);
if watch
hp4 = plot(points(idx,2), points(idx,1), 'mo', 'MarkerFaceColor', [1,1,1]);
pause(.5);
delete(hp);
delete(hp2);
delete(hp4);
end
if max(distances) > ep
res = [l_DouglasPeucker(points(1:idx,:), ep, watch); l_DouglasPeucker(points(idx:end,:), ep, watch)];
else
res = [points(1,:); points(end,:)];
end
end
function d = l_distance_to_line(p1,p2,p)
% computes the distance of p to the line through by p1 and p2
% There might be much better implementations of this...
% we need 3-dimensional data for this
p1 = [p1(1), p1(2), 0];
p2 = [p2(1), p2(2), 0];
p = [p(1,1) p(1,2) 0];
a = p2 - p1;
b = p - p1;
d = norm(cross(a,b)) ./ norm(a);
end