Fast way to find the neighboor of pixel - matlab

I am programming for task that finds the neighbor of a given pixel x in image Dthat can formula as:
The formula shown pixels y which satisfy the distance to pixel x is 1, then they are neighbor of pixel x. This is my matlab code. However, it still takes long time to find. Could you suggest a faster way to do it. Thank you so much
%-- Find the neighborhood of one pixel
% x is pixel coordinate
% nrow, ncol is size of image
function N = find_neighbor(x,nrow,ncol)
i = x(1);
j = x(2);
I1 = i+1;
if (I1 > nrow)
I1 = nrow;
end
I2 = i-1;
if (I2 < 1)
I2 = 1;
end
J1 = j+1;
if (J1 > ncol)
J1 = ncol;
end
J2 = j-1;
if (J2 < 1)
J2 = 1;
end
N = [I1, I2, i, i; j, j, J1, J2];
For example: ncol=128; nrow=128; x =[30;110] then output
N =31 29 30 30; 110 110 111 109]
For calling the function in loop
x=[30 31 32 33; 110 123 122 124]
for i=1:length(x)
N = find_neighbor(x(:,i),nrow,ncol);
end

Here's a vectorized approach using bsxfun:
% define four neighbors as coordinate differences
d = [-1 0 ; 1 0 ; 0 -1 ; 0 1]';
% add to pixel coordinates
N = bsxfun(#plus, x, permute(d, [1 3 2]));
% make one long list for the neighbors of all pixels together
N = reshape(N, 2, []);
% identify out-of-bounds coordinates
ind = (N(1, :) < 1) | (N(1, :) > nrow) | (N(2, :) < 1) | (N(2, :) > ncol);
% and remove those "neighbors"
N(:, ind) = [];
The permute is there to move the "dimension" of four different neighbors into the 3rd array index. This way, using bsxfun, we get the combination of every pair of original pixel coordinates with every pair of relative neighbor coordinates. The out-of-bounds check assumes that nrow belongs to the first coordinate and ncol to the second coordinate.
With
ncol=128;
nrow=128;
x = [30 31 32 33; 110 123 122 124];
the result is
N =
29 30 31 32 31 32 33 34 30 31 32 33 30 31 32 33
110 123 122 124 110 123 122 124 109 122 121 123 111 124 123 125
Different neighbors of different pixels can end up to be the same pixel, so there can be duplicates in the list. If you only want each resulting pixel once, use
% remove duplicates?
N = unique(N', 'rows')';
to get
N =
29 30 30 30 31 31 31 32 32 32 33 33 33 34
110 109 111 123 110 122 124 121 123 124 122 123 125 124

Matlab's performance is horrible when calling small functions many time. The Matlab approach is to do vectorize as much as possible. A vectorized version of your code:
function N = find_neighbor(x,nrow,ncol)
N = [min(x(1,:)+1,nrow), max(x(1,:)-1,1), x(1,:), x(1,:); x(2,:), x(2,:),min(x(2,:)+1,ncol), max(x(2,:)-1,1)];
end
and usage
x=[30 31 32 33; 110 123 122 124]
N = find_neighbor(x,nrow,ncol);
BTW, for pixels on the border , your solution always gives 4 neighbors. This is wrong. the neighbors of (1,1) for examples should be only (2,1) and (1,2), while you add two extra (1,1).
The solution to this is quite simple - delete all neighbors that are outside the image
function N = find_neighbor(x,nrow,ncol)
N = [x(1,:)+1, x(1,:)-1, x(1,:), x(1,:); x(2,:), x(2,:),x(2,:)+1, x(2,:)-1];
N(:,N(1,:)<1 | N(1,:)> nrow | N(2,:)<1 | N(2,:)>ncol)=[];
end

Related

Find Array around Maximum Values of an Array

i have a quite complicated question here which i am working on. It's extremely difficult to describe in words, so i will try to explain it with an example.
Assume i have a matrix of values:
A =
[31 85 36 71 51]
[12 33 74 39 12]
[67 11 13 14 18]
[35 36 84 33 57]
Now, i want to first find a maximum vector in the first dimension, which is easy:
[max_vector,~] = max(A,[],1);
max_vector=[67,85, 84, 71,57]
Now i want to get a "slimmed" matrix with values around the maxima (periodical indices):
Desired_Matrix =
[12 36 36 33 18]
[67 85 84 71 57]
[35 33 13 39 51]
This is the matrix with the vectors around the maximum values of matrix A. Can someone tell me how to do this without using a double for loop?
Thank you!
% Input.
A = [31 85 36 71 51; 12 33 74 39 12; 67 11 13 14 18; 35 36 84 33 57]
% Dimensions needed.
nRows = size(A, 1);
nCols = size(A, 2);
% Get maxima and corresponding indices in input.
[max_vector, ind] = max(A);
% Get neighbouring indices.
ind = [ind - 1; ind; ind + 1];
% Modulo indices to prevent dimension overflow.
ind = mod(ind, nRows);
% Correct zero indices.
ind(ind == 0) = nRows;
% Calculate correct indices in A.
temp = repmat(0:nRows:nRows*(nCols-1), 3, 1);
ind = ind + temp;
% Output.
B = A(ind)
Since we have max indices per column, but later want to access these elements in the original array A, we need proper linear indices for A. Here, the trick is to add the number of rows multiplied by the column index (starting by 0). The easiest way to understand might be to remove the semicolons, and inspect the intermediate values of ind.
#HansHirse's answer is more efficient, as it does not create an intermediate matrix.
Try this:
[~, ind_max] = max(A,[],1);
A_ext = A([end 1:end 1],:);
ind_lin = bsxfun(#plus, bsxfun(#plus, ind_max, (0:2).'), (0:size(A_ext,2)-1)*size(A_ext,1));
result = reshape(A_ext(ind_lin), 3, []);
For Matlab R2016b or newer, you can simplify the third line:
[~, ind_max] = max(A,[],1);
A_ext = A([end 1:end 1],:);
ind_lin = ind_max + (0:2).' + (0:size(A_ext,2)-1)*size(A_ext,1);
result = reshape(A_ext(ind_lin), 3, []);
Here is another solution. This is similar to HansHirse's answer, with two improvements:
Slightly more elegantly handles the modular indexing
Is more flexible for specifying which neighbours your want
Code:
% Input
A = [31 85 36 71 51;
12 33 74 39 12;
67 11 13 14 18;
35 36 84 33 57];
% Relative rows of neighbours, i.e. this is [-1, 0, 1] for +/- one row
p = -1:1;
% Get A row and column counts for ease
[nr, nc] = size(A);
% Get max indices
[~,idx] = max( A, [], 1 );
% Handle overflowing indices to wrap around rows
% You don't have to redefine "idx", could use this directly in the indexing line
idx = mod( idx + p.' - 1, nr ) + 1;
% Output B. The "+ ... " is to convert to linear indices, as "idx"
% currently just refers to the row number.
B = A(idx + (0:nr:nr*nc-1));
You can use the Image Processing Toolbox to generate the result, though is less efficient than other solutions.
[~,idx] = max(A, [], 1);
d = imdilate( idx == (1:size(A,1) ).', [1;1;1], 'full');
p = padarray(A, 1, 'circular');
Desired_Matrix = reshape(p(d), 3, []);
just for your information, here is the generalized form for the 3D-Case:
A = zeros(3,5,5);
for id = 1: 20
A(:,:,id) = id;
if id == 10
A(:,:,id) = 100;
end
end
% Relative rows of neighbours, i.e. this is [-1, 0, 1] for +/- one row
p = -1:1;
% Get A row and column counts for ease
[nr, nc, nz] = size(A);
% Get max indices
[~,idx] = max( A, [], 3 );
% Handle overflowing indices to wrap around rows
% You don't have to redefine "idx", could use this directly in the indexing line
idx = mod( idx + reshape(p,1,1,3) - 1, nz ) + 1;
% Output B. The "+ ... " is to convert to linear indices, as "idx"
% currently just refers to the row number.
INDICES = ((idx-1) * (nr*nc)+1 )+ reshape(0:1:nc*nr-1,nr,nc);
B = A(INDICES);

Get points which are within a given distance in two different matrices

I have two matrices A and B, in which the number of rows can vary. A and B do not necessarily have the same number of rows.
For example:
A = [ 110 90
130 140
230 50
370 210 ];
B = [ 321 95
102 35
303 200 ];
Now matrix A and B have 'corresponding points'. Corresponding points are rows where the values in the 2nd column of both matrices are within +/-20.
For example:
A(1,2) = 90 and B(1,2) = 95, the difference is within +/-20 so A(1,:) and B(1,:) are corresponding points.
A(2,2) = 140 and B(2,2) = 35, the difference is not within +/-20 so A(2,:) and B(2,:) are not corresponding points.
A(3,2) = 50 and B(2,2) = 35, the difference is within +/-20 so A(3,:) and B(2,:) are corresponding points.
Using this I want to store the corresponding points of A and B in C and D respectively. For the above example, the final matrices should look like this:
C = [ 110 90
230 50
370 210 ]
D = [ 321 95
102 35
303 200 ]
You can get all of the distances using pdist2
dists = pdist2( A(:,2), B(:,2) )
>> dists = [ 5 55 110
45 105 60
45 15 150
115 175 10 ]
Then get the indices of all 'corresponding points', as defined by a threshold of 20.
% Get combinations within tolerance
idx = dists < 20;
% Get indices
[iA, iB] = find(idx);
Then you can create the final matrices
C = A(iA, :);
D = B(iB, :);
Edit: One way to ensure each pairing is unique (i.e. A(1,:) cannot be paired with multiple rows from B) would be to get the minimum dists for each row/column. Note: this would still give you duplicate matches if the distances are exactly the same, you haven't defined how this should be handled.
dists = pdist2( A(:,2), B(:,2) );
% Set values which are greater than the row/column minima to be infinity.
% This means they will never be within the tolerance of 20 (or whatever else)
dists ( bsxfun(#gt, dists, min(dists,[],1)) | bsxfun(#gt, dists, min(dists,[],2)) ) = Inf;
% In MATLAB versions > 2016b, you can use implicit expansion to replace bsxfun
% That would be: dists( dists > min(dists,[],1) | dists > min(dists,[],2) )
% Now continue as before
[iA, iB] = find( dists < 20 );
C = A(iA, :);
D = B(iB, :);

Find the difference between positive and negative peaks MATLAB

I need to find the difference between positive and negative peaks where the difference is greater than +-3.
I am using findpeaks function in MATLAB to find the positive and negative peaks of the data.
In an example of my code:
[Ypos, Yposloc] = findpeaks(YT0);
[Yneg, Ynegloc] = findpeaks(YT0*-1);
Yneg = Yneg*-1;
Yposloc and Ynegloc return the locations of the positive and negative peaks in the data.
I want to concatenate Ypos and Yneg based on the order of the peaks.
For example, my peaks are
Ypos = [11 6 -10 -10 6 6 6 6 6 -5]
Yneg = [-12 -14 -11 -11 -11 5 5 5 -6]
Locations in YT0
Yposloc = [24 63 79 84 93 95 97 100 156]
Ynegloc = [11 51 78 81 85 94 96 99 154]
In this case, where both Yposloc and Ynegloc are 9x1, I can do the following;
nColumns = size(Yposs,2);
YTT0 = [Yneg, Ypos]';
YTT0 = reshape(YTT0(:),nColumns,[])';
YTT0 = diff(YTT0)
YT0Change = numel(YTT0(YTT0(:)>=3 | YTT0(:)<=-3));
Total changes that I am interested is 6
However, I need to concatenate Yneg and Ypos automatically, based on their locations. So I think I need to to do an if statement to figure out if my positive or negative peaks come first? Then, I am not sure how to tackle the problem of when Ypos and Yneg are different sizes.
I am running this script multiple times where data changes and the negative/positive peak order are constantly changing. Is there a simple way I can compare the peak locations or am I on the right track here?
I would check each minimum with both the previous and the next maxima. In order to do that you can first combine positive and negative peaks according to their order:
Y = zeros(1, max([Yposloc, Ynegloc]));
Yloc = zeros(size(Y));
Yloc(Yposloc) = Yposloc;
Yloc(Ynegloc) = Ynegloc;
Y(Yposloc) = Ypos; % I think you inserted one extra '6' in your code!
Y(Ynegloc) = Yneg;
Y = Y(Yloc ~= 0) % this is the combined signal
Yloc = Yloc(Yloc ~= 0) % this is the combined locations
% Y =
%
% -12 11 -14 6 -11 -10 -11 -10 -11 6 5 6 5 6 5 6 -6 -5
%
% Yloc =
%
% 11 24 51 63 78 79 81 84 85 93 94 95 96 97 99 100 154 156
And then calculate the differences:
diff(Y)
% ans =
%
% 23 -25 20 -17 1 -1 1 -1 17 -1 1 -1 1 -1 1 -12 1
If you want changes of at least 6 units:
num = sum(abs(diff(Y)) > 6)
% num =
%
% 6
Ypos = [11 6 -10 -10 6 6 6 6 -5];
Yneg = [-12 -14 -11 -11 -11 5 5 5 -6];
Yposloc = [24 63 79 84 93 95 97 100 156];
Ynegloc = [11 51 78 81 85 94 96 99 154];
TOTAL=[Yposloc Ynegloc;Ypos Yneg];
%creatin a vector with positions in row 1 and values in row 2
[~,position]=sort(TOTAL(1,:));
%resort this matrix so the values are in the orginial order
TOTAL_sorted=TOTAL(:,position);
%look at the changes of the values
changes=diff(TOTAL_sorted(2,:));
if changes(1)>0
disp('First value was a Minimum')
else
disp('First value was a MAximum')
end
%same structure at the TOTAL matrix
%abs(changes)>3 produces a logical vector that shows where the absolute values was bigger
%than 3, in my opinon its rather intresting where the end is then were the start is
% thats why i add +1
Important_changes=TOTAL_sorted(:,find(abs(changes)>3)+1);
plot(TOTAL_sorted(1,:),TOTAL_sorted(2,:))
hold on
plot(Important_changes(1,:),Important_changes(2,:),...
'Marker','o','MarkerSize',10, 'LineStyle','none');
hold off

Huge number of intilization a small vector without a for-loop?

I received the code to the initialization the vector A1.
Sc=[75 80 85];
Sp=[60 65 70];
C=[10 20 30 40 50 60;
11 21 31 41 51 61];
% KK=1000000;
% for k=1:KK
k=1;
A1=[];
A1=[-C(k,1)+max(60-Sc(1),0) -C(k,2)+max(60-Sc(2),0) -C(k,3)+max(60-Sc(3),0)
-C(k,4)+max(Sp(1)-60,0) -C(k,5)+max(Sp(2)-60,0) -C(k,6)+max(Sp(3)-60,0)];
%end; %KK
The code above is work, but it isn't optimal (it's very long and I need rewrite it when vector's length, n, is changed). In my task the length of A1 is even, and lies in range 6<=n<=16 typically. But I need to initialize vector A1 the huge number times, k<=10^6.
I'd like to rewrite code. My code is below.
n= size(C,2);
M=60;
%KK=1000000;
% for k=1:KK
k=1;
AU=[];AD=[];
for i=1:n
if i<=n/2
AU=[AU, -C(k,i)+max(M-Sc(i),0)];
else
AD=[AD, -C(k,i)+max(Sp(i-n/2)-M,0)];
end %if
end % i
A1=[AU; AD]
% end; % KK
Question. Is it possible to rewrite the code without for-loop? Does it make sense when the vector's length, n, is substantially less the number of initialization, k?
This is a vectorized form:
Sc=[75 80 85];
Sp=[60 65 70];
C=[10 20 30 40 50 60;
11 21 31 41 51 61];
kk=2;%1000000;
n= 6;
M=60;
[K , I] = meshgrid(1:kk,1:n);
Idx = sub2ind([kk,n], K, I);
condition = I <= n/2;
AU = -C(Idx(condition)) + max(M-Sc(I(condition)),0).';
AD = -C(Idx(~condition)) + max(Sp(I(~condition) - n/2)-M,0).';
A1 = [AU AD].'
note: I changed kk from 1000000 to 2 because number of rows of C is 2 and can not be indexed with numbers greater than 2. So this make sence if number of rows of C would be 1000000

Checking the repeated values and then finding mean

I m working on my project, where i have 2 images as Img1 and Img2.
As Img1 is the binary image so i have calculated all decimal values.
For Img2 i have taken the pixel values.
For convenience i have taken 10X10 matrix values from the entire image for the below operation.
[row,col] = size(Img1);
m = zeros(row,col);
w = [1 2 4 8; 16 32 64 128; 256 512 1024 2048; 4096 8192 16384 32768];
for i=2:10
for j=2:10
O = double(Img1(i-1:i+2,j-1:j+2));
m(i,j) = sum(sum(O.* w));
end;
end;
[row,col] = size(Img2);
count = row*col;
outMat = zeros(4,4,count);
l=0;
%m = zeros(row,col);delete
O = zeros(1,256);
for i=2:10
for j=2:10
l=l+1;
outMat(:,:,l) = Img2(i-1:i+2,j-1:j+2);
vec = outMat(3,3,:);
vec = vec(:);
end;
end;
Now, for Img2 , i have collected all pixel values, and need to store 2 col.as below.
Col1 col2 from Img2
from Img1
44128 162
54960 150
58320 119
31200 120
48240 180
54960 160
44128 163
51109 90
44128 56
Here, 44128 is repeated 3 times,now adding all correspong mapping values from col2 i.e.
162,163,56 add them all divide by 3(becos occurance of 44128 is 3 times) and same procedure
to be followed for all values.
44128 (162+163+56)/3
54960 (150+160)/2
58320 (119/1)
31200 (120/1)
48240 (180/1)
51109 (90/1)
Here, I want to create an array N of 1D 1X(size of col) which acts as a counter of Img1 decimal
values,repeated values and store the counter values inside N, and then finding mean by dividing corresponding counter values of N to the Img2 pixel values as above.
Please help:-( , how can i write the code further.
Here's a way doing it using sparse.
Accumulate sums and counts:
S = sparse(Img1, ones(size(Img1)), Img2);
N = sparse(Img1, ones(size(Img1)), ones(size(Img2)));
Determine which values actually occur:
ind = find(N)
Extract sums and counts for those values:
S = full(S(ind))
N = full(N(ind))
Compute corresponding means
M = S ./ N
For your example numbers, this gives
ind =
31200
44128
48240
51109
54960
58320
S =
120
381
180
90
310
119
N =
1
3
1
1
2
1
M =
120
127
180
90
155
119
Try this:
totals = sparse(1, Img1, Img2 );
denominators = sparse( 1, Img1, 1);
img1vals = find(totals);
averages = nonzeros(totals) ./ nonzeros(denominators);
[img1vals(:) averages(:)]