MATLAB идентифицирует соседние области в 3D-изображении

У меня есть 3D-изображение, разделенное на смежные области, где каждый воксель имеет одинаковое значение. Значение, присвоенное этому региону, является уникальным для региона и служит меткой. Пример изображения ниже описывает случай 2D:

     1 1 1 1 2 2 2
     1 1 1 2 2 2 3
Im = 1 4 1 2 2 3 3
     4 4 4 4 3 3 3
     4 4 4 4 3 3 3

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

    0 1 0 1
A = 1 0 1 1
    0 1 0 1
    1 1 1 0

Я ищу быстрое решение для больших 3D-изображений в MATLAB. Я придумал решение, которое выполняет итерацию по всем регионам, что занимает 0.05s на итерацию — к сожалению, это займет более получаса для изображения с 32 000 регионов. Есть ли у кого-нибудь более элегантный способ сделать это? Я публикую текущий алгоритм ниже:

labels = unique(Im); % assuming labels go continuously from 1 to N
A = zeros(labels);

for ii=labels
  % border mask to find neighbourhood
  dil = imdilate( Im==ii, ones(3,3,3) );
  border = dil - (Im==ii);

  neighLabels = unique( Im(border>0) );
  A(ii,neighLabels) = 1;
end

imdilate — это узкое место, которого я хотел бы избежать.

Спасибо за помощь!


person Lisa    schedule 02.04.2014    source источник
comment
MEX-файл? Быстро загорается....   -  person kkuilla    schedule 02.04.2014
comment
Спасибо за предложение. Однако кажется, что imdilate уже реализован в файле MEX!   -  person Lisa    schedule 02.04.2014


Ответы (4)


Я придумал решение, которое представляет собой комбинацию Divakar и teng, а также мои собственные модификации, и я обобщил их на случай 2D или 3D.

Чтобы сделать его более эффективным, я, вероятно, должен предварительно выделить r и c, но пока это среда выполнения:

  • Для трехмерного изображения размеров 117x159x126 и 32000 отдельные области: 0.79s
  • Для приведенного выше 2D примера: 0.004671s с этим раствором, 0.002136s с раствором Дивакара, 0.03995s с раствором тенга.

Однако я не пытался распространить победителя (Дивакара) на 3D-кейс!

noDims = length(size(Im));
validim = ones(size(Im))>0;
labels = unique(Im);

if noDims == 3
    Im = padarray(Im,[1 1 1],'replicate', 'post');
    shifts = {[-1 0 0] [0 -1 0] [0 0 -1]};
elseif noDims == 2
    Im = padarray(Im,[1 1],'replicate', 'post');
    shifts = {[-1 0] [0 -1]};
end

% get value of the neighbors for each pixel
% by shifting the image in each direction
r=[]; c=[];
for i = 1:numel(shifts)
    tmp = circshift(Im,shifts{i});
    r = [r ; Im(validim)];
    c = [c ; tmp(validim)]; 
end

A = sparse(r,c,ones(size(r)), numel(labels), numel(labels) );
% make symmetric, delete diagonal
A = (A+A')>0;
A(1:size(A,1)+1:end)=0;

Спасибо за помощь!

person Lisa    schedule 02.04.2014
comment
Отличное решение. Разреженная матрица - это путь! - person pangyuteng; 02.04.2014
comment
Спасибо! Разреженная матрица была на самом деле данностью, я не мог сохранить график такого размера каким-либо другим способом - я оставил его в начальном посте для ясности, потому что это не имело решающего значения для метода. - person Lisa; 02.04.2014
comment
Уже +1 это! Захватывающие вещи на самом деле. Получите также 3D-результаты! :) - person Divakar; 02.04.2014
comment
Я не совсем уверен, правильно ли я понимаю :) Вы просите меня сообщить о случае 3D? Первый результат на самом деле является 3D-результатом (я отредактировал пост, чтобы сделать его немного яснее). - person Lisa; 02.04.2014
comment
Ну я имел ввиду 3D с bsxfun подходом? Это был 0.79s с bsxfun? Вы сказали, что не расширили bsxfun до 3D. - person Divakar; 02.04.2014
comment
Предполагая, что я расширил его правильно: он работает уже некоторое время (~ 5-10 минут), поэтому, похоже, он не так хорошо масштабируется для 3D и больших изображений. - person Lisa; 02.04.2014
comment
Ага, bsxfun выходим на лимит памяти, как мы и боялись. - person Divakar; 02.04.2014
comment
@Lisa Итак, я думаю, придерживайтесь кода, который у вас есть здесь, для 3D-кейсов. Но не забудьте bsxfun, может пригодится! :) - person Divakar; 02.04.2014

