I need to get a 1-D profile of a circular image, for example 256x256 sin(R) image
I've written a matlab function for the task but it turns out to be very un-efficient.
the function averages over radius intervals of the original images.
matlab profiler reveals that the first line in the for-loop [indxs=find(...)]
takes ~86% of the running time.
i need to run the function on a some thousands of simulated images (some larger then 256x256) and it takes very long time to complete.
does anyone knows how can i make this code run faster?
maybe someone has another, more efficient way to do the task??
i also tried to convert to function into C++ & mex file using matlab coder
but it took longer (x3) to perform the task, might be because the sub-function- "findC"
uses some 2D-ffts to find the center of the image.
Thanks you All,
Dudas
My Matlab function:
function [sig R_axis Center]= Im2Polar (imR,ch,Center_Nblock)
% Converts Circular image to 1-D sig
% based on true image values w/o interpolation
% Input -
% imR - circular sinuns image
% ch - number of data-points in output signal (sig)
% Center_Nblock - a varible related to the image center finding method
% Output -
% sig - 1D vector of the circular image profile
% R_axis - axis data-points for sig
% Center - image center in pixels
[Mr Nr] = size(imR); % size of rectangular image
[Center]=findC(imR,Center_Nblock);
Xc=Center(1);
Yc=Center(2);
rMax=sqrt((Mr/2)^2 + (Nr/2)^2);
x=[0:1:Mr-1]-Xc+1;
y=[0:1:Nr-1]-Yc+1;
[X,Y]=meshgrid(x,y);
[TH,R] = cart2pol(X,Y);
% Assembling 1-D signal
sig=single([]);
ii=1;
dr=floor(rMax)/ch;
V=dr:dr:floor(rMax);
for v=V
indxs=find((v-dr)<=R & R<v);**
sig(ii)=mean(imR(indxs));
Nvals(ii)=length(indxs);
ii=ii+1;
end %for v
R_axis=V-dr/2;
end % of function
Following from the comments here's an example of something I might try. Let's work with a 9x9 example. Suppose you have the following annulus.
A =
0 0 0 0 0 0 0 0 0
0 0 1 1 1 1 1 0 0
0 1 1 1 0 1 1 1 0
0 1 1 0 0 0 1 1 0
0 1 0 0 0 0 0 1 0
0 1 1 0 0 0 1 1 0
0 1 1 1 0 1 1 1 0
0 0 1 1 1 1 1 0 0
0 0 0 0 0 0 0 0 0
Then the indices of your sort of mask are, lets say [k n]
>> [k n]
ans =
3 2
4 2
5 2
6 2
7 2
2 3
3 3
4 3
6 3
7 3
8 3
2 4
3 4
7 4
8 4
2 5
8 5
2 6
3 6
7 6
8 6
2 7
3 7
4 7
6 7
7 7
8 7
3 8
4 8
5 8
6 8
7 8
Now have a 9x9 matrix of zeroes on hand called B, we can shift the whole thing over to the left by one pixel as follows using the formula (i+9*(j-1)) to convert double index to a single index.
>> B=zeros(9,9);
>> B((k)+9*(n-2))=1
B =
0 0 0 0 0 0 0 0 0
0 1 1 1 1 1 0 0 0
1 1 1 0 1 1 1 0 0
1 1 0 0 0 1 1 0 0
1 0 0 0 0 0 1 0 0
1 1 0 0 0 1 1 0 0
1 1 1 0 1 1 1 0 0
0 1 1 1 1 1 0 0 0
0 0 0 0 0 0 0 0 0
Or move down and to the right as follows
>> B=zeros(9,9);
>> B((k+1)+9*(n-0))=1
B =
0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0
0 0 0 1 1 1 1 1 0
0 0 1 1 1 0 1 1 1
0 0 1 1 0 0 0 1 1
0 0 1 0 0 0 0 0 1
0 0 1 1 0 0 0 1 1
0 0 1 1 1 0 1 1 1
0 0 0 1 1 1 1 1 0
As long as it doesn't go out of bounds you should be able to shift a single annular mask around with a simple addition to put the center at the image center.
Related
Let's say I have a square matrix M:
M = [0 0 0 0 0 1 9; 0 0 0 0 0 4 4; 0 0 1 1 6 1 1; 0 1 2 9 2 1 0; 2 1 8 3 2 0 0; 0 8 1 1 0 0 0; 14 2 0 1 0 0 0]
0 0 0 0 0 1 9
0 0 0 0 0 4 4
0 0 1 1 6 1 1
M = 0 1 2 9 2 1 0
2 1 8 3 2 0 0
0 8 1 1 0 0 0
14 2 0 1 0 0 0
Now I'd like to calculate two different cumulative sums: One that goes from the top of each column to the element of the column, that is a diagonal element of the matrix, and one that goes from the bottom of the column to the same diagonal element.
The resulting matrix M'should therefore be the following:
0 0 0 0 0 1 9
0 0 0 0 0 4 5
0 0 1 1 6 2 1
M' = 0 1 3 9 4 1 0
2 2 8 5 2 0 0
2 8 1 2 0 0 0
14 2 0 1 0 0 0
I hope the explanation of what I'm trying to achieve is comprehensible enough. Since my matrices are much larger than the one in this example, the calculation should be efficient as well...but so far I couldn't even figure out how to calculate it "inefficiently".
In one line using some flipping and the upper triangular function triu:
Mp = fliplr(triu(fliplr(cumsum(M)),1)) ...
+flipud(triu(cumsum(flipud(M)),1)) ...
+flipud(diag(diag(flipud(M))));
The following will do the job:
Mnew = fliplr(triu(cumsum(triu(fliplr(M)),1))) + flipud(triu(cumsum(triu(flipud(M)),1)));
Mnew = Mnew - fliplr(diag(diag(fliplr(Mnew)))) + fliplr(diag(diag(fliplr(M))));
But is it the fastest method?
I think logical indexing might get you there faster
I'm trying to copy part of a matrix (matrix 1) in matlab to another empty matrix of zeros (matrix 2) so that the section I copy from matrix 1 has the same indices in matrix 2, e.g.
Matrix 1 (mat1):
0 3 0 0 2 4 1 2 6
1 3 4 2 0 0 0 2 0
0 2 6 1 3 6 6 1 1
0 0 0 2 1 3 3 1 0
1 4 5 2 3 3 0 0 1
Matrix 2 (mat2) desired output:
0 0 0 0 0 0 0 0 0
0 0 4 2 0 0 0 0 0
0 0 6 1 3 6 6 0 0
0 0 0 2 1 3 3 0 0
0 0 0 0 0 0 0 0 0
I've tried something like
mat2([2:4],[3:7]) = mat1([2:4],[3:7])
but of course it doesn't work... any ideas of an efficient way to do this? I couldn't find another thread to help with this problem.
Thanks!
It does work. You just need to create mat2 first:
mat2 = zeros(size(mat1));
mat2(2:4, 3:7) = mat1(2:4, 3:7);
Note that you don't need the square brackets on those ranges.
Do this:
mat2 = zeros(size(mat1));
Before copying over.
I want to use a 3D blob detector which is a filtration algorithm, used for detection of spherical object in images. In this filter voxels with value greater than all pixels in their 26-neighborhood are set to 1
(s(x,y,z)=max(n26(x,y,z)))
and every other pixel is set to 0. Is there any function in matlab to do this work?
M(x,y,z)={ 1 if s(x,y,z)=max(n26(x,y,z))
0 otherwise
The easiest way to find local maxima is to use imdilate:
%# s = 3D array
msk = true(3,3,3);
msk(2,2,2) = false;
%# assign, to every voxel, the maximum of its neighbors
s_dil = imdilate(s,msk);
M = s > s_dil; %# M is 1 wherever a voxel's value is greater than its neighbors
matlabs own imregionalmax supports 26n from the get go, output is a logical.
2D example with 8n:
A =
1 1 1 1 1 1 1 1
1 3 3 3 1 1 4 1
1 3 5 3 1 4 4 4
1 3 3 3 1 4 4 4
1 1 1 1 1 4 6 4
1 1 1 1 1 4 4 4
>> B = imregionalmax(A);
>> B
B =
0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0
0 0 1 0 0 0 0 0
0 0 0 0 0 0 0 0
0 0 0 0 0 0 1 0
0 0 0 0 0 0 0 0
I am trying to find centroid of objects. I have already implemented connected components labeling and I have developed following code for centroid, it does give result but does not gives correct result:
I have following output matrix i.e matrix_img:
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 1 1 1 1 0 0
2 2 2 2 0 0 0 0 0 1 1 1 1 1 1 0
2 2 2 2 0 0 0 0 1 1 1 1 1 1 1 1
2 2 2 2 0 0 0 0 1 1 1 1 1 1 1 1
2 2 2 2 0 0 0 0 1 1 1 1 1 1 1 1
2 2 2 2 0 0 0 0 0 1 1 1 1 1 1 0
2 2 2 2 0 0 0 0 0 0 1 1 1 1 0 0
2 2 2 2 0 0 0 0 0 0 0 0 0 0 0 0
2 2 2 2 0 0 0 0 0 0 0 0 0 0 0 0
2 2 2 2 0 0 5 5 5 0 0 0 0 0 0 0
2 2 2 2 0 0 5 5 5 0 0 0 0 0 0 0
2 2 2 2 0 0 5 5 5 0 0 0 0 0 0 0
2 2 2 2 0 0 0 0 0 0 0 0 0 0 0 0
and following is the code
n= max(max(matrix_img));
for k=1:n
a(k)=length(find(matrix_img==k));
sx(k)=0;
sy(k)=0;
cx=0;
cy=0;
for i=1:1:r
for j=1:1:c
if(matrix_img(i,j)==k)
sx(k)=sx(k)+i;
sy(k)=sy(k)+j;
cx=sx(k)/a(k);
cy=sy(k)/a(k);
end
end
end
fprintf('Centroid of Object %d is %d and %d \n', k, cx, cy);
end
It gives result like :
Centroid of Object 1 is 7 and 1.250000e+001
Centroid of Object 2 is 1.050000e+001 and 2.500000e+000
Centroid of Object 3 is 0 and 0
Centroid of Object 4 is 0 and 0
Centroid of Object 5 is 14 and 8
Object 5 result is correct, object 2 is completely wrong and object 1 is partially wrong.. what shall I do?
The values you obtain are the exact centroids for those objects. So you might want to define what you'd expect to get as results.
To make this more clear, I've colored the objects in your matrix. By the symmetry on your cartesian grid, there should be an equal number of points to the left and right of your centroid and the same for above/below the centroid. I've drawn a figure with your objects colored in, together with lines to represent the horizontal and vertical center lines. Those are lines for which, we have an equal number of points to the left/right (or above/below) of them that are part of a certain object.
Their intersection is the centroid, so you can see for object 5 (blue one) that the centroid is at (8, 14). For the other two objects, these center lines do not lie on the integer grid you have: the red object (1) has its centroid at (12.5, 7) which is also the outcome of your code and the green object (2) is centered around (2.5, 10.5).
You will either have to live with inaccuracy introduced by rounding your centroids (e.g. round(cx)) or you will have to live with the non-integer coordinates of the centroids.
Next to that, I also recommend you vectorize your code as oli showed: this allows you to run your code faster and it is easier to understand when you are somewhat familiar with MATLAB than for loops.
Perhaps a little note with regard to your string representation: don't use %d for non-integers, as you see that will cause real numbers to be displayed in scientific notation. I think it's clearer if you use something like %0.2f as your format string.
When you use matlab, avoid using loops, it makes your code very slow, and it is much longer.
You can do the same thing by doing that:
[y x]=ndgrid(1:size(matrix_img,1),1:size(matrix_img,2));
n=max(matrix_img(:));
for k=1:n
cy=mean(y(matrix_img==k));
cx=mean(x(matrix_img==k));
fprintf('Centroid of Object %d is %2.2g and %2.2g \n', k, cx, cy);
end
(Maybe you want to swap x and y in that code)
I am working on Connected Components labeling, and my matrix is:
1 1 0 2 2 2 0 3
1 1 0 2 0 2 0 3
1 1 1 1 0 0 0 3
0 0 0 0 0 0 0 3
4 4 4 4 0 5 0 3
0 0 0 4 0 5 0 3
6 6 0 4 0 0 0 3
6 6 0 4 0 7 7 7
and now, i want to do the second scan over it, for this i have made following code:
for i=1:1:r
for j=1:1:c
if (bw(i,j)>=1)
if (i-1>0 & i+1<=r)
% if ( bw(i,j)~= bw(i-1,j) | bw(i,j)~= bw(i+1,j))
if ( (bw(i,j)~= bw(i-1,j) & bw(i-1,j)>0))
bw(i,j)= min (bw(i-1,j),bw(i,j))
elseif ((bw(i,j)~= bw(i+1,j) & bw(i+1,j)>0))
bw(i,j) = min(bw(i+1,j),bw(i,j));
end
end
if (j-1>0 & j+1<=c)
if ( (bw(i,j)~= bw(i,j-1) & bw(i,j-1)>0))
bw(i,j) = min (bw(i,j-1),bw(i,j));
elseif((bw(i,j)~= bw(i,j+1) & bw(i,j+1)>0))
bw(i,j) = min (bw(i,j+1),bw(i,j)) ;
end
end
end
end
end
disp(bw);
but the problem is, when i run this code, i get the following output
1 1 0 2 2 2 0 3
1 1 0 1 0 2 0 3
1 1 1 1 0 0 0 3
0 0 0 0 0 0 0 3
4 4 4 4 0 5 0 3
0 0 0 4 0 5 0 3
6 6 0 4 0 0 0 3
6 6 0 4 0 7 7 7
only one value changes (2nd row, 4 col) in my result, whereas, I want:
1 1 0 1 1 1 0 3
1 1 0 1 0 1 0 3
1 1 1 1 0 0 0 3
0 0 0 0 0 0 0 3
4 4 4 4 0 5 0 3
0 0 0 4 0 5 0 3
6 6 0 4 0 0 0 3
6 6 0 4 0 3 3 3
could somebody please help? where am i making a mistake?
Nice solution by Jonas. As I was a bit into coding when I saw it added, I thought I'd show you my solution as well. Hopefully it is a bit more similar to your original code.
One of the mistakes you made was assuming this connection could be done in just one pass. Generaly it can not, as detections of some of the "snake elements" are dependent on the way you iterate throught the matrix. Becuase of this, I added an outer while loop.
bw =[ 1 1 0 2 2 2 0 3;
1 1 0 2 0 2 0 3;
1 1 1 1 0 0 0 3;
0 0 0 0 0 0 0 3;
4 4 4 4 0 5 0 3;
0 0 0 4 0 5 0 3;
6 6 0 4 0 0 0 3;
6 6 0 4 0 7 7 7 ];
%Set up matrix
[r,c] = size(bw);
%Zero pad border
bwZ = [zeros(1,c+2);[zeros(r,1) bw zeros(r,1)];zeros(1,c+2)];
%Iterate over all elements within zero padded border
done=0;%Done flag
cc=1; %Infinite loop protection
while not(done) && cc<r*c
done=1;cc=cc+1;
for i=2:r+1
for j=2:c+1
%Point should be evaluated
p = bwZ(i,j);
if p >= 1
%Pick out elements around active elements
if bwZ(i-1,j)==0;ue=inf;else ue = bwZ(i-1,j); end;%Up element
if bwZ(i+1,j)==0;de=inf;else de = bwZ(i+1,j); end;%Down element
if bwZ(i,j-1)==0;le=inf;else le = bwZ(i,j-1); end;%Left element
if bwZ(i,j+1)==0;re=inf;else re = bwZ(i,j+1); end;%Right element
bwZ(i,j) = min([ue de le re]);
%Set flag, if something has changed
if bwZ(i,j) ~= p
done = 0;
end
end
end
end
end
%Remove zero padding
bw = bwZ(2:end-1,2:end-1)
Output:
bw =
1 1 0 1 1 1 0 3
1 1 0 1 0 1 0 3
1 1 1 1 0 0 0 3
0 0 0 0 0 0 0 3
4 4 4 4 0 5 0 3
0 0 0 4 0 5 0 3
6 6 0 4 0 0 0 3
6 6 0 4 0 3 3 3
If the numbers don't need to be preserved, you can simply call bwlabel on your original image:
newImage = bwlabel(originalImage>0);
EDIT
Here's another version. It checks each connected component to see whether there's another connected component touching it. If yes, that connected component is relabelled.
%# nCC: number of connected components
nCC = max(originalImage(:));
for cc = 1:nCC
%# check whether the component exists
myCC = originalImage==cc;
if any(any(myCC))
%# create a mask to check for neighbors
%# by creating a border of 1 pixel
%# around the original label
msk = imdilate(myCC,true(3)) & ~myCC;
%# read all the pixel values under the mask
neighbours = originalImage(msk);
%# we're not interested in zeros, remove them
neighbours = neighbours(neighbours > 0);
if ~isempty(neighbours)
%# set the label of all neighbours to cc
originalImage( ismember(originalImage,neighbours) ) = cc;
end
end
end