Алгоритм группировки групп точек, которые следуют направлению

31

Примечание. Я помещаю этот вопрос как в теги MATLAB, так и в Python, так как я наиболее владею этими языками. Тем не менее, я приветствую решения на любом языке.


Вопрос Преамбула

Я взял изображение с линзой с рыбий глаз. Это изображение состоит из рисунка с кучей квадратных объектов. То, что я хочу сделать с этим изображением, - это определить центр тяжести каждого из этих квадратов, а затем использовать эти точки для выполнения искажения изображения - в частности, я ищу параметры правильной модели искажения. Следует отметить, что не все квадраты должны быть обнаружены. До тех пор, пока хорошее большинство из них, то это совершенно нормально.... но это не пункт этого поста. Алгоритм оценки параметров я уже написан, но проблема в том, что для него требуются точки, которые кажутся коллинеарными по изображению.

В базовом вопросе, который я хочу задать, даются эти точки, как наилучшим образом их группировать, чтобы каждая группа состояла из горизонтальной линии или вертикальной линии?

Предыстория моей проблемы

Это не очень важно в отношении вопроса, который я задаю, но если вы хотите узнать, откуда я получил свои данные, и для дальнейшего понимания вопроса, который я задаю, прочитайте. Если вас это не интересует, вы можете перейти к разделу Проблема ниже.


Пример изображения, с которым я имею дело, показан ниже:

Изображение 554

Это изображение 960 x 960. Первоначально изображение было более высокого разрешения, но я подбираю изображение, чтобы ускорить время обработки. Как вы можете видеть, есть куча квадратных узоров, рассеянных на изображении. Кроме того, центроиды, которые я вычислил, относятся к вышеуказанному подвыбранному изображению.

Конвейер, который я настроил для извлечения центроидов, следующий:

  • Выполните обнаружение Canny Edge
  • Сосредоточьтесь на интересующей области, которая минимизирует ложные срабатывания. Эта область интересов представляет собой в основном квадраты без какой-либо черной ленты, которая покрывает одну из сторон.
  • Найти все отдельные замкнутые контуры
  • Для каждого отдельного замкнутого контура...

    а. Выполните обнаружение Harris Corner

    б. Определите, имеет ли результат 4 угловых точки

    с. Если это так, то этот контур принадлежал квадрату и нашел центроид этой формы

    д. Если это не так, пропустите эту фигуру

  • Поместите все обнаруженные центроиды с шага №4 в матрицу для дальнейшего изучения.

Здесь приведен пример с приведенным выше изображением. Каждый обнаруженный квадрат имеет четыре цвета, закодированные в соответствии с расположением, где он относится к самому квадрату. Для каждого центроида, который я обнаружил, я пишу идентификатор, где этот центроид находится в самом изображении.

Изображение 555

С приведенным выше изображением найдено 37 обнаруженных квадратов.

Настройка проблемы

Предположим, что у меня есть пиксельные точки изображения, хранящиеся в матрице N x 3. Первые два столбца - это координаты x (горизонтальный) и y (вертикальный), где в координатном пространстве изображения координата y инвертирована, что означает, что положительный y перемещается вниз, Третий столбец - это идентификатор, связанный с точкой.

Вот некоторый код, написанный в MATLAB, который берет эти точки, разбивает их на 2D-сетку и помещает каждую точку в третий столбец матрицы. Если вы читаете приведенный выше фон, это точки, которые были обнаружены моим алгоритмом, описанным выше.

