Поиск островов с границами нулей

90
12

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


Предположим, что у меня есть эта матрица:


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 1 1 0 0 0 0 0 0 0 0 1
0 0 0 1 1 1 0 1 1 0 0 0 1 1 1 1 0
0 0 0 0 0 0 1 0 1 0 0 0 0 1 1 1 1
0 0 0 1 0 1 0 1 1 0 0 0 1 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 1 0 1 0 1 1 1 0 0 0 0 0 0 0
0 0 1 0 1 1 1 1 1 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

По пограничному слою я имею в виду строки 2 и 7, а столбцы 3 и 10 для острова № 1.


Это показано ниже:


explain


Я хочу индексы строк и столбцов островов. Итак, для вышеуказанной матрицы желаемый результат:


isl{1}= {[2 3 4 5 6 7];          % row indices of island#1
[3 4 5 6 7 8 9 10]} % column indices of island#1

isl{2}= {[2 3 4 5 6 7]; % row indices of island#2
[12 13 14 15 16 17]}; % column indices of island#2

isl{3} ={[9 10 11 12]; % row indices of island#3
[2 3 4 5 6 7 8 9 10 11];} % column indices of island#3


Не важно, какой остров обнаружен первым.


Хотя я знаю, что функция [r,c] = find(matrix) может давать индексы строк и столбцов единиц, но у меня нет никаких подсказок о том, как обнаружить связанные, поскольку они могут быть связаны в горизонтальном, вертикальном и диагональном порядке.
Любые идеи о том, как справиться с этой проблемой?

спросил(а) 2021-01-19T20:14:39+03:00 6 месяцев, 2 недели назад
1
Решение
90

Вы должны посмотреть статистику BoundingBox и ConvexHull, возвращаемую regionprops:


a = imread('circlesBrightDark.png');
bw = a < 100;
s = regionprops('table',bw,'BoundingBox','ConvexHull')

https://www.mathworks.com/help/images/ref/regionprops.html

ответил(а) 2021-01-19T20:14:39+03:00 6 месяцев, 2 недели назад
64

Довольно легко!


Просто используйте bwboundaries, чтобы получить границы каждого из блоков. вы можете просто получить min и max в каждом направлении x и y каждой границы, чтобы построить свой ящик.

ответил(а) 2021-01-19T20:14:39+03:00 6 месяцев, 2 недели назад
63

Использовать расширение изображения и regionprops


mat = [...
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 1 1 0 0 0 0 0 0 0 0 1;
0 0 0 1 1 1 0 1 1 0 0 0 1 1 1 1 0;
0 0 0 0 0 0 1 0 1 0 0 0 0 1 1 1 1;
0 0 0 1 0 1 0 1 1 0 0 0 1 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 1 0 1 0 1 1 1 0 0 0 0 0 0 0;
0 0 1 0 1 1 1 1 1 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];
mat=logical(mat);
dil_mat=imdilate(mat,true(2,2)); %here we make bridges to 1 px away ones
l_mat=bwlabel(dil_mat,8);
bb = regionprops(l_mat,'BoundingBox');
bb = struct2cell(bb); bb = cellfun(@(x) fix(x), bb, 'un',0);
isl = cellfun(@(x) {max(1,x(2)):min(x(2)+x(4),size(mat,1)),...
max(1,x(1)):min(x(1)+x(3),size(mat,2))},bb,'un',0);

ответил(а) 2021-01-19T20:14:39+03:00 6 месяцев, 2 недели назад
63

Поиск подключенных компонентов и их ограничивающих коробок - легкая часть. Более сложная часть заключается в объединении ограничивающих прямоугольников на острова.


Ограничивающие коробки


Сначала легкая часть.


function bBoxes = getIslandBoxes(lMap)
% find bounding box of each candidate island
% lMap is a logical matrix containing zero or more connected components
bw = bwlabel(lMap); % label connected components in logical matrix
bBoxes = struct2cell(regionprops(bw, 'BoundingBox')); % get bounding boxes
bBoxes = cellfun(@round, bBoxes, 'UniformOutput', false); % round values
end

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


0   0   0
0 1 0
0 0 0

будет иметь ограничивающий прямоугольник


[ 1.5000   1.5000   1.0000   1.0000 ]

который мы округлим до


[ 2  2  1  1]

Слияние


