A filter that simultaneously uses elements from two arrays

Suppose we have two arrays of the same size - A and B

Now we need a filter that, for a given mask size, selects elements from A , but removes the central element of the mask and inserts the corresponding element from B .

So, a 3x3 “pseudo mask” will look something like this:

 AAA ABA AAA 

Doing something similar for the averaging filter is pretty simple. We can calculate the average value for elements from A without a central element, and then combine it with the appropriate proportion with elements from 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; 

But how to do something similar for a median filter ( medfilt2 or even better for ordfilt2 )

+4
source share
2 answers

The way to solve this problem is to find a way to combine information from A and B so that filtering itself becomes easy.

The first thing I thought was to link A and B in the third dimension and pass in a filter mask that will take 8 elements from the “A-slice” and the central element from the “B-slice”. Unfortunately, this is not supported by Matlab.

While nlfilter only works with 2D images, it allows you to specify any function for filtering. So you can create a function that somehow can find the correct values ​​for A and B. So, I came to my first decision.

You create a new C array that contains the index of the element for each element, i.e. the first element is 1, the second element is 2, etc. Then you start nlfilter, which takes a 3x3 sliding window and passes the C values ​​inside the window to the filter function, ffn. ffn is an anonymous function that calls crazyFilter, and it was initialized so that A and B are passed on every call. CrazyFunction takes values ​​from the sliding window C, which are nothing more than the indices in A and B, and collects the values ​​from A and B from them.

The second solution is exactly the same, except that instead of moving the sliding window, you create a new array that in each column has the contents of the sliding window in any possible place. With an overlapping window, the column array becomes larger than the original array. Again, you just need to use the values ​​of the array of columns C, which are the indices in A and B, to look up the values ​​of A and B in the appropriate places.

EDIT If you have enough memory, im2col and col2im can significantly speed up the process

 %# 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)); 

Old version (uses less memory, filling may be required)

 %# 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); 
+4
source

Here is a solution that will work if your data is an unsigned integer type (for example, a typical grayscale image of uint8 type). You can combine two matrices A and B into one matrix of a larger integer type with data from one matrix stored in the lower bits and data from another matrix stored in the higher bits. You can then use NLFILTER to apply a filter function that extracts the appropriate data bits to collect the necessary matrix values.

The following example applies the median filter of the form described above (an array of 3-x-3 elements from A with a central element from B ) into two unsigned 8-bit matrices of random values:

 %# 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 

Below are additional links to some of the key features used above: PADARRAY , BITAND , BITSHIFT .

+2
source

Source: https://habr.com/ru/post/1305776/


All Articles