data = [ 475.  ,  605.75,    1.;
       571.  ,  586.5 ,    2.;
       233.  ,  558.5 ,    3.;
       669.5 ,  562.75,    4.;
       291.25,  546.25,    5.;
       759.  ,  536.25,    6.;
       362.5 ,  531.5 ,    7.;
       448.  ,  513.5 ,    8.;
       834.5 ,  510.  ,    9.;
       897.25,  486.  ,   10.;
       545.5 ,  491.25,   11.;
       214.5 ,  481.25,   12.;
       271.25,  463.  ,   13.;
       646.5 ,  466.75,   14.;
       739.  ,  442.75,   15.;
       340.5 ,  441.5 ,   16.;
       817.75,  421.5 ,   17.;
       423.75,  417.75,   18.;
       202.5 ,  406.  ,   19.;
       519.25,  392.25,   20.;
       257.5 ,  382.  ,   21.;
       619.25,  368.5 ,   22.;
       148.  ,  359.75,   23.;
       324.5 ,  356.  ,   24.;
       713.  ,  347.75,   25.;
       195.  ,  335.  ,   26.;
       793.5 ,  332.5 ,   27.;
       403.75,  328.  ,   28.;
       249.25,  308.  ,   29.;
       495.5 ,  300.75,   30.;
       314.  ,  279.  ,   31.;
       764.25,  249.5 ,   32.;
       389.5 ,  249.5 ,   33.;
       475.  ,  221.5 ,   34.;
       565.75,  199.  ,   35.;
       802.75,  173.75,   36.;
       733.  ,  176.25,   37.];

figure; hold on;
axis ij;
scatter(data(:,1), data(:,2),40, 'r.');
text(data(:,1)+10, data(:,2)+10, num2str(data(:,3)));

Аналогично в Python, используя numpy и matplotlib, мы имеем:

import numpy as np
import matplotlib.pyplot as plt

data = np.array([[ 475.  ,  605.75,    1.  ],
   [ 571.  ,  586.5 ,    2.  ],
   [ 233.  ,  558.5 ,    3.  ],
   [ 669.5 ,  562.75,    4.  ],
   [ 291.25,  546.25,    5.  ],
   [ 759.  ,  536.25,    6.  ],
   [ 362.5 ,  531.5 ,    7.  ],
   [ 448.  ,  513.5 ,    8.  ],
   [ 834.5 ,  510.  ,    9.  ],
   [ 897.25,  486.  ,   10.  ],
   [ 545.5 ,  491.25,   11.  ],
   [ 214.5 ,  481.25,   12.  ],
   [ 271.25,  463.  ,   13.  ],
   [ 646.5 ,  466.75,   14.  ],
   [ 739.  ,  442.75,   15.  ],
   [ 340.5 ,  441.5 ,   16.  ],
   [ 817.75,  421.5 ,   17.  ],
   [ 423.75,  417.75,   18.  ],
   [ 202.5 ,  406.  ,   19.  ],
   [ 519.25,  392.25,   20.  ],
   [ 257.5 ,  382.  ,   21.  ],
   [ 619.25,  368.5 ,   22.  ],
   [ 148.  ,  359.75,   23.  ],
   [ 324.5 ,  356.  ,   24.  ],
   [ 713.  ,  347.75,   25.  ],
   [ 195.  ,  335.  ,   26.  ],
   [ 793.5 ,  332.5 ,   27.  ],
   [ 403.75,  328.  ,   28.  ],
   [ 249.25,  308.  ,   29.  ],
   [ 495.5 ,  300.75,   30.  ],
   [ 314.  ,  279.  ,   31.  ],
   [ 764.25,  249.5 ,   32.  ],
   [ 389.5 ,  249.5 ,   33.  ],
   [ 475.  ,  221.5 ,   34.  ],
   [ 565.75,  199.  ,   35.  ],
   [ 802.75,  173.75,   36.  ],
   [ 733.  ,  176.25,   37.  ]])

plt.figure()
plt.gca().invert_yaxis()

plt.plot(data[:,0], data[:,1], 'r.', markersize=14)

for idx in np.arange(data.shape[0]):
    plt.text(data[idx,0]+10, data[idx,1]+10, str(int(data[idx,2])), size='large')

plt.show()

Получаем:

Изображение 556


Вернуться к вопросу

Как вы можете видеть, эти точки более или менее в шаблоне сетки, и вы можете видеть, что мы можем формировать линии между точками. В частности, вы можете видеть, что есть линии, которые могут быть сформированы горизонтально и вертикально.

