MATLAB calculate area of shape on plot - matlab

I Create a plot using imagesc. The X/Y axis are longitude and latitude respectively. The Z values are the intensity of the images for the image shown below. What I'd like to be able to do is calculate the area in each of the polygons shown. Can anybody recommend a straightforward (or any) method in accomplishing this?
EDIT
Forgot to include image.

Below is a toy example. It hinges on the assumption that the Z values are different inside the objects from outside (here: not 0). Also here I assume a straight divider at column 4, but the same principle (applying a mask) can be applied with other boundaries. This also assumes that the values are equidistant along x and y axes, but the question does not state the opposite. If that is not the case, a little more work using bsxfun is needed.
A = [0 2 0 0 0 2 0
3 5 3 0 1 4 0
1 4 0 0 3 2 3
2 3 0 0 0 4 2
0 2 6 0 1 6 1
0 3 0 0 2 3 0
0 0 0 0 0 0 0];
area_per_pix = 0.5; % or whatever
% plot it
cm = parula(10);
cm(1, :) = [1 1 1];
figure(1);
clf
imagesc(A);
colormap(cm);
% divider
dv_idx = 4;
left_object = A(:, 1:(dv_idx-1));
left_mask = left_object > 0; % threshold object
num_pix_left = sum(left_mask(:));
% right object, different method
right_mask = repmat((1:size(A, 2)) > dv_idx, size(A, 1), 1);
right_mask = (A > 0) & right_mask;
num_pix_right = sum(right_mask(:));
fprintf('The left object is %.2f units large, the right one %.2f units.\n', ...
num_pix_left * area_per_pix, num_pix_right * area_per_pix);

This might be helpful: http://se.mathworks.com/matlabcentral/answers/35501-surface-area-from-a-z-matrix
He has not used imagesc, but it's a similar problem.

Related

Count length and frequency of island of consecutive numbers

I have a sequence of ones and zeros and I would like to count how often islands of consecutive ones appear.
Given:
S = [1 1 0 0 1 1 1 0 1 0 0 1 0 0 0 1 1 0 0 1 0 0 1 1 0 1]
By counting the islands of consecutive ones I mean this:
R = [4 3 1]
…because there are four single ones, three double ones and a single triplet of ones.
So that when multiplied by the length of the islands [1 2 3].
[4 3 1] * [1 2 3]’ = 13
Which corresponds to sum(S), because there are thirteen ones.
I hope to vectorize the solution rather than loop something.
I came up with something like:
R = histcounts(diff( [0 (find( ~ (S > 0) ) ) numel(S)+1] ))
But the result does not make much sense. It counts too many triplets.
All pieces of code I find on the internet revolve around diff([0 something numel(S)]) but the questions are always slightly different and don’t really help me
Thankful for any advice!
The following should do it. Hopefully the comments are clear.
S = [1 1 0 0 1 1 1 0 1 0 0 1 0 0 0 1 1 0 0 1 0 0 1 1 0 1];
% use diff to find the rising and falling edges, padding the start and end with 0
edges = diff([0,S,0]);
% get a list of the rising edges
rising = find(edges==1);
% and falling edges
falling = find(edges==-1);
% and thereby get the lengths of all the runs
SRuns = falling - rising;
% The longest run
maxRun = max(SRuns);
% Finally make a histogram, putting the bin centres
R = hist(SRuns,1:maxRun);
You could also obtain the same result with:
x = find(S==1)-(1:sum(S)) %give a specific value to each group of 1
h = histc(x,x) %compute the length of each group, you can also use histc(x,unique(x))
r = histc(h,1:max(h)) %count the occurence of each length
Result:
r =
4,3,1

Color discrimination of matrix connected components

