Nonlinear Filter for image processing - looking for minimum inside a mask - matlab

I have an idea about a filter for images but I do not know how I can realize this without using a double-for-loop in MATLAB.
I have an image, and I want to use a linear filter mask on it, let's say:
[1,1,1,1,1]
This filter mask is moving over the image, pixel by pixel. For each neighbourhood, the pixel I is set to the minimum of the surrounding neighbourhood.
Here is an example:
[ … image data …]
[ … …]
[ … …]
[ … 23 68 155 20 53 …]
[ … …]
For my example, I want to filter the centering pixel with the value 155. The result would be:
[ … image data …]
[ … …]
[ … …]
[ … 23 68 20 20 53 …]
[ … …]
The pixel 155 gets replaced with the minimum value in his neighbourhood.
I can do this with a double-for-loop, but it is really slow, too slow to use it for my application.
Would be happy for a good idea how to increase the speed! Thank you

Your filter idea is called erosion. It is implemented in the Image Processing Toolbox in the function imerode. In your case, you'd apply:
result = imerode(image_data, [1,1,1,1,1]);
The neighborhood can have any shape. Set elements to 0 to exclude them from the neighborhood. For example, for a roundish neighborhood you can use
[0,1,1,1,0
1,1,1,1,1
1,1,1,1,1
1,1,1,1,1
0,1,1,1,0]

If I understand your question correctly, what you want is finding a moving minimum value with a specified window width along a specific row. This can be done with movmin function which was introduced in version 2016a.
Knowing that movmin processes columns by default (as dim = 1). So in your case, you may want to set the dim argument to 2 (move along the rows) and discard the endpoints for the value outside of the window. A sample code may look like:
k = randi(20,20,11); % make some samples
ci = 6; % sample at the center row
wd = 5; % filter window width
k(:,ci) = movmin(k(:,(ci-2:ci+2)),wd,2,'Endpoints','discard') % replace the center row samples
Take a look at the movmin documentation to learn more.

I was working on an own solution, when the answer of Y. Chang came up... I wanted to post it nevertheless. At least, the result is the same, so it seems to work.
% Test input.
A = round(rand(5) * 10)
% Dimensions.
nRows = size(A, 1);
nCols = size(A, 2);
% Kernel.
b = [1, 1, 1, 1, 1]
% Step size.
step = floor(numel(b) / 2);
% Output.
B = zeros(nRows, nCols);
for k = 1:nRows
temp = repmat(A(k, :), nCols + 2 * step, 1);
idx = double(triu(ones(size(temp)), -numel(b) + 1) & tril(ones(size(temp)), 0));
idx(idx == 0) = NaN;
temp = temp .* idx;
temp = min(temp, [], 2).';
B(k, :) = temp(step+1:end-step);
end
B
% Compare with movmin function.
BB = movmin(A, numel(b), 2)
Output:
A =
9 2 1 6 7
2 5 9 1 7
2 8 5 10 4
2 0 6 5 8
8 3 10 7 6
b =
1 1 1 1 1
B =
1 1 1 1 1
2 1 1 1 1
2 2 2 4 4
0 0 0 0 5
3 3 3 3 6
BB =
1 1 1 1 1
2 1 1 1 1
2 2 2 4 4
0 0 0 0 5
3 3 3 3 6

Related

Assigning values in neighborhood of local maxima’s to the value of local maxima based on varying window width (non symmetric window width)