Теперь трудная часть. Во-первых, условие слияния:


    Мы объединяем ограничительную рамку b2 в ограничительную рамку b1, если b2, а остров b1 (включая пограничный слой) имеет непустое пересечение.

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


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


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


function mBoxes = mergeBoxes(bBoxes)
% find bounding boxes that intersect, and merge them
mBoxes = bBoxes;
% merge bounding boxes that overlap
anyMerged = true; % flag to show when we've finished
while (anyMerged)
anyMerged = false; % no boxes merged on this iteration so far...
for box1 = numel(mBoxes):-1:2
for box2 = box1-1:-1:1
% if intersection between bounding boxes is > 0, merge
% the size of box1 is increased b y 1 on all sides...
% this is so that components that lie within the borders
% of another component, but not inside the bounding box,
% are merged
if (rectint(mBoxes{box1} + [-1 -1 2 2], mBoxes{box2}) > 0)
coords1 = rect2corners(mBoxes{box1});
coords2 = rect2corners(mBoxes{box2});

minX = min(coords1(1), coords2(1));
minY = min(coords1(2), coords2(2));
maxX = max(coords1(3), coords2(3));
maxY = max(coords1(4), coords2(4));

mBoxes{box2} = [minX, minY, maxX-minX+1, maxY-minY+1]; % merge
mBoxes(box1) = []; % delete redundant bounding box

anyMerged = true; % bounding boxes merged: loop again
break;
end
end
end
end
end


Функция слияния использует небольшую служебную функцию, которая преобразует прямоугольники с форматом [x y width height] в вектор индексов для верхних левых, нижних правых углов [x1 y1 x2 y2]. (Это было фактически использовано в другой функции, чтобы проверить, что остров имеет нулевую границу, но, как обсуждалось выше, эта проверка не нужна.)


function corners = rect2corners(rect)
% change from rect = x, y, width, height
% to corners = x1, y1, x2, y2
corners = [rect(1), ...
rect(2), ...
rect(1) + rect(3) - 1, ...
rect(2) + rect(4) - 1];
end

Форматирование вывода и функция драйвера


Возвращаемое значение из mergeBoxes - это массив ячеек прямоугольных объектов. Если вы найдете этот формат полезным, вы можете остановиться здесь, но его легко получить в формате, запрошенном с диапазонами строк и столбцов для каждого острова:


function rRanges = rect2range(bBoxes, mSize)
% convert rect = x, y, width, height to
% range = y:y+height-1; x:x+width-1
% and expand range by 1 in all 4 directions to include zero border,
% making sure to stay within borders of original matrix
rangeFun = @(rect) {max(rect(2)-1,1):min(rect(2)+rect(4),mSize(1));...
max(rect(1)-1,1):min(rect(1)+rect(3),mSize(2))};
rRanges = cellfun(rangeFun, bBoxes, 'UniformOutput', false);
end

Все, что осталось, является основной функцией, чтобы связать все остальные вместе, и мы закончили.


function theIslands = getIslandRects(m)
% get rectangle around each component in map
lMap = logical(m);

% get the bounding boxes of candidate islands
bBoxes = getIslandBoxes(lMap);

% merge bounding boxes that overlap
bBoxes = mergeBoxes(bBoxes);

% convert bounding boxes to row/column ranges
theIslands = rect2range(bBoxes, size(lMap));

end


Здесь пробег с использованием матрицы образцов, заданной в вопросе:


M =
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 1 1 0 0 0 0 0 0 0 0 1
0 0 0 1 1 1 0 1 1 0 0 0 1 1 1 1 0
0 0 0 0 0 0 1 0 1 0 0 0 0 1 1 1 1
0 0 0 1 0 1 0 1 1 0 0 0 1 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 1 0 1 0 1 1 1 0 0 0 0 0 0 0
0 0 1 0 1 1 1 1 1 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
>> getIslandRects(M)
ans =
{
[1,1] =
{
[1,1] =
9 10 11 12
[2,1] =
2 3 4 5 6 7 8 9 10 11
}
[1,2] =
{
[1,1] =
2 3 4 5 6 7
[2,1] =
3 4 5 6 7 8 9 10
}
[1,3] =
{
[1,1] =
2 3 4 5 6 7
[2,1] =
12 13 14 15 16 17
}
}

ответил(а) 2021-01-19T20:14:39+03:00 6 месяцев, 2 недели назад
Ваш ответ
Введите минимум 50 символов
Чтобы , пожалуйста,
Выберите тему жалобы:

Другая проблема