In Matlab, I have a matrix M, say:
M=[0 0 2 2 0 0
0 0 2 2 0 3
1 1 2 2 3 3
1 1 0 0 0 0
1 1 0 0 0 0];
with some connected components labeled 1,2 and 3.
I need to discriminate the components (1, 2 and 3) by using different colors (red, green and blue for example). Any help to do this. Thanks in advance
You can use image and colormap. From the documentation of the former,
image(C) displays the data in array C as an image. Each element of C
specifies the color for 1 pixel of the image.
When C is a 2-dimensional m-by-n matrix, the elements of C are used as
indices into the current colormap to determine the color. For 'direct' CDataMapping (the default),
values in C are treated as colormap indices (1-based if double, 0-based
if uint8 or uint16).
Thererfore, you only need to call image(M+1), so that the values start at 1; and then define a suitable colormap. The colormap is a 3-column matrix, where each row defines a color in terms of its R, G, B components.
M = [0 0 2 2 0 0;0 0 2 2 0 3;1 1 2 2 3 3;1 1 0 0 0 0;1 1 0 0 0 0];
imagesc(M+1) % add 1 so that values start at 1, not 0
cmap = [1 1 1; % white
.7 0 0; % dark red
0 .7 0; % dark green
0 0 .7]; % dark blue
colormap(cmap) % set colormap
axis tight % avoid white space around the values
axis equal % aspect ratio 1:1

Quick Assembly of sparse matrix

