2013-06-01 4 views
5

У меня проблема. У меня есть Матрица A с целыми числами от 0 до 5. , например, как:Операция быстрого соседства Matlab

x=randi(5,10,10) 

Теперь я хочу, чтобы вызвать фильтр, размер 3х3, который дает мне наиболее общее значение

Я попробовал 2 решения:

fun = @(z) mode(z(:)); 
y1 = nlfilter(x,[3 3],fun); 

который занимает очень долго ...

и

y2 = colfilt(x,[3 3],'sliding',@mode); 

который также занимает много времени. У меня есть действительно большие матрицы, и оба решения занимают много времени. Есть ли более быстрый способ?

+0

Фильтр режима всегда будет медленным, так как он должен гистограммировать значения, а затем искать наиболее распространенное значение - так что вы получите несколько проходов через наши данные. Реализован в «ванильном матлабе», который очень медленный. Вы можете быть намного быстрее, написав для себя небольшую рутину «mex» - с этим легко справиться. Вам нужны какие-то указатели? – Floris

+0

Хм ... Я никогда не писал рутину mex ... Знаете ли вы простой пример, который я могу взять в качестве образца? Мне было бы легче начать. – user2441536

ответ

3

+1 к @Floris за отличное предложение использовать hist. Это очень быстро. Вы можете сделать немного лучше, хотя. hist основан на histc, который может использоваться вместо этого. histc является скомпилированной функцией, то есть не написанной в Matlab, поэтому решение выполняется намного быстрее.

Вот небольшая функция, которая пытается обобщить то, что сделал @Floris (также это решение возвращает вектор, а не желаемую матрицу) и добивается того, что вы делаете с nlfilter и colfilt. Он не требует, чтобы вход имел конкретные размеры и использует im2col для эффективной переупорядочивания данных. Фактически, первые три строки и вызов im2col практически идентичны тому, что делает colfit в вашем случае.

function a=intmodefilt(a,nhood) 
[ma,na] = size(a); 
aa(ma+nhood(1)-1,na+nhood(2)-1) = 0; 
aa(floor((nhood(1)-1)/2)+(1:ma),floor((nhood(2)-1)/2)+(1:na)) = a; 
[~,a(:)] = max(histc(im2col(aa,nhood,'sliding'),min(a(:))-1:max(a(:)))); 
a = a-1; 

Использование:

x = randi(5,10,10); 
y3 = intmodefilt(x,[3 3]); 

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

Наконец, отметим, что randi(IMAX,N,N) возвращает значения в диапазоне 1:IMAX, не 0:IMAX, как вы, кажется, состояние.

+0

ничего себе это потрясающе. Мне просто нужно было изменить a = a-1 на a = a-2. теперь это именно то, что я хочу. Это в 50 раз быстрее, чем colfilt :) Завтра я попытаюсь проверить код, чтобы улучшить свои навыки. – user2441536

+0

Последняя строка функции 'a = a-1;', преобразует второй выходной аргумент 'max' из 1 на индекс, основанный на 0. Если 'min (a (:)) = 1', это устанавливает границы выходной матрицы равными 0, а значения варьируются от« 1 »до« max (a (:)) »(от« 1 »до« 5 » 'в вашем примере). Используя 'a = a-2;', тогда будет перемещаться внутренняя часть матрицы в диапазоне от «0» до «4» в примере вашего примера. – horchler

+1

Да, я получил это :). Это действительно впечатляет. Я узнал много сегодня – user2441536

2

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

Вот код, который показывает основную идею (используя только функции, доступные в Freemat - У меня нет Matlab на моей машине дома ...):

N = 100; 
A = randi(0,5*ones(3*N,3*N)); 
B = reshape(permute(reshape(A,[3 N 3 N]),[1 3 2 4]), [ 9 N*N]); 
hh = hist(B, 0:5); % histogram of each 3x3 block: bin with largest value is the mode 
[mm mi] = max(hh); % mi will contain bin with largest value 
figure; hist(B(:),0:5); title 'histogram of B'; % flat, as expected 
figure; hist(mi-1, 0:5); title 'histogram of mi' % not flat?... 

Вот участки: enter image description here enter image description here

странная вещь, когда вы запустите этот код, является то, что распределение mi не является плоской, но перекос в сторону меньших значений. Когда вы проверяете гистограммы, вы увидите, что это связано с тем, что у вас часто будет больше одного бина с максимальным значением в нем. В этом случае вы получаете первый бит с максимальным номером. Очевидно, это сильно исказит ваши результаты; что-то думать о. Гораздо лучшим фильтром может быть медианный фильтр - тот, который имеет равное количество соседних пикселей выше и ниже. У этого есть уникальное решение (в то время как mode может иметь до четырех значений, для девяти пикселей - а именно, четыре бункера с двумя значениями каждый).

Что-то думать.

Невозможно показать вам пример mex (неправильный компьютер); но на веб-сайте Mathworks (и во всем Интернете) есть достаточно хороших примеров, которые довольно легко отслеживать. См., Например, http://www.shawnlankton.com/2008/03/getting-started-with-mex-a-short-tutorial/

+0

Thx для вашей помощи. Предложение с resharp массивом - это что-то, что я должен попробовать и посмотреть, если он быстрее, чем colfilt ... Я также думал об использовании медиа-фильтра, но я не думаю, что это подходящее решение для меня ... Я также пытаюсь его решить с mex, если это не сложно. Thx again :) – user2441536

+0

@Floris, спасибо за сообщение! как мы можем изменить данные, но теперь они основаны на круговых кварталах? Круговые окрестности задаются его «радиусом». Я пытаюсь решить вопрос, размещенный здесь => http://stackoverflow.com/questions/21750989/matlab-efficient-comput-of-local-histograms-within-circular-neighboorhoods – Tin

+0

@Tin - интересный вопрос. Я подозреваю, что вы можете создать вектор «соседних смещений» один раз; см. мой ответ http://stackoverflow.com/questions/21462711/get-the-neighbors-of-a-matrix-element/21464505#21464505 для некоторых деталей. Если это не поможет оставить комментарий, я попытаюсь взглянуть. – Floris

Смежные вопросы