Например, если вы ссылаетесь на изображение в фоновом разделе моей проблемы, мы можем видеть, что существует 5 групп точек, которые могут быть сгруппированы горизонтально. Например, пункты 23, 26, 29, 31, 33, 34, 35, 37 и 36 образуют одну группу. Пункты 19, 21, 24, 28, 30 и 32 образуют другую группу и т.д. И т.д. Аналогично в вертикальном смысле мы видим, что точки 26, 19, 12 и 3 образуют одну группу, точки 29, 21, 13 и 5 образуют другую группу и т.д.


Вопрос для запроса

Мой вопрос таков: что такое метод, который может успешно группировать точки в горизонтальных группировках и вертикальных группах отдельно, учитывая, что точки могут быть в любой ориентации?

Условия

  • Там должно быть не менее трех точек на строку. Если есть что-то меньшее, то это не относится к сегменту. Поэтому точки 36 и 10 не квалифицируются как вертикальная линия, и аналогично изолированная точка 23 не должна быть качественной как вертикальная линия, но она является частью первой горизонтальной группировки.

  • Вышеуказанный шаблон калибровки может быть в любой ориентации. Однако для того, что я имею в виду, наихудшая ориентация, которую вы можете получить, - это то, что вы видите выше в фоновом разделе.


Ожидаемый результат

Вывод будет представлять собой пару списков, в которых первый список содержит элементы, в которых каждый элемент дает вам последовательность идентификаторов точек, которые образуют горизонтальную линию. Аналогично, во втором списке есть элементы, в которых каждый элемент дает вам последовательность идентификаторов точек, которые образуют вертикальную линию.

Следовательно, ожидаемый результат для горизонтальных последовательностей будет выглядеть примерно так:

MATLAB

horiz_list = {[23, 26, 29, 31, 33, 34, 35, 37, 36], [19, 21, 24, 28, 30, 32], ...};
vert_list = {[26, 19, 12, 3], [29, 21, 13, 5], ....};

Python

horiz_list = [[23, 26, 29, 31, 33, 34, 35, 37, 36], [19, 21, 24, 28, 30, 32], ....]
vert_list = [[26, 19, 12, 3], [29, 21, 13, 5], ...]

Что я пробовал

Алгоритмически, я попытался отменить вращение, которое испытывается в этих точках. Я выполнил Анализ основных компонентов и попытался проецировать точки относительно вычисленных ортогональных базисных векторов, чтобы точки были более или менее прямая прямоугольная сетка.

Как только у меня это получилось, просто проделайте некоторую обработку в режиме сканирования, где вы можете группировать точки на основе дифференциального изменения либо по горизонтальной, либо по вертикальной координате. Вы сортируете координаты с помощью значений x или y, затем изучите эти отсортированные координаты и найдите большое изменение. Как только вы столкнетесь с этим изменением, вы можете группировать точки между изменениями вместе, чтобы сформировать ваши линии. Выполнение этого в отношении каждого измерения даст вам либо горизонтальную, либо вертикальную группировку.

Что касается PCA, вот что я сделал в MATLAB и Python:

MATLAB

%# Step #1 - Get just the data - no IDs
data_raw = data(:,1:2);

%# Decentralize mean
data_nomean = bsxfun(@minus, data_raw, mean(data_raw,1));

%# Step #2 - Determine covariance matrix
%# This already decentralizes the mean
cov_data = cov(data_raw);

%# Step #3 - Determine right singular vectors
[~,~,V] = svd(cov_data);

%# Step #4 - Transform data with respect to basis
F = V.'*data_nomean.';

%# Visualize both the original data points and transformed data
figure;
plot(F(1,:), F(2,:), 'b.', 'MarkerSize', 14);
axis ij;
hold on;
plot(data(:,1), data(:,2), 'r.', 'MarkerSize', 14);

Python

import numpy as np
import numpy.linalg as la

# Step #1 and Step #2 - Decentralize mean
centroids_raw = data[:,:2]
mean_data = np.mean(centroids_raw, axis=0)

# Transpose for covariance calculation
data_nomean = (centroids_raw - mean_data).T

# Step #3 - Determine covariance matrix
# Doesn't matter if you do this on the decentralized result
# or the normal result - cov subtracts the mean off anyway
cov_data = np.cov(data_nomean)