I have indices
I = [nGrid x 9] matrix % mesh on fine grid (9 point rectangle)
J = [nGrid x 4] matrix % mesh on coarse grid (4 point rectangle)
Here, nGrid is large number depending on the mesh (e.g. 1.e05)
Then I want to do like
R_ref = [4 x 9] matrix % reference restriction matrix from fine to coarse
P_ref = [9 x 4] matrix % reference prolongation matrix from coarse to fine
R = sparse(size) % n_Coarse x n_Fine
P = sparse(size) % n_Fine x n_Coarse
for k = 1 : nGrid % number of elements on coarse grid
R(I(k,:),J(k,:)) = R_ref;
P(J(k,:),I(k,:)) = P_ref;
end
size is predetermined number.
Note that even if there is same index in (I,J), I do not want to accumulate. I just want to put stencils Rref and Pref at each indices respectively.
I know that this is very slow due to the data structure of sparse.
Usually, I use
sparse(row,col,entry,n_row,n_col)
I can use this by manipulate I, J, R_ref, P_ref by bsxfun and repmat.
However, this cannot be done because of the accumulation of sparse function
(if there exists (i,j) such that [row(i),col(i)]==[row(j),col(j)], then R(row(i),row(j)) = entry(i)+entry(j))
Is there any suggestion for this kind of assembly procedure?
Sample code
%% INPUTS
% N and M could be much larger
N = 2^5+1; % number of fine grid in x direction
M = 2^5+1; % number of fine grid in y direction
% [nOx * nOy] == nGrid
nOx = floor((M)/2)+1; % number of coarse grid on x direction
nOy = floor((N)/2)+1; % number of coarse grid on y direction
Rref = [4 4 -1 4 -2 0 -1 0 0
-1 -1 -2 4 4 4 -1 4 4
-1 -1 4 -2 4 -2 4 4 -1
0 4 4 0 0 4 -1 -2 -1]/8;
Pref = [2 1 0 1 0 0 0 0 0
0 0 0 1 1 1 0 1 2
0 0 1 0 1 0 2 1 0
0 2 1 0 0 1 0 0 0]'/2;
%% INDEX GENERATION
tri_ref = reshape(bsxfun(#plus,[0,1,2]',[0,N,2*N]),[],1)';
TRI_ref = reshape(bsxfun(#plus,[0,1]',[0,nOy]),[],1)';
I = reshape(bsxfun(#plus,(1:2:N-2)',0:2*N:(M-2)*N),[],1);
J = reshape(bsxfun(#plus,(1:nOy-1)',0:nOy:(nOx-2)*nOy),[],1);
I = bsxfun(#plus,I,tri_ref);
J = bsxfun(#plus,J,TRI_ref);
%% THIS PART IS WHAT I WANT TO CHANGE
R = sparse(nOx*nOy,N*M);
P = R';
for k = 1 : size(I,1)
R(J(k,:),I(k,:)) = Rref;
P(I(k,:),J(k,:)) = Pref;
end

How can I find local maxima in an image in MATLAB?

I have an image in MATLAB:
y = rgb2gray(imread('some_image_file.jpg'));
and I want to do some processing on it:
pic = some_processing(y);
and find the local maxima of the output. That is, all the points in y that are greater than all of their neighbors.
I can't seem to find a MATLAB function to do that nicely. The best I can come up with is:
[dim_y,dim_x]=size(pic);
enlarged_pic=[zeros(1,dim_x+2);
zeros(dim_y,1),pic,zeros(dim_y,1);
zeros(1,dim_x+2)];
% now build a 3D array
% each plane will be the enlarged picture
% moved up,down,left or right,
% to all the diagonals, or not at all
[en_dim_y,en_dim_x]=size(enlarged_pic);
three_d(:,:,1)=enlarged_pic;
three_d(:,:,2)=[enlarged_pic(2:end,:);zeros(1,en_dim_x)];
three_d(:,:,3)=[zeros(1,en_dim_x);enlarged_pic(1:end-1,:)];
three_d(:,:,4)=[zeros(en_dim_y,1),enlarged_pic(:,1:end-1)];
three_d(:,:,5)=[enlarged_pic(:,2:end),zeros(en_dim_y,1)];
three_d(:,:,6)=[pic,zeros(dim_y,2);zeros(2,en_dim_x)];
three_d(:,:,7)=[zeros(2,en_dim_x);pic,zeros(dim_y,2)];
three_d(:,:,8)=[zeros(dim_y,2),pic;zeros(2,en_dim_x)];
three_d(:,:,9)=[zeros(2,en_dim_x);zeros(dim_y,2),pic];
And then see if the maximum along the 3rd dimension appears in the 1st layer (that is: three_d(:,:,1)):
(max_val, max_i) = max(three_d, 3);
result = find(max_i == 1);
Is there any more elegant way to do this? This seems like a bit of a kludge.
bw = pic > imdilate(pic, [1 1 1; 1 0 1; 1 1 1]);
If you have the Image Processing Toolbox, you could use the IMREGIONALMAX function:
BW = imregionalmax(y);
The variable BW will be a logical matrix the same size as y with ones indicating the local maxima and zeroes otherwise.
NOTE: As you point out, IMREGIONALMAX will find maxima that are greater than or equal to their neighbors. If you want to exclude neighboring maxima with the same value (i.e. find maxima that are single pixels), you could use the BWCONNCOMP function. The following should remove points in BW that have any neighbors, leaving only single pixels:
CC = bwconncomp(BW);
for i = 1:CC.NumObjects,
index = CC.PixelIdxList{i};
if (numel(index) > 1),
BW(index) = false;
end
end
Alternatively, you can use nlfilter and supply your own function to be applied to each neighborhood.
This "find strict max" function would simply check if the center of the neighborhood is strictly greater than all the other elements in that neighborhood, which is always 3x3 for this purpose. Therefore:
I = imread('tire.tif');
BW = nlfilter(I, [3 3], #(x) all(x(5) > x([1:4 6:9])) );
imshow(BW)
In addition to imdilate, which is in the Image Processing Toolbox, you can also use ordfilt2.
ordfilt2 sorts values in local neighborhoods and picks the n-th value. (The MathWorks example demonstrates how to implemented a max filter.) You can also implement a 3x3 peak finder with ordfilt2 with the following logic:
Define a 3x3 domain that does not include the center pixel (8 pixels).
>> mask = ones(3); mask(5) = 0 % 3x3 max
mask =
1 1 1
1 0 1
1 1 1
Select the largest (8th) value with ordfilt2.
>> B = ordfilt2(A,8,mask)
B =
3 3 3 3 3 4 4 4
3 5 5 5 4 4 4 4
3 5 3 5 4 4 4 4
3 5 5 5 4 6 6 6
3 3 3 3 4 6 4 6
1 1 1 1 4 6 6 6
Compare this output to the center value of each neighborhood (just A):
>> peaks = A > B
peaks =
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
or, just use the excellent: extrema2.m

Getting all pixel coordinates of a vector inside a image

I have an intensity/greyscale image, and I have chosen a pixel inside this image. I want to send vectors starting from this pixel in all directions/angles, and I want to sum all the intensities of the pixels touching one vector, for all vectors.
After this step I would like to plot a histogram with the intensities on one axis and the angle on the other axis. I think I can do this last step on my own, but I don't know how to create these vectors inside my greyscale image and how to get the coordinates of the pixels a vector touches.
I previously did this in C++, which required a lot of code. I am sure this can be done with less effort in MATLAB, but I am quite new to MATLAB, so any help would be appreciated, since I haven't found anything helpful in the documentation.
It might not be the best way to solve it, but you can do it using a bit of algebra, heres how...
We know the Point-Slope formula of a line passing through point (a,b) with angle theta is:
y = tan(theta) * (x-a) + b
Therefore a simple idea is to compute the intersection of this line with y=const for all const, and read the intensity values at the intersection. You would repeat this for all angles...
A sample code to illustrate the concept:
%% input
point = [128 128]; % pixel location
I = imread('cameraman.tif'); % sample grayscale image
%% calculations
[r c] = size(I);
angles = linspace(0, 2*pi, 4) + rand;
angles(end) = [];
clr = lines( length(angles) ); % get some colors
figure(1), imshow(I), hold on
figure(2), hold on
for i=1:length(angles)
% line equation
f = #(x) tan(angles(i))*(x-point(1)) + point(2);
% get intensities along line
x = 1:c;
y = round(f(x));
idx = ( y<1 | y>r ); % indices of outside intersections
vals = diag(I(x(~idx), y(~idx)));
figure(1), plot(x, y, 'Color', clr(i,:)) % plot line
figure(2), plot(vals, 'Color', clr(i,:)) % plot profile
end
hold off
This example will be similar to Amro's, but it is a slightly different implementation that should work for an arbitrary coordinate system assigned to the image...
Let's assume that you have matrices of regularly-spaced x and y coordinates that are the same size as your image, such that the coordinates of pixel (i,j) are given by (x(i,j),y(i,j)). As an example, I'll create a sample 5-by-5 set of integer coordinates using MESHGRID:
>> [xGrid,yGrid] = meshgrid(1:5)
xGrid =
1 2 3 4 5
1 2 3 4 5
1 2 3 4 5
1 2 3 4 5
1 2 3 4 5
yGrid =
1 1 1 1 1
2 2 2 2 2
3 3 3 3 3
4 4 4 4 4
5 5 5 5 5
Next we can define a line y = m*(x - a) + b passing through the coordinate system by selecting some values for the constants and computing y using the x coordinates of the grid:
>> a = 0;
>> b = 1;
>> m = rand
m =
0.5469
>> y = m.*(xGrid(1,:)-a)+b
y =
1.5469 2.0938 2.6406 3.1875 3.7344
Finally, we find the y points in the grid that differ from the points computed above by less than the grid size:
>> index = abs(yGrid-repmat(y,size(yGrid,1),1)) <= yGrid(2,1)-yGrid(1,1)
index =
1 0 0 0 0
1 1 1 0 0
0 1 1 1 1
0 0 0 1 1
0 0 0 0 0
and use this index matrix to get the x and y coordinates for the pixels crossed by the line:
>> xCrossed = xGrid(index);
>> yCrossed = yGrid(index);