This question is an extension of my previous question with some new issues so I thought to make a new query. I hope it is ok.
https://stackoverflow.com/questions/46054811/changing-the-values-in-the-neighbourhood-of-local-maxima-to-the-local-maxima/46055833#46055833
Query:
Where I find local maxima. I want to make a window and assign the values depending upon window size to the neighbors the value of local maxima.
Problem: (I want my window size to change as in my original signal I have different behavior around local maxima’s.) For example in 8th window local maxima is at 34th location but I have assigned the values on the left of 34th location the value of local maxima.
In short I want to have varying and controllable window width.
Please have a look at the attached picture to get an idea of output what I want.
I hope it will give some good idea.
I want to have varying and non symmetric means not same at every local maxima window width .
I have also attached a code for nlfilter which is doing exactly what I want to do means it makes window and assign values to local maximas within that window width but I need to have flexible and changeable window width.
Is it possible to have varying window width or is there some other way possible to do that.
enter image description here
Code:
t = 1:35 ;
Y = [1 3 13 6 2 7 5 4 2 4 1 0 1 2 3 5 0 0 1 0 0 2 3 6 7 0 0 8 0 1 1 2 3 4 2];
n = 2;
[p l] = findpeaks(Y);
locations = zeros(size(Y));
locations(l) = true;
locations = conv(locations, ones(1, 2*n+1), 'same') > 0;
X = -inf(size(Y)); % create temporary
X(l) = Y(l); % copy the local maxima
X = nlfilter(X, [1 2*n+1 ], #(x) max(x)); %replace all values with it local maxima
X(l) = Y(l); % ensure local maxima are not changed
Y(locations) = X(locations); % copy filtered temporary to output
figure(1)
hold on
plot(t,Y,'r')
t = 1:35 ;
Y = [1 3 13 6 2 7 5 4 2 4 1 0 1 2 3 5 0 0 1 0 0 2 3 6 7 0 0 8 0 1 1 2 3 4 2];
plot(t,Y,'b')
hold off
I shall be grateful to you for your valuable replies.
Further Explanation:
Please have a look at the pictures attached.
2nd picture is a part of original signal with local maximas mentioned as green dots.
In 1st pictures the red lines shows the region which I want to assign the value of local maxima. Green Dot is local maxima . So you will see that if I apply window with fixed width it will not work because the points before local maxima are less than after local maxima.
The reason for placing 1 outside in example is same that there are few points before local maxima which I want to flat as compared to after local maxima.
The same is the case with other windows like last window 8 I have local maxima on 34th location but why I have chosen large values before it is only due to the values I want to assign the values of local maxima .
You can define a criterion that starting form a peak and going to the both sides we compute variance of neighbors of the peak and we increase the radius of the neighborhood until variance of neighboring elements becomes greater than a predefined threshold.
Here idx_peaks is position of peaks and peaks is value of peaks. After applying the threshold you can get number of elements before and after position of each peak n_before and n_after. Then you can find indices of neighborhood and assign values to them.
Y = [1 3 13 6 2 7 5 4 2 4 1 0 1 2 3 5 0 0 1 0 0 2 3 6 7 0 0 8 0 1 1 2 3 4 2];
idx_peaks = [3 6 10 16 19 25 28 34];
peaks = [13 7 4 5 1 7 8 4];
threshold = 2;
cumvar = #(a)cumsum(a(:).^2)./(1:numel(a)).'-(cumsum(a(:))./(1:numel(a)).').^2;
categ = zeros(numel(Y),1);
categ(idx_peaks)=1;
forward_categ = cumsum(categ(idx_peaks(1):end));
n_after = accumarray(forward_categ,Y(idx_peaks(1):end),[],#(x)sum(cumvar(x)<threshold)-1).';
backward_categ = cumsum(flipud(categ(1:idx_peaks(end))));
n_before = flipud(accumarray(backward_categ,fliplr(Y(1:idx_peaks(end))),[],#(x)sum(cumvar(x)<threshold)-1)).';
lo = idx_peaks-n_before;
up = idx_peaks+n_after;
val = repelem(peaks,up-lo+1);
index=cumsum(accumarray(cumsum([1;up(:)-lo(:)+1]),[lo(:);0]-[0;up(:)]-1)+1);
index= index(1:end-1);
Y(index) = val
Here is the result when setting the threshold to 2 :
Y=
[1 3 13 6 2 7 4 4 4 4 4 4 1 5 5 5 1 1 1 1 1 1 1 7 7 0 0 8 4 4 4 4 4 4 4]

Applying median filter to data with 2 axes

I have the following code:
x = VarName3;
y = VarName4;
x = (x/6000)/60;
plot(x, y)
Where VarName3 and VarName4 are 3000x1. I would like to apply a median filter to this in MATLAB. However, the problem I am having is that, if I use medfilt1, then I can only enter a single array of variables as the first argument. And for medfilt2, I can only enter a matrix as the first argument. But the data looks very obscured if I convert x and y into a matrix.
The x is time and y is a list of integers. I'd like to be able to filter out spikes and dips. How do I go about doing this? I was thinking of just eliminating the erroneous data points by direct manipulation of the data file. But then, I don't really get the effect of a median filter.
I found a solution using sort.
Median is the center element, so you can sort three elements, and take the middle element as median.
sort function also returns the index of the previous syntaxes.
I used the index information for restoring the matching value of X.
Here is my code sample:
%X - simulates time.
X = [1 2 3 4 5 6 7 8 9 10];
%Y - simulates data
Y = [0 1 2 0 100 1 1 1 2 3];
%Create three vectors:
Y0 = [0, Y(1:end-1)]; %Left elements [0 0 1 2 0 2 1 1 1 2]
Y1 = Y; %Center elements [0 1 2 0 2 1 1 1 2 3]
Y2 = [Y(2:end), 0]; %Right elements [1 2 0 2 1 1 1 2 3 0]
%Concatenate Y0, Y1 and Y2.
YYY = [Y0; Y1; Y2];
%Sort YYY:
%sortedYYY(2, :) equals medfilt1(Y)
%I(2, :) equals the index: value 1 for Y0, 2 for Y1 and 3 for Y2.
[sortedYYY, I] = sort(YYY);
%Median is the center of sorted 3 elements.
medY = sortedYYY(2, :);
%Corrected X index of medY
medX = X + I(2, :) - 2;
%Protect X from exceeding original boundries.
medX = min(max(medX, min(X)), max(X));
Result:
medX =
1 2 2 3 6 7 7 8 9 9
>> medY
medY =
0 1 1 2 1 1 1 1 2 2
Use a sliding window on the data vector centred at a given time. The value of your filtered output at that time is the median value of the data in the sliding window. The size of the sliding window is an odd value, not necessarily fixed to 3.

Identify adjacent superpixels iteratively

Let A be:
1 1 1 1 1 1
1 2 2 3 3 3
4 4 2 2 3 4
4 4 4 4 4 4
4 4 5 5 6 6
5 5 5 5 5 6
I need to identify a particular superpixel's adjacent pixels,
e.g.
The 1st adjacency of 2 is 1, 3, 4
The 2nd adjacency of 2 is 5, 6
The 3rd adjacency of 2 is ...
What is the FASTEST way to do it?
Assume you have a function adj(value), that has the code from your previous question.
sidenote: you probably would like that adj() function not to return the value of the pixel you are analyzing. you can make that easily.
you could do:
img=[your stuff];
imgaux=img;
ii=1;
val=2; %whatever value you want
while numel(unique(imgaux))>1 % Stop if the whole image is a single superpixel
adjacent{ii}=adj(val);
% expand the superpixel to the ii order of adjacency
for jj=1:size(adjacent{ii},1)
imgaux(imgaux==adjacent{ii}(jj))==val;
end
ii=ii+1;
end
Now size(adjacent,2) will be the amount of adjacency levels for that superpixel.
I am guessing this code is optimizable, I welcome any try for it!
Following Dan's suggestion on the comments, here is a possible implementation:
% Parameters
pixVal = 2;
adj = {};
prevMask = A == pixVal;
for ii = 1:length(A)
currMask = imdilate(prevMask, ones(2 * ii + 1));
adj{ii} = setdiff(unique(A(currMask & ~prevMask))', [adj{:}]);
if isempty(adj{ii})
break
end
prevMask = currMask;
end
Where pixVal is the pixel you want to look at.
Result:
>> adj{:}
ans =
1 3 4
ans =
5 6
ans =
Empty matrix: 1-by-0
Here's another approach reusing the code from your previous question:
%// create adjacency matrix
%// Includes code from #LuisMendo's answer
% // Data:
A = [ 1 1 1 1 1 1
1 2 2 3 3 3
4 4 2 2 3 4
4 4 4 4 4 4
4 4 5 5 6 6
5 5 5 5 5 6 ];
adj = [0 1 0; 1 0 1; 0 1 0]; %// define adjacency. [1 1 1;1 0 1;1 1 1] to include diagonals
nodes=unique(A);
J=zeros(numel(nodes));
for value=nodes.'
mask = conv2(double(A==value), adj, 'same')>0; %// from Luis' code
result = unique(A(mask)); %// from Luis' code
J(value,result)=1;
J(value,value)=0;
end
J is now the adjacency matrix for your matrix A and this becomes a graph problem. From here you would use the appropriate algorithm to find the shortest path. Path length of 1 is your "1st adjacency", path length of 2 is "2nd adjacency" and so on.
Dijkstra to find shortest path from a single node
Floyd-Warshall to find shortest paths from all the nodes
Breadth-first search for a single node, plus you can generate a handy tree
Update
I decided to play around with a custom Breadth-First Traversal to use in this case, and it's a good thing I did. It exposed some glaring errors in my pseudocode, which has been corrected above with working Matlab code.
Using your sample data, the code above generates the following adjacency matrix:
J =
0 1 1 1 0 0
1 0 1 1 0 0
1 1 0 1 0 0
1 1 1 0 1 1
0 0 0 1 0 1
0 0 0 1 1 0
We can then perform a depth-first traversal of the graph, putting each level of the breadth-first tree in a row of a cell array so that D{1} lists the nodes that have a distance of 1, D{2} has a distance of 2, etc.
function D = BFD(A, s)
%// BFD - Breadth-First Depth
%// Find the depth of all nodes connected to node s
%// in graph A (represented by an adjacency matrix)
A=logical(A); %// all distances are 1
r=A(s,:); %// newly visited nodes at the current depth
v=r; %// previously visited nodes
v(s)=1; %// we've visited the start node
D={}; %// returned Depth list
while any(r)
D(end+1,:)=find(r);
r=any(A(r,:))&~v;
v=r|v;
end
end
For a start node of 2, the output is:
>> D=BFD(J,2)
D =
{
[1,1] =
1 3 4
[2,1] =
5 6
}

Matlab: How I can make this transformation on the matrix A? (part 2)

N.B: This question is more complex than my previous question: Matlab: How I can make this transformation on the matrix A?
I have a matrix A 4x10000, I want to use it to find another matrix C, based on a predefined vector U.
I'll simplify my problem with a simple example:
from a matrix A
20 4 4 74 20 20 4
36 1 1 11 36 36 1
77 1 1 15 77 77 1
3 4 2 6 7 8 15
and
U=[2 3 4 6 7 8 2&4&15 7&8 4|6].
& : AND
| : OR
I want, first, to find an intermediate entity B:
2 3 4 6 7 8 2&4&15 7&8 4|6
[20 36 77] 0 1 0 0 1 1 0 1 0 4
[4 1 1] 1 0 1 0 0 0 1 0 1 4
[74 11 15] 0 0 0 1 0 0 0 0 1 2
we put 1 if the corresponding value of the first line and the vector on the left, made ​​a column in the matrix A.
the last column of the entity B is the sum of 1 of each line.
at the end I want a matrix C, consisting of vectors which are left in the entity B, but only if the sum of 1 is greater than or equal to 3.
for my example:
20 4
C = 36 1
77 1
This was a complex one indeed and because of the many restrictions and labeling processes involved, it won't be as efficient as the solution to the previous problem. Here's the code to solve the posted problem -
find_labels1 = 2:8; %// Labels to be detected - main block
find_labels2 = {[2 4 15],[7 8],[4 6]}; %// ... side block
A1 = A(1:end-1,:); %// all of A except the last row
A2 = A(end,:); %// last row of A
%// Find unique columns and their labels for all of A execpt the last row
[unqmat_notinorder,row_ind,inv_labels] = unique(A1.','rows'); %//'
[tmp_sortedval,ordered_ind] = sort(row_ind);
unqcols = unqmat_notinorder(ordered_ind,:);
[tmp_matches,labels] = ismember(inv_labels,ordered_ind);
%// Assign labels to each group
ctl = numel(unique(labels));
labelgrp = arrayfun(#(x) find(labels==x),1:ctl,'un',0);
%// Work for the main comparisons
matches = bsxfun(#eq,A2,find_labels1'); %//'
maincols = zeros(ctl,numel(find_labels1));
for k = 1:ctl
maincols(k,:) = any(matches(:,labelgrp{k}),2);
end
%// Work for the extra comparisons added that made this problem extra-complex
lens = cellfun('length',find_labels2);
lens(end) = 1;
extcols = nan(ctl,numel(find_labels2));
for k = 1:numel(find_labels2)
idx = find(ismember(A2,find_labels2{k}));
extcols(:,k)=arrayfun(#(n) sum(ismember(labelgrp{n},idx))>=lens(k),1:ctl).'; %//'
end
C = unqcols(sum([maincols extcols],2)>=3,:).' %//'# Finally the output
I will give you a partial answer. I think you can take from here. Idea is to concatenate first 3 rows of A with each element of U replicated as last column. After you get the 3D matrix, replicate your original A and then just compare the rows. The rows which are equal, that is equivalent to putting one in your table.
B=(A(1:3,:).';
B1=repmat(B,[1 1 length(U)]);
C=permute(U,[3 1 2]);
D=repmat(C,[size(B1,1),1,1]);
E=[B1 D];
F=repmat(A',[1 1 size(E,3)]);
Now compare F and E, row-wise. If the rows are equal, then you put 1 in your table. For replicating & and |, you can form some kind of indicator vector.
Say,
indU=[1 2 3 4 5 6 7 7 7 8 8 -9 -9];
Same positive value indicates &, same negative value indicates |. Different value indicate different entries.
I hope you can take from here.

Matlab: Matrix Neighbour Extraction

I have a large number of images which I've broken down into segments such that their matrices look like:
img = [ 1 1 1 1 1 2 2 2 3 3 3 3
1 1 1 1 2 2 2 2 2 3 3 3
1 1 1 4 4 4 2 2 2 3 3 3
5 5 5 5 5 5 5 2 2 3 3 3 ];
where each number represents a different region and each region is arbitrarily shaped. So in this case, region 1 has neighbours 2, 4 and 5, region 2 has neighbours 1, 3 and 4 and so on.
I've extracted all of the regions into separate cells and obtained statistics (mean, variance, etc) which I plan to use to merge regions with statistics within a certain tolerance. I'm struggling to think of an efficient way to obtain the neighbours of each region to allow that merging to occur.
I have a horrible solution which takes a very long time for even one image:
referenceImage = [ 1 1 1 1 1 2 2 2 3 3 3 3;
1 1 1 1 2 2 2 2 2 3 3 3;
1 1 1 4 4 4 2 2 2 3 3 3;
5 5 5 5 5 5 5 2 2 3 3 3];
% Wish to extract each region into a separate cell
lastSP = 5;
sps = 1:lastSP;
% Could be a way to vectorise the below loop but it escapes me
superPixels(lastSP) = struct('Indices', 0, 'Neighbours', 0);
% Split data into separate cells
parfor a = 1 : lastSP
inds = find(referenceImage == sps(a));
superPixels(a).Indices = inds;
end
szs = size(referenceImage); % Sizes of RGB Image
for a = 1 : lastSP + 1
mask = zeros(szs(1), szs(2)); % Just bin mask wanted
mask(superPixels(a).Indices) = 1; % Mark the region pixels as one
mask = xor(bwmorph(mask, 'thicken'), mask); % Obtain the outlying regions
inds = find(mask ==1); % Fetch the external region indices
neighbours = []; % Have to dynamically grow neighbours matrix
neigh = 1;
for b = 1 : length(inds)
found = false;
if ~isempty(neighbours) % Check neighbours first
for c = 1 : length(neighbours)
if any(superPixels(neighbours(c)).Indices == inds(b))
found = true;
break;
end
end
end
if ~found
for c = 1 : lastSP + 1 % Check every other region
if any(superPixels(c).Indices == inds(b))
neighbours(neigh) = c;
neigh = neigh + 1;
break;
end
end
end
end
superPixels(a).Neighbours = neighbours;
end
I'm wondering if this is actually the best way to approach this problem. I know the very last loop is the main problem but I can't think of another way to reasonably write this, unless I recurse and check the neighbours of known neighbours.
Any help or nudges in the right direction would be greatly appreciated; thanks!
A simple (but probably not maximally efficient) solution is to dilate each region mask to pick neighbors:
labels = unique(img);
nLabels = length(labels);
neighbors = cell(nLabels,1);
for iLabel = 1:nLabels
msk = img == labels(iLabel);
adjacentPixelMask = imdilate(msk,true(3)) & ~msk;
neighbors{iLabel} = unique(img(adjacentPixelMask));
end
neighbors{1}
ans =
2
4
5