# Step #4 - Determine right singular vectors via SVD
# Note - This is already V^T, so there no need to transpose
_,_,V = la.svd(cov_data)

# Step #5 - Transform data with respect to basis
data_transform = np.dot(V, data_nomean).T

plt.figure()
plt.gca().invert_yaxis()

plt.plot(data[:,0], data[:,1], 'b.', markersize=14)
plt.plot(data_transform[:,0], data_transform[:,1], 'r.', markersize=14)

plt.show()

Приведенный выше код не только перепрограммирует данные, но также отображает как исходные точки, так и проецируемые точки вместе на одном рисунке. Однако, когда я пытался перепроверить мои данные, это сюжет, который я получаю:

Изображение 557

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


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

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

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

  • 1
    Я не уверен, но, возможно, вы могли бы добиться некоторого сокращения, вычислив выпуклый корпус и сначала поработав над его границами ...
  • 0
    @LeandroCaniglia - это хорошая идея. Я разберусь с этим и посмотрю, как все пойдет. Спасибо за чаевые!
Показать ещё 22 комментария
Теги:
image-processing
algorithm
image

3 ответа

11
Лучший ответ

Примечание 1: У него есть ряд настроек → , которые для других изображений могут потребоваться изменить, чтобы получить результат, который вы хотите увидеть.% Настройки - играть с этими значениями

Примечание 2: Он не находит все нужные строки → но его отправную точку....

Чтобы вызвать эту функцию, вызовите это в командной строке:

>> [h, v] = testLines;

Получаем:

>> celldisp(h)

h{1} =
     1     2     4     6     9    10
h{2} =
     3     5     7     8    11    14    15    17
h{3} =
     1     2     4     6     9    10
h{4} =
     3     5     7     8    11    14    15    17
h{5} =
     1     2     4     6     9    10
h{6} =
     3     5     7     8    11    14    15    17
h{7} =
     3     5     7     8    11    14    15    17
h{8} =
     1     2     4     6     9    10
h{9} =
     1     2     4     6     9    10
h{10} =
    12    13    16    18    20    22    25    27
h{11} =
    13    16    18    20    22    25    27
h{12} =
     3     5     7     8    11    14    15    17
h{13} =
     3     5     7     8    11    14    15
h{14} =
    12    13    16    18    20    22    25    27
h{15} =
     3     5     7     8    11    14    15    17
h{16} =
    12    13    16    18    20    22    25    27
h{17} =
    19    21    24    28    30
h{18} =
    21    24    28    30
h{19} =
    12    13    16    18    20    22    25    27
h{20} =
    19    21    24    28    30
h{21} =
    12    13    16    18    20    22    24    25
h{22} =
    12    13    16    18    20    22    24    25    27
h{23} =
    23    26    29    31    33    34    35
h{24} =
    23    26    29    31    33    34    35    37
h{25} =
    23    26    29    31    33    34    35    36    37
h{26} =
    33    34    35    37    36
h{27} =
    31    33    34    35    37

>> celldisp(v)
v{1} =
    33    28    18     8     1
v{2} =
    34    30    20    11     2
v{3} =
    26    19    12     3
v{4} =
    35    22    14     4
v{5} =
    29    21    13     5
v{6} =
    25    15     6
v{7} =
    31    24    16     7
v{8} =
    37    32    27    17     9

Также генерируется цифра, которая рисует линии через каждый правильный набор точек:

Изображение 6382

function [horiz_list, vert_list] = testLines

global counter;
global colours; 
close all;

