Соединение множества точек для получения не самопересекающегося невыпуклого многоугольника - PullRequest
0 голосов
/ 04 апреля 2019

У меня есть неупорядоченный набор точек 2D, который представляет углы здания. Мне нужно соединить их, чтобы получить план здания.

Точки были получены путем объединения разных полигонов, собранных разными людьми. Моя идея состоит в том, чтобы использовать эти многоугольники, чтобы привести точки в порядок (например, взять область между самым большим и самым маленьким многоугольниками и соединить точки так, чтобы они находились в этой области).

Я пытался использовать критерии минимального расстояния, а также соединять точки по углам. Но, к сожалению, это не работает. Одна полезная вещь, которую я имею, - это необработанные данные многих полигонов, в которых порядок точек правильный. Так есть ли возможность сравнить эти полигоны, чтобы соединить эти точки? Как я упоминал выше, мой профессор предложил взять самые большие и самые маленькие полигоны и использовать область между ними в качестве буфера. Все точки попадут в эту буферную зону. Но я не уверен, как это реализовать.

X = [364.533 372.267 397.067 408.133 382.471 379.533 329.250 257.200 199.412 195.267 184.385 168.643 157.533 174.500 108.533 99.333 150.733 184.800 138.105 179.474 218.278 232.133 267.714 306.929 312.143 357.733 421.333 431.000 371.867 364.533]; 
Y = [192.027 233.360 228.627 286.693 314.541 292.960 327.450 340.500 348.671 326.693 269.308 330.857 274.493 226.786 239.200 193.467 182.760 101.893 111.000 80.442 74.356 140.360 64.643 56.857 77.786 69.493 133.293 180.427 142.160 192.027];

enter image description here enter image description here

Ожидаемый результат - замкнутый многоугольник, представляющий план здания. У меня есть 15 примеров сборки, и код должен работать для всех. Некоторые здания не сохраняют критерии правильного угла между углами. Я прилагаю данные, которые у меня были. Точки, которые у меня есть, получены путем интегрирования многоугольников. Так есть ли способ использовать эти многоугольники (в которых точки в порядке) фактические данные до интеграции

Ответы [ 3 ]

6 голосов
/ 04 апреля 2019

РЕДАКТИРОВАТЬ

Итак, я мог бы найти решение, используя мою нижеприведенную идею.

Замечания: Я добавил одну отсутствующую точку вручную.И я удалил два крошечных уголка внизу.Либо это должно быть всего четыре угла, либо они могут рассматриваться как углы вообще.

Я прямо заявляю, что, поскольку моя идея включает в себя предположение, что углы обычно имеют угол 90 градусов.

Общий подход

  • Найдите порядок точек по методу, указанному ниже.
  • Для всех точек определите потенциальных «соседей» в пределах сотносительно найденного порядка.
  • Для каждых двух соседей определите угол между соседом # 1 - текущей точкой - соседом # 2.В идеале этот угол должен составлять 90 градусов.
  • Для всех возможных комбинаций найти ту, которая имеет минимальное общее расстояние, то есть расстояние (сосед # 1 - текущая точка) + расстояние (текущая точка - сосед # 2).

Я понял, что с помощью цикла for для всех точек, в результате чего все линии отрисовываются два раза.Кроме того, многие вычисления могут быть векторизованы и перенесены из цикла.Оптимизация не была моей целью прямо сейчас.; -)

% Point data of building corners; modified!
X = [285.400 372.267 397.067 408.133 382.471 379.533 199.412 195.267 184.385 168.643 157.533 174.500 108.533 99.333 150.733 184.800 138.105 179.474 218.278 232.133 267.714 306.929 312.143 357.733 421.333 431.000 371.867 364.533];
Y = [130.150 233.360 228.627 286.693 314.541 292.960 348.671 326.693 269.308 330.857 274.493 226.786 239.200 193.467 182.760 101.893 111.000 80.442 74.356 140.360 64.643 56.857 77.786 69.493 133.293 180.427 142.160 192.027];

