фильтр, который использует элементы из двух массивов одновременно - PullRequest
4 голосов
/ 01 апреля 2010

Предположим, у нас есть два массива одинакового размера - A и B.

Теперь нам нужен фильтр, который для данного размера маски выбирает элементы из A, но удаляет центральный элемент маски и вставляет туда соответствующий элемент из B.

Таким образом, псевдомаска 3x3 будет выглядеть примерно так:

A A A
A B A
A A A

Сделать что-то подобное для усредняющего фильтра довольно просто. Мы можем вычислить среднее значение для элементов из A без центрального элемента, а затем объединить его в правильной пропорции с элементами из B:

h = ones(3,3);
h(2,2) =0; 
h = h/sum(h(:));
A_ave = filter2(h, A);
C = (8/9) * A_ave + (1/9) * B;

Но как сделать нечто подобное для медианного фильтра (medfilt2 или еще лучше для ordfilt2)

Ответы [ 2 ]

4 голосов
/ 01 апреля 2010

Способ решить эту проблему - найти способ объединить информацию из A и B, чтобы сама фильтрация стала проще.

Первое, о чем я подумал, - это объединить А и В в третьем измерении и передать маску фильтра, которая будет брать 8 элементов из «А-среза» и центральный элемент из «В-среза». Это, к сожалению, не поддерживается Matlab.

Хотя nlfilter работает только с 2D-изображениями, он позволяет вам указать любую функцию для фильтрации. Таким образом, вы можете создать функцию, которая каким-то образом сможет искать правильные значения A и B. Таким образом, я пришел к своему первому решению.

Вы создаете новый массив C, который содержит индекс элемента для каждого элемента, то есть первый элемент равен 1, второй элемент равен 2 и т. Д. Затем вы запускаете nlfilter, который берет скользящее окно 3x3 и пропускает значения C внутри окна для функции фильтрации, ffn. ffn - это анонимная функция, которая вызывает crazyFilter и инициализирована так, что A и B передаются при каждом вызове. CrazyFunction берет значения из скользящего окна C, которые являются ничем иным, как индексами, в A и B, и собирает значения из A и B из них.

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

EDIT Если у вас достаточно памяти, im2col и col2im могут значительно ускорить процесс

%# define A,B
A = randn(100);
B = rand(100);

%# pad A, B - you may want to think about how you want to pad
Ap = padarray(A,[1,1]);
Bp = padarray(B,[1,1]);

#% EITHER -- the more more flexible way
%# create a pseudo image that has indices instead of values
C = zeros(size(Ap));
C(:) = 1:numel(Ap);
%# convert to 'column image', where each column represents a block
C = im2col(C,[3,3]);
%# read values from A
data = Ap(C);
%# replace centers with values from B
data(5,:) = Bp(C(5,:));

%# OR -- the more efficient way
%# reshape A directly into windows and fill in B
data = im2col(Ap,[3,3]);
data(5,:) = B(:);

% median and reshape
out = reshape(median(data,1),size(A));

Старая версия (использует меньше памяти, может потребоваться заполнение)

%# define A,B
A = randn(100);
B = rand(100);

%# define the filter function
ffun = @(x)crazyFilter(x,A,B);

%# create a pseudo image that has indices instead of values
C = zeros(size(A));
C(:) = 1:numel(A);

%# filter
filteredImage = nlfilter(C,[3,3],ffun);




%# filter function
function out = crazyFilter(input,A,B)
%#CRAZYFILTER takes the median of a 3x3 mask defined by input, taking 8 elements from A and 1 from B

%# read data from A
data = A(input(:));
%# replace center element with value from B
data(5) = B(input(5));

%# return the median
out = median(data);
2 голосов
/ 01 апреля 2010

Вот решение, которое будет работать, если ваши данные имеют тип целое число без знака (как типичное изображение в градациях серого типа uint8). Вы можете объединить две матрицы A и B в одну матрицу целочисленного типа большего размера, причем данные из одной матрицы хранятся в младших битах, а данные из другой матрицы хранятся в старших битах. Затем вы можете использовать NLFILTER , чтобы применить функцию фильтрации, которая извлекает соответствующие биты данных для сбора необходимых значений матрицы.

В следующем примере применяется медианный фильтр формы, которую вы описали выше (массив элементов размером 3 на 3 из A с центральным элементом из B) к двум 8-битным матрицам без знака случайных значений:

%# Initialize some variables:

A = randi([0 255],[3 3],'uint8');  %# One random matrix of uint8 values
B = randi([0 255],[3 3],'uint8');  %# Another random matrix of uint8 values

C = uint16(A)+bitshift(uint16(B),8);  %# Convert to uint16 and place the values
                                      %#   of A in the lowest 8 bits and the
                                      %#   values of B in the highest 8 bits

C = padarray(C,[1 1],'symmetric');  %# Pad the array edges

%# Make the median filtering function for each 3-by-3 block:

medFcn = @(x) median([bitand(x(1:4),255) ...  %# Get the first four A values
                      bitshift(x(5),-8) ...   %# Get the fifth B value
                      bitand(x(6:9),255)]);   %# Get the last four A values

%# Perform the filtering:

D = nlfilter(C,[3 3],medFcn);
D = uint8(D(2:end-1,2:end-1));  %# Remove the padding and convert to uint8

Вот дополнительные ссылки для некоторых из ключевых функций, использованных выше: PADARRAY , BITAND , BITSHIFT .

...