Попробуйте это -

Im = padarray(Im,[1 1],'replicate');

labels = unique(Im);
box1 = [-size(Im,1)-1 -size(Im,1) -size(Im,1)+1 -1 1 size(Im,1)-1 size(Im,1) size(Im,1)+1];

mat1 = NaN(numel(labels),numel(labels));
for k2=1:numel(labels)
    a1 = find(Im==k2);
    for k1=1:numel(labels)
        a2 = find(Im==k1);
        t1 = bsxfun(@plus,a1,box1);
        t2 = bsxfun(@eq,t1,permute(a2,[3 2 1]));
        mat1(k2,k1) = any(t2(:));
    end
end
mat1(1:size(mat1,1)+1:end)=0;

Если это работает для вас, поделитесь с нами временем выполнения для сравнения? Хотелось бы увидеть, заваривается ли кофе быстрее, чем за полчаса!

person Divakar    schedule 02.04.2014
comment
Спасибо! Вроде работает, но пока пытаюсь понять как именно :) О времени выполнения отчитаюсь! - person Lisa; 02.04.2014
comment
Умно, немного запутанно! Я объединил части ваших, @teng и моих собственных модификаций и расширил их на общий случай 2D или 3D, я опубликую решение и время выполнения (0.79s для многомерного 3D-изображения!) в отдельном ответе. ! - person Lisa; 02.04.2014
comment
bsxfun точно приносит эту запутанную природу! Ничего себе, 30 минут до 0,79 секунды !? - person Divakar; 02.04.2014
comment
Я предполагаю, что делать 30 000 расширений - не самый умный способ сделать это :) - person Lisa; 02.04.2014
comment
@ Лиза Это была именно проблема! Что касается bsxfun, это подход с интенсивным использованием памяти и, скорее всего, использует компиляцию JIT (точно в срок), именно поэтому он работает хорошо. Но по той же причине, если вы скармливаете ему огромные данные, ему может не хватить памяти. Вот почему я с нетерпением жду результатов 3D :) - person Divakar; 02.04.2014

Ниже моя попытка.

 Im = [1 1 1 1 2 2 2;
     1 1 1 2 2 2 3;
     1 4 1 2 2 3 3;
     4 4 4 4 3 3 3;
     4 4 4 4 3 3 3];

 % mark the borders
 validim = zeros(size(Im));
 validim(2:end-1,2:end-1) = 1;

 % get value of the 4-neighbors for each pixel
 % by shifting the images 4 times in each direction
 numNeighbors = 4;
 adj = zeros([prod(size(Im)),numNeighbors]);
 shifts = {[0 1] [0 -1] [1 0] [-1 0]};
 for i = 1:numNeighbors
     tmp = circshift(Im,shifts{i});
     tmp(validim == 0) = nan;
     adj(:,i) = tmp(:);
 end

 % mark neighbors where it does not eq Im
 imDuplicates = repmat(Im(:),[1 numNeighbors]);
 nonequals = adj ~= imDuplicates;
 % neglect the border
 nonequals(isnan(adj)) = 0;     
 % get these neighbor values and the corresponding Im value
 compared = [imDuplicates(nonequals == 1) adj(nonequals == 1)];

 % construct your 'A' % possibly could be more optimized here.
 labels = unique(Im);
 A = zeros(numel(labels));
 for i = 1:size(compared,1)
     A(compared(i,1),compared(i,2)) = 1;
 end
person pangyuteng    schedule 02.04.2014
comment
Большое спасибо! Я попробую и отчитаюсь. - person Lisa; 02.04.2014
comment
тенг, я использовал часть вашего решения для окончательной рабочей версии, спасибо! Я поместил свою версию и отчеты о времени выполнения в отдельный ответ, проверьте его, если вам интересно. - person Lisa; 02.04.2014

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

Im =

 1     2     2
 3     3     3
 3     4     4

Результирующая матрица смежности в соответствии с вашим кодом:

A =

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

который утверждает смежность между метками «2» и «4»: явно неверно. Это происходит просто потому, что вы читаете дополненные метки Im, основанные на индексах «validim», которые теперь не соответствуют новому Im и доходят до нижних границ.

person Arash    schedule 22.10.2014