% Place approximative center of building at (0, 0)
X = X - mean(X);
Y = Y - mean(Y);
C = [mean(X), mean(Y)];

% Sort points by angle with respect to center
[~, idx] = sort(atan2(X, Y));

% Rearrange points with respect to sorted angles
X = X(idx);
Y = Y(idx);

% Number of data points
n = numel(X);

% Calculate direction vectors for X and Y coordinates
dvX = repmat(X.', 1, n);
dvX = dvX - dvX.';
dvY = repmat(Y.', 1, n);
dvY = dvY - dvY.';

% Calculate distances
dst = sqrt(dvX.^2 + dvY.^2);

% Number of "neighbouring" points to be considered with respect to the order
nn = 8;


figure(1);
hold on;

% Center
plot(C(1), C(2), 'kx', 'MarkerSize', 15);

% Plain points
plot(X, Y, '.', 'MarkerSize', 15);

for k = 1:n

  % Index  
  text(X(k) + 0.05, Y(k) + 0.05, num2str(k), 'FontSize', 12);

  % Set up neighbourhood  
  nbh = mod([k-nn/2:k-1 k+1:k+nn/2], n);
  nbh(nbh == 0) = n;

  % Calculate angles and total distance arrays
  ang = Inf(nn);
  len = Inf(nn);
  for ii = 1:nn
    l = nbh(ii);
    d1 = [dvX(k, l) dvY(k, l)];
    for jj = ii+1:nn
      m = nbh(jj);
      d2 = [dvX(k, m) dvY(k, m)];
      len(ii, jj) = dst(k, l) + dst(k, m);
      ang(ii, jj) = abs(pi/2 - acos(dot(d1, d2) / (norm(d1) * norm(d2))));
    end
  end

  % Find candidates with angle difference < 10 degree
  cand = find(ang < pi/18);

  % For these candidates, find the one with the shortest total distance
  [~, I] = min(len(cand));

  % Get corresponding indices
  [I, J] = ind2sub([nn nn], cand(I));
  cand = nbh([I J]);

  % Lines 
  plot([X(k) X(cand(1))], [Y(k) Y(cand(1))], 'b', 'LineWidth', 1);
  plot([X(k) X(cand(2))], [Y(k) Y(cand(2))], 'b', 'LineWidth', 1);

end

hold off;

Выходное изображение:

Output


Примерным (!) Решением является определение центраконтура, описанного найденными точками, и используйте atan2 относительно центра, чтобы упорядочить точки по углу.См. Следующий фрагмент кода для визуализации:

% Points
X = 2 * rand(1, 15) - 1;
Y = 2 * rand(1, 15) - 1;

% Center
C = [0, 0];

% Determine indices
[~, idx] = sort(atan2(X, Y));

figure(1);
hold on;

% Center
plot(C(1), C(2), 'kx', 'MarkerSize', 15);

% Plain points
plot(X, Y, '.', 'MarkerSize', 15);

% Indices and lines
for k = 1:numel(X)
  text(X(idx(k)) + 0.05, Y(idx(k)) + 0.05, num2str(k), 'FontSize', 12);
  if (k == numel(X))
    plot([X(idx(k)) X(idx(1))], [Y(idx(k)) Y(idx(1))], 'b');
  else
    plot([X(idx(k)) X(idx(k+1))], [Y(idx(k)) Y(idx(k+1))], 'b');
  end
end

hold off;

Дает следующий вывод:

Output

Хотя я уверен, чтоЯ боюсь, что определенное количество вогнутостей будет правильно обработано для данного примера (особенно для верхней части).Это потому, что изображение не является идеальным видом сверху, поэтому углы немного «искажены».

Тем не менее, возможно, порядок может увеличить ваш подход к минимальному расстоянию.

4 голосов
/ 08 апреля 2019

Вот решение, которое хорошо подходит для фигур, контуры которых сделаны из перпендикулярных * линий (как в вашем примере). Идея заключается в следующем:

  1. Мы вращаем точки, чтобы выровнять * их по сетке XY.
  2. Мы группируем точки в семейства, которые имеют одинаковые * X или Y координаты.
  3. Для каждой точки мы вычисляем две точки: самую близкую по горизонтали и самую близкую по вертикали из допустимых семейств .
  4. Построить матрицу связности и преобразовать обратно.

Так же, как и в ответе HansHirse , я должен изменить набор данных: добавить недостающий угол (пункт 30), удалить два неугловых элемента (пункты 7-8), удалить дубликат последней точки.

* - приблизительно .

function A = q55511236
%% Initialization:
% Define points:
X = [364.533 372.267 397.067 408.133 382.471 379.533 329.250 257.200 199.412 195.267 184.385 ...
     168.643 157.533 174.500 108.533 99.333 150.733 184.800 138.105 179.474 218.278 232.133 ...
     267.714 306.929 312.143 357.733 421.333 431.000 371.867];
Y = [192.027 233.360 228.627 286.693 314.541 292.960 327.450 340.500 348.671 326.693 269.308 ...
     330.857 274.493 226.786 239.200 193.467 182.760 101.893 111.000 80.442 74.356 140.360 ...
     64.643 56.857 77.786 69.493 133.293 180.427 142.160];

%% Preprocessing:
% Centering:
XY = [X;Y] - [mean(X); mean(Y)];
% Rotation:
[U,~,~] = svd(XY,'econ');
rXY = (U.' * XY).';

% Fixing problems w/ some points:
rXY = vertcat(rXY, [-21.8, 66]); % add missing point
rXY(7:8, :) = NaN; % remove non-corners
% figure(); scatter(rXY(:,1),rXY(:,2));

%% Processing:
% Group points according to same-X and same-Y
CLOSE_ENOUGH_DISTANCE = 10; % found using trial and error
[~,~,sameXpts] = uniquetol(rXY(:,1), CLOSE_ENOUGH_DISTANCE, 'DataScale', 1);
[~,~,sameYpts] = uniquetol(rXY(:,2), CLOSE_ENOUGH_DISTANCE, 'DataScale', 1);

% Create masks for distance evaluations:
nP = size(rXY,1);
[maskX,maskY] = deal(zeros(nP));
maskX(sameXpts == sameXpts.') = Inf;
maskY(sameYpts == sameYpts.') = Inf;

% Compute X and Y distances separately (we can do this in the rotated space)
dX = abs(rXY(:,1) - rXY(:,1).') + maskX + 1./maskY;
dY = abs(rXY(:,2) - rXY(:,2).') + maskY + 1./maskX;
[~,nX] = min(dX);
[~,nY] = min(dY);

% Construct connectivity matrix:
A = false(nP);
idxTrue = sub2ind(size(A), repmat(1:nP, [1,2]), [nX(:).', nY(:).']);
A(idxTrue) = true;

%% Plot result:
% Rotated coordinates:
figure(); gplot(A, rXY, '-o'); text(rXY(:,1), rXY(:,2), string(1:nP));
uXY = (U*rXY.').';
% Original coordinates:
figure(); gplot(A, uXY, '-o'); text(uXY(:,1), uXY(:,2), string(1:nP)); axis ij;

В результате:

enter image description here

0 голосов
/ 03 июня 2019

Концепция, используемая для ответа, - это «проблема коммивояжера». Вокруг точек создается буфер, и этот буфер включается в качестве дополнительного критерия.

a=[141 188 178 217 229 282 267 307 313 357 372 422 434 365 372 398 411 382 382 233 229 191 185 166 156 183 173 114 97 149 139 139];
b=[109 103 79 76 140 132 64 56 78 72 141 133 180 192 234 228 287 293 315 348 343 348 329 332 270 268 225 240 194 184 108 108];
X=[364.5333 232.1333 397.0667 157.5333 431 421.3333 306.9286 184.3846 357.7333 199.4118 168.6429 179.4737 408.1333 382.4706 150.7333 372.2667 184.8 138.1053 312.1429 108.5333 174.5 195.2667 257.2 99.33333 379.5333 371.8667 329.25 280.7059 267.7143 218.2778];
Y=[192.0267 140.36 228.6267 274.4933 180.4267 133.2933 56.85714 269.3077 69.49333 348.6706 330.8571 80.44211 286.6933 314.5412 182.76 233.36 101.8933 111 77.78571 239.2 226.7857 326.6933 340.5 193.4667 292.96 142.16 327.45 130.5529 64.64286 74.35556];
R = [a' b'];
d = 12;
polyout = polybuffer(R,'lines',d)
figure
 %imshow(I2);
hold on
%plot(R(:,1),R(:,2),'r.','MarkerSize',10)
plot(X,Y,'r.', 'MarkerSize', 15)
plot(polyout)
axis equal
hold off
[s,t] = boundary(polyout);  %%this is the boundary polygon of the buffer 
numPoints = length(clustersCentroids);
x = X; %these are the points to be connected
y = Y;
x([1 2],:)=x([2 1],:);
y([1 2],:)=y([2 1],:);
figure
plot(x, y, 'bo', 'LineWidth', 2, 'MarkerSize', 17);
grid on;
 imshow(I2);
xlabel('X', 'FontSize', 10);
ylabel('Y', 'FontSize', 10);
% Make a list of which points have been visited
beenVisited = false(1, numPoints);
% Make an array to store the order in which we visit the points.
visitationOrder = ones(1, numPoints);
% Define a filasafe
maxIterations = numPoints + 1;
iterationCount = 1;
% Define a current index.  currentIndex will be 1 to start and then will vary.
currentIndex = 1;
while sum(beenVisited) < numPoints
    visitationOrder(iterationCount) = currentIndex; 
  beenVisited(currentIndex) = true; 
  % Get the x and y of the current point.
  thisX = x(currentIndex);
  thisY = y(currentIndex);
  %text(thisX + 0.01, thisY, num2str(currentIndex), 'FontSize', 35, 'Color', 'r');
  % Compute distances to all other points
  distances = sqrt((thisX - x) .^ 2 + (thisY - y) .^ 2);
  distances(beenVisited)=inf;
   distances(currentIndex) = inf;
  % Don't consider visited points by setting their distance to infinity.
  [out,idx] = sort(distances);
  xx=[x(currentIndex) x(idx(1))]
   yy=[y(currentIndex) y(idx(1))]
  if isempty(polyxpoly(xx,yy,s,t))
   iterationCount = iterationCount + 1;
   currentIndex =idx(1);
  else 
     xx=[x(currentIndex) x(idx(2))]
   yy=[y(currentIndex) y(idx(2))]  
  if isempty(polyxpoly(xx,yy,s,t))
   iterationCount = iterationCount + 1;
   currentIndex =idx(2);
   else 
     xx=[x(currentIndex) x(idx(3))]
   yy=[y(currentIndex) y(idx(3))]  
  if isempty(polyxpoly(xx,yy,s,t))
   iterationCount = iterationCount + 1;
   currentIndex =idx(3);
   else 
     xx=[x(currentIndex) x(idx(4))]
   yy=[y(currentIndex) y(idx(4))]  
  if isempty(polyxpoly(xx,yy,s,t))
   iterationCount = iterationCount + 1;
   currentIndex =idx(4);
  end
  end
  end
  end
end

% Plot lines in that order.
hold on;
orderedX = [x(visitationOrder); x(1)];
orderedY = [y(visitationOrder) ;y(1)];
plot(orderedX,orderedY, 'm-', 'LineWidth', 2);
title('Result', 'FontSize', 10);
...