data = [ 475.  ,  605.75,    1.;
       571.  ,  586.5 ,    2.;
       233.  ,  558.5 ,    3.;
       669.5 ,  562.75,    4.;
       291.25,  546.25,    5.;
       759.  ,  536.25,    6.;
       362.5 ,  531.5 ,    7.;
       448.  ,  513.5 ,    8.;
       834.5 ,  510.  ,    9.;
       897.25,  486.  ,   10.;
       545.5 ,  491.25,   11.;
       214.5 ,  481.25,   12.;
       271.25,  463.  ,   13.;
       646.5 ,  466.75,   14.;
       739.  ,  442.75,   15.;
       340.5 ,  441.5 ,   16.;
       817.75,  421.5 ,   17.;
       423.75,  417.75,   18.;
       202.5 ,  406.  ,   19.;
       519.25,  392.25,   20.;
       257.5 ,  382.  ,   21.;
       619.25,  368.5 ,   22.;
       148.  ,  359.75,   23.;
       324.5 ,  356.  ,   24.;
       713.  ,  347.75,   25.;
       195.  ,  335.  ,   26.;
       793.5 ,  332.5 ,   27.;
       403.75,  328.  ,   28.;
       249.25,  308.  ,   29.;
       495.5 ,  300.75,   30.;
       314.  ,  279.  ,   31.;
       764.25,  249.5 ,   32.;
       389.5 ,  249.5 ,   33.;
       475.  ,  221.5 ,   34.;
       565.75,  199.  ,   35.;
       802.75,  173.75,   36.;
       733.  ,  176.25,   37.];

figure; hold on;
axis ij;

% Change due to Benoit_11
scatter(data(:,1), data(:,2),40, 'r.'); text(data(:,1)+10, data(:,2)+10, num2str(data(:,3)));
text(data(:,1)+10, data(:,2)+10, num2str(data(:,3)));

% Process your data as above then run the function below(note it has sub functions)
counter = 0;
colours = 'bgrcmy';
[horiz_list, vert_list] = findClosestPoints ( data(:,1), data(:,2) );


function [horiz_list, vert_list] = findClosestPoints ( x, y )
  % calc length of points
  nX = length(x);
  % set up place holder flags
  modelledH = false(nX,1);
  modelledV = false(nX,1);
  horiz_list = {};
  vert_list = {};

  % loop for all points
  for p=1:nX
    % have we already modelled a horizontal line through these?
    % second last param - true - horizontal, false - vertical
    if modelledH(p)==false
      [modelledH, index] = ModelPoints ( p, x, y, modelledH, true, true );
      horiz_list = [horiz_list index];
    else
      [~, index] = ModelPoints ( p, x, y, modelledH, true, false );
      horiz_list = [horiz_list index];
    end

    % make a temp copy of the x and y and remove any of the points modelled 
    %  from the horizontal -> this  is to avoid them being found in the 
    %  second call.
    tempX = x;
    tempY = y;
    tempX(index) = NaN;
    tempY(index) = NaN;
    tempX(p) = x(p);
    tempY(p) = y(p);
    % Have we found a vertial line?
    if modelledV(p)==false
      [modelledV, index] = ModelPoints ( p, tempX, tempY, modelledV, false, true );
      vert_list = [vert_list index];
    end
  end
