How do I create a custom nonlinear filter?

Therefore I am interested in creating a custom nonlinear filter. My mask is a 3 by 3 matrix, and I want it to take my center point and look at the values ​​directly associated with it (excluding diagonal elements). I want to subtract the average of each of these adjacent values, and then find either the minimum of those values. I am mainly looking at elevation data and I want to find the smallest delta-Z to the midpoint.

Example:

Z = [64 21 31 59 38 30 92 26 81 47 43 60 53 23 18 71];

So let's say I was looking at Z(3,3)

= 43 right now . I would take 43 and subtract 92, 60, 18 and 47; respectively, -49, -17, 25 and -4. Then I want it to only output -49. This process will be repeated for each element in the Z-matrix. How should I do it? Thank!

+3


source to share


2 answers


@chappjc's answer is perfectly acceptable. However, if you want to use a based approach, colfilt

you can transform pixel neighborhoods with im2col

so that 3 x 3 overlapped quarters are placed in columns. What will happen here is that the pixel neighborhoods are built in a large column format, so the columns of each pixel neighborhood are added into one column. You will take all of these columns and place them in a 2D matrix. In our case, the number of rows will be 9, while we will have as many columns as there are valid pixels. This is the result of usingim2col

... How pixel neighborhoods are obtained, again in a basic column format. Starting at the top left corner of the image, 3 x 3 pixel blocks are collected row by row down. Once we get to the bottom of the matrix, we move on to the next column and then omit the rows again. This work behavior im2col

is vital to the operation of this algorithm.

Once you've done that, extract the second, fourth, sixth and eighth lines of this, respectively, to get the elements in the west, north, south, and east directions (cardinal directions) in the neighborhood. You would subtract the fifth row, which will be the center of the neighborhood with each of their respective cardinal directions, then take the minimum value. However, before you do that, you will need to put an array with 1 pixel border so that you can handle border pixels in Z

. This pixel boundary is assumed to be zero.

In other words, try doing something like this:

Zpad = padarray(Z, [1 1]);
A = im2col(Zpad, [3 3]);
cardinal_directions = A(2:2:8,:);
out = reshape(min(bsxfun(@minus, A(5,:), cardinal_directions), [], 1), size(Z));

      

Looks like a mouth! Let it go slowly. I used padarray

and created a 1 pixel border of zeros that surrounds the original matrix Z

and saved it in Zpad

. I then use im2col

3 x 3 pixels of the appended result to transform each neighborhood into columns of 9 elements each. Then I extract the main directions of each pixel's neighborhood by selecting the second, fourth, sixth and eighth lines of the output im2col

. Once I extract those baselines, I extract the fifth row, which is the center of each pixel neighborhood, and subtract with the corresponding pixel neighborhoods. Then I take the minimum across all columns, using min

and working on all rows (indicating the size of the operation 1

).



I use bsxfun

to make it easier to subtract the center pixel in each area with their respective cardinal directions. This output will be the only vector and so I will need the reshape

vector back to the matrix. This row vector has its elements arranged in the basic column format, so I need to change the matrix to the correct matrix.

This is what I get with your example:

out =

    26   -43   -61    28
   -43   -62    49   -66
    28   -34   -49   -11
   -28   -30   -53    11

      

If you want to double check that this is correct, take a look at Z(2,2)

. We see that the center element is 30 and the cardinal elements are 21, 38, 47 and 92. Taking 30 and subtracting with each element, we get 9, -8, -17 and -62. The minimum of them is -62, which can be seen at the level out(2,2)

. Likewise, your example with Z(3,3)

gives -49 in out(3,3)

, as expected. You will need to take care of what happens along the border out

. I've zero-padded this matrix, and so there are entries along the border where you take the center of the neighborhood and subtract with zero. You haven't figured out correctly what you want to do along the border, so I am assuming that in this case the main directions along the border, if you go outside the limits Z

, are zero.

+5


source


For filtering, min / max ordfilt2

is probably the most efficient (possibly imdilate

/ imerode

, but that's a different story).

First, create a mask that specifies the four nearest neighbors:

>> mask = false(3); mask([2 4 6 8]) = true
mask =

     0     1     0
     1     0     1
     0     1     0

      

Filter with this mask:



>> Zmax = ordfilt2(Z,4,mask); % last value (4th non-zero) is max
>> out = Z - Zmax
out =

    26   -43   -61    28
   -43   -62    49   -66
    28   -34   -49   -11
   -28   -30   -53    11

      

In any case, to handle negative numbers, be sure to use a capable data type.

BTW, see this answer for usage ordfilt2

and imdilate

for finding peaks, a similar task.

+3


source







All Articles