Получить индексы граничных ячеек подмножества матрицы. Матлаб

Дана матрица, где 1 — текущее подмножество

test =

     0     0     0     0     0     0
     0     0     0     0     0     0
     0     0     1     1     0     0
     0     0     1     1     0     0
     0     0     0     0     0     0
     0     0     0     0     0     0

Есть ли функция или быстрый способ изменить подмножество на границу текущего подмножества?

Например. Получите это подмножество из «теста» выше

test =

     0     0     0     0     0     0
     0     1     1     1     1     0
     0     1     0     0     1     0
     0     1     0     0     1     0
     0     1     1     1     1     0
     0     0     0     0     0     0

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

Обратите внимание, что подмножество БУДЕТ соединено, но может не быть прямоугольным. Это может быть большой улов.

Это возможное подмножество.... (Дополнит это границей NaN)

test =

     0     0     0     0     0     0
     0     0     0     0     0     0
     0     0     1     1     0     0
     0     0     1     1     0     0
     0     0     1     1     1     1
     0     0     1     1     1     1

Идеи?


person Community    schedule 29.11.2012    source источник


Ответы (3)


Основные шаги, которые я бы использовал:

  1. Выполните расширение фигуры, чтобы получить новую область, которая представляет собой форму и ее границу.
  2. Вычтите исходную форму из расширенной формы, чтобы оставить только границу
  3. Используйте границу для индексации матрицы данных, затем возьмите минимум.

Расширение

Что я хочу сделать здесь, так это передать окно 3x3 по каждой ячейке и взять максимальное значение в этом окне:

[m, n] = size(A); % assuming A is your original shape matrix
APadded = zeros(m + 2, n + 2);
APadded(2:end-1, 2:end-1) = A; % pad A with zeroes on each side
ADilated = zeros(m + 2, n + 2); % this will hold the dilated shape.

for i = 1:m
    for j = 1:n
        mask = zeros(size(APadded));
        mask(i:i+2, j:j+2) = 1; % this places a 3x3 square of 1's around (i, j)
        ADilated(i + 1, j + 1) = max(APadded(mask));
    end
end

Вычитание формы

Это в основном логическое И и логическое НЕ для удаления пересечения:

ABoundary = ADilated & (~APadded);

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

ABoundary = ABoundary(2:end-1, 2:end-1);

Найдите минимальную точку данных вдоль границы

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

dataMinimum = min(data(ABoundary));
person Brian L    schedule 29.11.2012
comment
Именно то решение, которое я искал. Я знал, что что-то довольно простое ускользает от меня. Спасибо! - person ; 29.11.2012

Вы должны рассматривать это как проблему морфологии, а не теории множеств. Это можно довольно легко решить с помощью imdilate() (требуется пакет изображения). В основном вам нужно только вычесть изображение до его расширения с матрицей 3x3 из 1 .

octave> test = logical ([0  0  0  0  0  0
                         0  0  0  0  0  0
                         0  0  1  1  0  0
                         0  0  1  1  0  0
                         0  0  1  1  1  1
                         0  0  1  1  1  1]);
octave> imdilate (test, true (3)) - test
ans =

   0   0   0   0   0   0
   0   1   1   1   1   0
   0   1   0   0   1   0
   0   1   0   0   1   1
   0   1   0   0   0   0
   0   1   0   0   0   0

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

Обратите внимание, что вам не обязательно использовать logical(), и в этом случае вам придется использовать ones() вместо true(). Но это занимает больше памяти и имеет худшую производительность.

EDIT: поскольку вы пытаетесь сделать это без использования какого-либо набора инструментов Matlab, взгляните на источник imdilate() в Octave. В случае логических матриц (как в вашем случае) это простое использование filter2(), которое принадлежит ядру Matlab. Тем не менее, следующая строка должна работать нормально и быть намного быстрее.

octave> (filter2 (true (3), test) > 0) - test
ans =

   0   0   0   0   0   0
   0   1   1   1   1   0
   0   1   0   0   1   0
   0   1   0   0   1   1
   0   1   0   0   0   0
   0   1   0   0   0   0
person carandraug    schedule 29.11.2012
comment
Отлично работает, я пишу пакет и хотел бы, чтобы он использовал только стандартный MATLAB, а не пакет изображений. Спасибо! - person ; 29.11.2012
comment
@kpurdon код, который я написал, точно так же работает в Matlab (imdilate() также является частью набора инструментов для изображений Matlab). - person carandraug; 29.11.2012
comment
Правильный! imdilate() включен в базовый пакет Matlab, а набор инструментов для изображений является стандартом для Matlab без дополнительной покупки? - person ; 04.12.2012
comment
@kpurdon ты прав, я тебя неправильно понял. Для этого требуется набор инструментов изображения Matlab. Когда вы упомянули стандартный Matlab, я прочитал, что хочу что-то для Matlab, а не для Octave, поэтому я ответил, что это не для Octave. В любом случае, вы можете использовать filter2(), для которого не требуется набор инструментов (см. мое редактирование) - person carandraug; 04.12.2012

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

Так:

test_new = test + ...
[[test(2:end,2:end);zeros(1,size(test,1)-1)],zeros(size(test,1),1)] + ... %move subset up-left
[[zeros(1,size(test,1)-1);test(1:end-1,2:end)],zeros(size(test,1),1)] + ... %move down-left
[zeros(size(test,1),1),[test(2:end,1:end-1);zeros(1,size(test,1)-1)]] + ... %move subset up-right
[zeros(size(test,1),1),[zeros(1,size(test,1)-1);test(1:end-1,1:end-1)]];  %move subset down-right

test_masked = test_new.*~test; %mask with original matrix
result = test_masked;
result(result>1)=1; % ensure that there is only 1's, not 2, 3, etc.

Результат для вашей матрицы test:

result =

 0     0     0     0     0     0
 0     1     1     1     1     0
 0     1     0     0     1     0
 0     1     0     0     1     1
 0     1     0     0     0     0
 0     1     0     0     0     0

Отредактировано - теперь он также захватывает углы, перемещая подмножество вверх и влево, вверх и вправо, вниз, затем влево, вниз, затем вправо.

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

person David_G    schedule 29.11.2012
comment
Конечно, хорошее решение, но, похоже, будет немного проще пойти по пути расширения. - person ; 29.11.2012
comment
Конечно, приятель - мне было бы интересно посмотреть, как разные решения сравниваются с точки зрения времени вычислений. С каким размером входной матрицы вы имеете дело? - person David_G; 29.11.2012
comment
Размер ввода будет варьироваться. Я тестирую на 10x10 и 1000x1000, но пишу это как функцию, которая теоретически может работать с любым размером. - person ; 04.12.2012