end
function [modelled, index] = ModelPoints ( p, x, y, modelled, method, fullRun )
  % p - row in your original data matrix
  % x - data(:,1)
  % y - data(:,2)
  % modelled - array of flags to whether rows have been modelled
  % method   - horizontal or vertical (used to calc graadients)
  % fullRun  - full calc or just to get indexes 
  %            this could be made better by storing the indexes of each horizontal in the method above

  % Settings - play around with these values 
  gradDelta = 0.2;  % find points where gradient is less than this value
  gradLimit = 0.45; % if mean gradient of line is above this ignore
  numberOfPointsToCheck = 7; % number of points to check when look along the line
                        % to find other points (this reduces chance of it
                        % finding other points far away
                        %  I optimised this for your example to be 7
                        %  Try varying it and you will see how it effect the result.

  % Find the index of points which are inline.
  [index, grad] = CalcIndex ( x, y, p, gradDelta, method );
  % check gradient of line
  if abs(mean(grad))>gradLimit
    index = [];
    return
  end
  % add point of interest to index
  index = [p index];

  % loop through all points found above to find any other points which are in
  %  line with these points (this allows for slight curvature
  combineIndex = [];
  for ii=2:length(index)
    % Find inedex of the points found above (find points on curve)
    [index2] = CalcIndex ( x, y, index(ii), gradDelta, method, numberOfPointsToCheck, grad(ii-1) );

    % Check that the point on this line are on the original (i.e. inline -> not at large angle
    if any(ismember(index,index2))
      % store points found
      combineIndex = unique([index2 combineIndex]);
    end
  end

  % copy to index
  index = combineIndex;
  if fullRun
    %  do some plotting
    %  TODO: here you would need to calculate your arrays to output.
    xx = x(index);
    [sX,sOrder] = sort(xx);
    % Check its found at least 3 points
    if length ( index(sOrder) ) > 2
      % flag the modelled on the points found
      modelled(index(sOrder)) = true;
      % plot the data
      plot ( x(index(sOrder)), y(index(sOrder)), colours(mod(counter,numel(colours)) + 1));
      counter = counter + 1;
    end
    index = index(sOrder);
  end
end  
function [index, gradCheck] = CalcIndex ( x, y, p, gradLimit, method, nPoints2Consider, refGrad )
  % x - data(:,1)
  % y - data(:,2)
  % p - point of interest
  % method (x/y) or (y\x)
  % nPoints2Consider - only look at N points (options)
  % refgrad          - rather than looking for gradient of closest point -> use this
  %                  - reference gradient to find similar points (finds points on curve)
  nX = length(x);
  % calculate gradient
  for g=1:nX
    if method
      grad(g) = (x(g)-x(p))\(y(g)-y(p));
    else
      grad(g) = (y(g)-y(p))\(x(g)-x(p));
    end
  end
  % find distance to all other points
  delta = sqrt ( (x-x(p)).^2 + (y-y(p)).^2 );
  % set its self = NaN
  delta(delta==min(delta)) = NaN;
  % find the closest points
  [m,order] = sort(delta);

  if nargin == 7
    % for finding along curve
    % set any far away points to be NaN
    grad(order(nPoints2Consider+1:end)) = NaN;
    % find the closest points to the reference gradient within the allowable limit
    index = find(abs(grad-refGrad)<gradLimit==1);
    % store output
    gradCheck = grad(index);
  else
    % find the points which are closes to the gradient of the closest point
    index = find(abs(grad-grad(order(1)))<gradLimit==1);
    % store gradients to output
    gradCheck = grad(index);
  end
end
end
  • 0
    Собирался опубликовать что-то подобное! Кстати: вы можете сделать большую часть ручной работы, используя: knnsearch gist.github.com/knedlsepp/5a62e16d22d01ab38f56 Я бы тогда предложил найти соединенные ребра там, где углы в точках соединения низкие. Таким образом, неправильные диагональные края будут удалены.
  • 0
    @knedlsepp - мне все еще интересно посмотреть, что вы предлагаете. Пожалуйста, продолжайте и опубликуйте это.
Показать ещё 15 комментариев
5

Хотя я не могу предложить лучший подход для группировки любого списка точек центра тяжести, чем тот, который вы уже пробовали, я надеюсь, что следующая идея может помочь вам:

Поскольку вы очень конкретно относитесь к содержимому своего изображения (содержащему поле квадратов), мне было интересно, действительно ли вам нужно группировать точки центра тяжести из данных, приведенных в вашем problem setup, или если вы можете использовать данные, описанные в Background to the problem. Поскольку вы уже определили углы каждого обнаруженного квадрата, а также их положение на этом квадрате, мне кажется, что было бы очень точно определить соседа заданного квадрата, сравнив угловые координаты.

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

Используя верхний предел разрешенной вертикальной/горизонтальной разности между углами, вы даже можете просто назначить наилучший квадрат соответствия в этих границах как сосед

Проблема может заключаться в том, что вы не обнаруживаете все квадраты, поэтому между квадратом 30 и 32. существует довольно большое пространство. Поскольку вы сказали, что вам нужно "по крайней мере" 3 квадрата на строку, это может оказаться для вас жизнеспособным чтобы просто игнорировать квадрат 32 в этой горизонтальной линии. Если это не вариант для вас, вы можете попытаться сопоставить как можно больше квадратов, а затем назначить "отсутствующие" квадраты точке в вашей сетке, используя ранее рассчитанные данные:

В примере о квадрате 32 вы бы обнаружили, что у него есть верхние и нижние соседи 27 и 37. Также вы должны были бы определить, что квадрат 27 находится внутри строк 1 и 37, находится в строке 3, так что вы может назначить квадрат 32 в строку "наилучшего соответствия" между ними, которая в этом случае, очевидно, равна 2.

Этот общий подход - это в основном подход, который вы уже пробовали, но, надеюсь, будет намного более точным, так как теперь вы сравниваете ориентацию и расстояние двух строк вместо простого сравнения местоположения двух точек в сетке.

Также как сиденид в ваших предыдущих попытках - можете ли вы использовать черные угловые линии для исправления начального вращения вашего изображения? Это может сделать дополнительные алгоритмы искажения (например, те, которые вы обсуждали с knedlsepp в комментариях) намного точнее. (EDIT: я прочитал комментарии Parag только сейчас - сравнение точек по углу линий, конечно, в основном такое же, как и поворот изображения заранее)

  • 1
    1. Мне не нужно просто рассматривать центроиды. Я, конечно, могу использовать любые данные, которые доступны на изображении, поэтому вы, безусловно, открыты для использования этой информации. 2. Конечно, я могу избежать использования квадрата 32 для горизонтальной линии. Другие центроиды в этом же ряду имеют достаточно хорошее распределение, которое можно считать коллинеарным. 3. Спасибо за ваш интерес :) Скорее всего, я буду использовать ваши идеи в сочетании с тем, что Matlabgui предоставил мне. Береги себя!
4

Я использую обрезанную версию размещенного изображения в качестве ввода. Здесь я обращаюсь только к случаю, когда ориентацию сетки можно рассматривать как горизонтальную/вертикальную. Это может не полностью решить вашу область действия, но я думаю, что это может дать вам несколько указателей.

Изображение 6383

Сделайте бинаризацию изображения так, чтобы искаженные квадраты были заполнены. Здесь я использую простой порог Otsu. Затем выполните преобразование расстояния этого двоичного изображения.

Изображение 6384

В изображении, преобразованном на расстояние, мы видим промежутки между квадратами как пики.

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

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

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

Изображение 6385

Изображение 6386

Для достаточно длинных подключенных компонентов вы можете поместить кривую (линию или полином), а затем классифицировать угловые точки, например, исходя из расстояния до кривой, на какой стороне кривой находится точка и т.д.

Я сделал это в Матлабе. Я не пробовал использовать фитинги и классифицировать детали.

clear all;
close all;

im = imread('0RqUd-1.jpg');
gr = rgb2gray(im);
% any preprocessing to get a binary image that fills the distorted squares
bw = ~im2bw(gr, graythresh(gr));

di = bwdist(bw);                % distance transform
di2 = imdilate(di, ones(3));    % propagate max

corners = corner(gr);           % simple corners

% find regional max for each column of dist image
regmxh = zeros(size(di2));
for c = 1:size(di2, 2)
    regmxh(:, c) = imregionalmax(di2(:, c));
end
% label connected components
ccomph = bwlabel(regmxh, 8);

% find regional max for each row of dist image
regmxv = zeros(size(di2));
for r = 1:size(di2, 1)
    regmxv(r, :) = imregionalmax(di2(r, :));
end
% label connected components
ccompv = bwlabel(regmxv, 8);

figure, imshow(gr, [])
hold on
plot(corners(:, 1), corners(:, 2), 'ro')
figure, imshow(di, [])
figure, imshow(label2rgb(ccomph), [])
hold on
plot(corners(:, 1), corners(:, 2), 'ro')
figure, imshow(label2rgb(ccompv), [])
hold on
plot(corners(:, 1), corners(:, 2), 'ro')

Чтобы получить эти строки для произвольных ориентированных сеток, вы можете представить изображение расстояния в виде графика и найти оптимальные пути. См. здесь для хорошего графического подхода.

  • 1
    Спасибо за такой подход. +1. Тем не менее, я буду использовать подход Matlabgui, поскольку он не требует ничего из набора инструментов для обработки изображений. Кроме того, алгоритм в конечном итоге будет выполняться на Python, поэтому его решение будет более переносимым. Тем не менее, спасибо за ваши усилия!

Ещё вопросы

Сообщество Overcoder
Наверх
Меню