How can I plot filled rectangles as a backdrop for a desired target in MATLAB? - matlab

I have two datasets, one of which is a target position, and the other is the actual position. I would like to plot the target with a +/- acceptable range and then overlay with the actual. This question is only concerning the target position however.
I have unsuccessfully attempted the built in area, fill, and rectangle functions. Using code found on stackoverflow here, it is only correct in certain areas.
For example
y = [1 1 1 2 1 1 3 3 1 1 1 1 1 1 1]; % Target datum
y1 = y+1; %variation in target size
y2 = y-1;
t = 1:15;
X=[t,fliplr(t)]; %create continuous x value array for plotting
Y=[y1,fliplr(y2)]; %create y values for out and then back
fill(X,Y,'b');
The figure produced looks like this:
I would prefer it to be filled within the red boxes drawn on here:
Thank you!

If you would just plot a function y against x, then you could use a stairs plot. Luckily for us, you can use the stairs function like:
[xs,ys] = stairs(x,y);
to create the vectors xs, ys which generate a stairs-plot when using the plot function. We can now use these vectors to generate the correct X and Y vectors for the fill function. Note that stairs generates column vectors, so we have to transpose them first:
y = [1 1 1 2 1 1 3 3 1 1 1 1 1 1 1]; % Target datum
y1 = y+1; %variation in target size
y2 = y-1;
t = 1:15;
[ts,ys1] = stairs(t,y1);
[ts,ys2] = stairs(t,y2);
X=[ts.',fliplr(ts.')]; %create continuous x value array for plotting
Y=[ys1.',fliplr(ys2.')]; %create y values for out and then back
fill(X,Y,'b');

Again, thank you hbaderts. You answered my question perfectly, however when I applied it to the large data set I needed for, I obtained this image
https://dl.dropboxusercontent.com/u/37982601/stair%20fill.png
I think it is because the fill function connects vertices to fill?
In any case, for the potential solution of another individual, combined your suggested code with the stair function and used the area function.
By plotting them on top of one another and setting the color of the lower area to be white, it appears as the rectangular figures I was after.
%sample code. produces image similar to o.p.
y = [1 1 1 2 1 1 3 3 1 1 1 1 1 1 1];
y1 = y+1;
y2 = y-1;
t = 1:15;
[ts,ys1] = stairs(t,y1);
[ts,ys2] = stairs(t,y2);
area(ts,ys1,'FaceColor','b','EdgeColor','none')
hold on
area(ts,ys2,'FaceColor','w','EdgeColor','none')
https://dl.dropboxusercontent.com/u/37982601/stair%20area.png
Thanks again for your help and for pointing me in the right direction!

Related

How to plot a Diagonal Histogram in Matlab

Given scatter data, or a matrix, I would like to generate a nice plot such as the one shown below, with all 3 histograms and a colored matrix. I'm specifically interested in the diagonal histogram, which ideally, would correspond to the diagonals of a matrix:
Source figure: www.med.upenn.edu/mulab/jpst.html
The existing command scatterhist is not that powerful to generate this type of graph. Any ideas?
Thanks!
EDIT:
Following #Cris Luengo's hints, I came up with the following code which does some first work at the inclined histogram: WORK IN PROGRESS (HELP WELCOME)!!
b = [0 1 2 3 4 5 6 7 8 9 10];
h = [0.33477 0.40166 0.20134 0.053451 0.008112 0.000643 2.7e-05 0 0 0 0];
wid = 0.25; bb = sort([b-wid b-wid b+wid b+wid]);
kk = [zeros(numel(h),1) h(:) h(:) zeros(numel(h),1)];
kk = reshape(kk',[1,numel(kk)]);
pp=patch(bb,kk,'b');axis([-.5 5 0 .5])
set(gca,'CameraUpVector',[-1,.08,0]);axis square
EDIT 2: Using rotation
phi = pi/4;
R = [cos(phi),-sin(phi);sin(phi),cos(phi)];
rr = [bb' kk'] * R;
bb = rr(:,1); kk = rr(:,2);
patch(bb,kk,'b'); axis([-.5 3 -4 .5])
Here is a recipe to plot the diagonal histogram, if you can do that I’m sure you can figure out the rest too.
Compute the histogram, the bin counts are h, the bin centers are b.
Build a coordinate matrix, attaching the coordinates of a point on the x-axis at the left and right ends of the histogram:
coords = [b(:),h(:)];
coords = [coord;b(end),0;b(1),0];
Using patch you can now plot the histogram as follows:
patch(coords(1,:),coords(2,:));
To plot a rotated histogram you can simply multiply the coords matrix with a rotation matrix, before using patch:
phi = pi/4;
R = [cos(phi),-sin(phi);sin(phi),cos(phi)];
coords = R * coords;
You might need to shift the plot to place it at the right location w.r.t. the other elements.
I recommend that you place all these graphic elements in the same axes object; you can set the axes’ visibility to 'off' so that it works only as a canvas for the other elements.
It will be a bit of work to get everything placed as in the plot you show, but none of it is difficult. Use the low-level image, line,patch and text to place those types of elements, don’t try to use the higher-level plotting functions such as plot, they don’t provide any benefits over the low-level ones in this case.

Find contour/edge in pcolor in Matlab

I'm trying to make a contour that follows the edges of the 'pixels' in a pcolor plot in Matlab. This is probably best explained in pictures. Here is a plot of my data. There is a distinct boundary between the yellow data (data==1) and the blue data (data==0):
Note that this is a pcolor plot so each 'square' is essentially a pixel. I want to return a contour that follows the faces of the yellow data pixels, not just the edge of the yellow data.
So the output contour (green line) passes through the mid-points of the face (red dots) of the pixels.
Note that I don't want the contour to follow the centre points of the data (black dots), which would do something like this green line. This could be achieved easily with contour.
Also, if it's any help, I have a few grids which may be useful. I have the points in the middle of the pixels (obviously, as that's what I've plotted here), I also have the points on the corners, AND I have the points on the west/east faces and the north/south faces. IF you're familiar with Arakawa grids, this is an Arakawa-C grid, so I have the rho-, u-, v- and psi- points.
I've tried interpolation, interweaving grids, and a few other things but I'm not having any luck. Any help would be HUGELY appreciated and would stop me going crazy.
Cheers, Dave
EDIT:
Sorry, I simplified the images to make what I was trying to explain more obvious, but here is a larger (zoomed out) image of the region I'm trying to separate:
As you can see, it's a complex outline which heads in a "southwest" direction before wrapping around and moving back "northeast". And here is the red line that I'd like to draw, through the black points:
You can solve this with a couple of modifications to a solution I posted to a related question. I used a section of the sample image mask in the question for data. First, you will need to fill the holes in the mask, which you can do using imfill from the the Image Processing Toolbox:
x = 1:15; % X coordinates for pixels
y = 1:17; % Y coordinates for pixels
mask = imfill(data, 'holes');
Next, apply the method from my other answer to compute an ordered set of outline coordinates (positioned on the pixel corners):
% Create raw triangulation data:
[cx, cy] = meshgrid(x, y);
xTri = bsxfun(#plus, [0; 1; 1; 0], cx(mask).');
yTri = bsxfun(#plus, [0; 0; 1; 1], cy(mask).');
V = [xTri(:) yTri(:)];
F = reshape(bsxfun(#plus, [1; 2; 3; 1; 3; 4], 0:4:(4*nnz(mask)-4)), 3, []).';
% Trim triangulation data:
[V, ~, Vindex] = unique(V, 'rows');
V = V-0.5;
F = Vindex(F);
% Create triangulation and find free edge coordinates:
TR = triangulation(F, V);
freeEdges = freeBoundary(TR).';
xOutline = V(freeEdges(1, [1:end 1]), 1); % Ordered edge x coordinates
yOutline = V(freeEdges(1, [1:end 1]), 2); % Ordered edge y coordinates
Finally, you can get the desired coordinates at the centers of the pixel edges like so:
ex = xOutline(1:(end-1))+diff(xOutline)./2;
ey = yOutline(1:(end-1))+diff(yOutline)./2;
And here's a plot showing the results:
imagesc(x, y, data);
axis equal
set(gca, 'XLim', [0.5 0.5+size(mask, 2)], 'YLim', [0.5 0.5+size(mask, 1)]);
hold on;
plot(ex([1:end 1]), ey([1:end 1]), 'r', 'LineWidth', 2);
plot(ex, ey, 'k.', 'LineWidth', 2);
Take a look at the following code:
% plotting some data:
data = [0 0 0 0 0 0 1 1
0 0 0 0 0 1 1 1
0 0 0 0 1 1 1 1
0 0 0 0 0 1 1 1
0 0 0 0 1 1 1 1
0 0 0 0 1 1 1 1
0 0 0 0 1 1 1 1];
p = pcolor(data);
axis ij
% compute the contour
x = size(data,2)-cumsum(data,2)+1;
x = x(:,end);
y = (1:size(data,1));
% compute the edges shift
Y = get(gca,'YTick');
y_shift = (Y(2)-Y(1))/2;
% plot it:
hold on
plot(x,y+y_shift,'g','LineWidth',3,'Marker','o',...
'MarkerFaceColor','r','MarkerEdgeColor','none')
It produces this:
Is this what you look for?
The most important lines above is:
x = size(data,2)-cumsum(data,2)+1;
x = x(:,end);
which finds the place of shifting between 0 to 1 for every row (assuming there is only one in a row).
Then, within the plot I shift y by half of the distance between two adjacent y-axis tick, so they will be placed at the center of the edge.
EDIT:
After some trials with this kind of data, I have got this result:
imagesc(data);
axis ij
b = bwboundaries(data.','noholes');
x = b{1}(:,1);
y = b{1}(:,2);
X = reshape(bsxfun(#plus,x,[0 -0.5 0.5]),[],1);
Y = reshape(bsxfun(#plus,y,[0 0.5 -0.5]),[],1);
k = boundary(X,Y,1);
hold on
plot(X(k),Y(k),'g','LineWidth',3,'Marker','o',...
'MarkerFaceColor','r','MarkerEdgeColor','none')
It's not perfect, but may get you closer to what you want in a more simple approach:
OK, I think I've solved it... well close enough to be happy.
First I take the original data (which I call mask_rho and use this to make masks mask_u, mask_v, which is similar to mask_rho but is shifted slightly in the horizontal and vertical directions, respectively.
%make mask_u and mask_v
for i = 2:size(mask_rho,2)
for j = 1:size(mask_rho,1)
mask_u(j, i-1) = mask_rho(j, i) * mask_rho(j, i-1);
end
end
for i = 1:size(mask_rho,2)
for j = 2:size(mask_rho,1)
mask_v(j-1, i) = mask_rho(j, i) * mask_rho(j-1, i);
end
end
I then make modified masks mask_u1 and mask_v1 which are the same as mask_rho but averaged with the neighbouring points in the horizontal and vertical directions, respectively.
%make mask which is shifted E/W (u) and N/S (v)
mask_u1 = (mask_rho(1:end-1,:)+mask_rho(2:end,:))/2;
mask_v1 = (mask_rho(:,1:end-1)+mask_rho(:,2:end))/2;
Then I use the difference between the masks to locate places where the masks change from 0 to 1 and 1 to 0 in the horizontal direction (in the u mask) and in the vertical direction (in the v mask).
% mask_u-mask_u1 gives the NEXT row with a change from 0-1.
diff_mask_u=logical(mask_u-mask_u1);
lon_u_bnds=lon_u.*double(diff_mask_u);
lon_u_bnds(lon_u_bnds==0)=NaN;
lat_u_bnds=lat_u.*double(diff_mask_u);
lat_u_bnds(lat_u_bnds==0)=NaN;
lon_u_bnds(isnan(lon_u_bnds))=[];
lat_u_bnds(isnan(lat_u_bnds))=[];
%now same for changes in mask_v
diff_mask_v=logical(mask_v-mask_v1);
lon_v_bnds=lon_v.*double(diff_mask_v);
lon_v_bnds(lon_v_bnds==0)=NaN;
lat_v_bnds=lat_v.*double(diff_mask_v);
lat_v_bnds(lat_v_bnds==0)=NaN;
lon_v_bnds(isnan(lon_v_bnds))=[];
lat_v_bnds(isnan(lat_v_bnds))=[];
bnd_coords_cat = [lon_u_bnds,lon_v_bnds;lat_u_bnds,lat_v_bnds]'; %make into 2 cols, many rows
And the result grabs all the coordinates at the edges of the boundary:
Now my answer goes a bit awry. If I plot the above vector as points plot(bnd_coords_cat(:,1),bnd_coords_cat(:,2),'kx' I get the above image, which is fine. However, if I join the line, as in: plot(bnd_coords_cat(:,1),bnd_coords_cat(:,2),'-' then the line jumps around, as the points aren't sorted. When I do the sort (using sort and pdist2) to sort by closest points, Matlab sometimes chooses odd points... nevertheless I figured I'd include this code as an appendix, and optional extra. Someone may know a better way to sort the output vectorbnds_coords_cat:
% now attempt to sort
[~,I]=sort([lon_u_bnds,lon_v_bnds]);
bnd_coords_inc1 = bnd_coords_cat(I,1);
bnd_coords_inc2 = bnd_coords_cat(I,2);
bnd_coords = [bnd_coords_inc1,bnd_coords_inc2];
bnd_coords_dist = pdist2(bnd_coords,bnd_coords);
bnd_coords_sort = nan(1,size(bnd_coords,1));
bnd_coords_sort(1)=1;
for ii=2:size(bnd_coords,1)
bnd_coords_dist(:,bnd_coords_sort(ii-1)) = Inf; %don't go backwards?
[~,closest_idx] = min(bnd_coords_dist(bnd_coords_sort(ii-1),:));
bnd_coords_sort(ii)=closest_idx;
end
bnd_coords_final(:,1)=bnd_coords(bnd_coords_sort,1);
bnd_coords_final(:,2)=bnd_coords(bnd_coords_sort,2);
Note that the pdist2 method was suggested by a colleague and also from this SO answer, Sort coordinates points in matlab. This is the final result:
To be honest, plotting without the line is fine. So as far as I'm concerned this is close enough to be answered!

How do i correctly fit a line and force it through a particular point in an image?

I am to fit planes through various points in an image, but I am having issues with forcing the line through a particular point in the image. This happens particularly when the line is 90 degrees.
My code is as follows:
I = [3 3 3 3 3 2 2
3 3 3 3 2 2 2
3 3 3 3 2 2 2
3 3 1 2 2 2 2
1 1 1 2 2 2 2
1 1 1 1 1 2 2
1 1 1 1 1 1 1];
% force the line through point p
p = [3,3];
% points to fit plane through
edgeA = [3,3.5; 3,4; 2.5,4; 2,4; 1.5,4];
edgeB = [3.5,3; 4,3; 4.5,3; 5,3];
% fit a plane through p and edgeA
xws = [p(2), edgeA(:,2)']';
yws = [p(1), edgeA(:,1)']';
Cws = [xws ones(size(xws))];
dws = yws;
Aeqws = [p(2) 1];
beqws = [p(1)];
planefitA = lsqlin(Cws ,dws,[],[],Aeqws, beqws);
% fit a plane through p and edgeB
xwn = [p(2), edgeB(:,2)']';
ywn = [p(1), edgeB(:,1)']';
Cwn = [xwn ones(size(xwn))];
dwn = ywn;
Aeqwn = [p(2) 1];
beqwn = [p(1)];
planefitB = lsqlin(Cwn ,dwn,[],[],Aeqwn, beqwn);
%%%%% plot the fitted planes:
xAxis = linspace(0, size(I, 2), 12);
%obtain linear curve
fA = planefitA(1)*xAxis + planefitA(2);
fB = planefitB(1)*xAxis + planefitB(2);
%plot the fitted curve
RI = imref2d(size(I),[0 size(I, 2)],[0 size(I, 1)]);
figure, imshow(I, RI, [], 'InitialMagnification','fit')
grid on;
hold on;
plot(xAxis,fA, 'Color', 'b', 'linewidth', 2);
plot(xAxis,fB, 'Color', 'r', 'linewidth', 2);
All the points in edgeB fall on a 90 degrees line. However, the function ends up fitting a wrong line through those points. I know this because using
planefitB = polyfit([p(2), edgeB(:,2)'], [p(1), edgeB(:,1)'], 1);
works for this particular line but the problem is that i have these process repeated so many times at different locations in my image, hence i do not know how to suggest polyfit when the line would be 90 degrees.
Please, any ideas/suggestions on how i could make this work? Many thanks.
This amounts to the least squares solution of only the angle of the line. The offset is fixed by the fact that it has to go through (3,3). The easiest way to express this is by offsetting your data points by the known crossing. That is, subtract (3,3) from your data points, and fit the best m for y=mx, the b being fixed to 0.
For the non-vertical case, you can use a classic least-squares formulation, but don't augment the constant 1 into the Vandermonde matrix:
slope = (edgeA(:,2) - p(2)) \ (edgeA(:,1) - p(1));
This gives exactly the same answer as your constrained lsq solution.
Now for the vertical line: A non-vertical line can be expressed in the standard functional form of y=mx, where the least squares formulation implicitly assumes an independent and a dependent variable. A vertical line doesn't follow that, so the only general choice is a "Total Least Squares" formulation, where errors in both variables are considered, rather than just the residuals in the dependent (y) variable.
The simplest way to write this is to choose a and b to minimize ax - by, in the least squares sense. [x_k -y_k]*[a b].' should be as close to a zero vector as possible. This is the vector closest to the null space of the [x -y] matrix, which can be computed with the svd. Swapping columns and fudging signs lets us just use svd directly:
[u s v] = svd(bsxfun(#minus, edgeA, p));
The last column of v is the closest to the null space, so mapping back to your x/y definitions, (edgeA-p)*v(:,2) is the line, so the y multiplier is in the top position, and the x in the lower, with a sign flip. To convert to y=mx form, just divide:
slope = -v(2,2)/v(1,2);
Note that this answer will be quite a bit different than the normal least squares answer, since you are treating the residuals differently. Also, the final step of computing "slope" won't work in the vertical case for the reasons we've already discussed (it produces Inf), so you are probably better off leaving the line as a normalized 2-vector, which won't have any corner cases.

drow cumulative distribution function in matlab

I have two vectors of the same size. The first one can have any different numbers with any order, the second one is decreasing (but can have the same elements) and consists of only positive integers. For example:
a = [7 8 13 6];
b = [5 2 2 1];
I would like to plot them in the following way: on the x axis I have points from a vector and on the y axis I have the sum of elements from vector b before this points divided by the sum(b). Therefore I will have points:
(7; 0.5) - 0.5 = 5/(5+2+2+1)
(8; 0.7) - 0.7 = (5+2)/(5+2+2+1)
(13; 0.9) ...
(6; 1) ...
I assume that this explanation might not help, so I included the image
Because this looks to me as a cumulative distribution function, I tried to find luck with cdfplot but with no success.
I have another option is to draw the image by plotting each line segment separately, but I hope that there is a better way of doing this.
I find the values on the x axis a little confusing. Leaving that aside for the moment, I think this does what you want:
b = [5 2 2 1];
stairs(cumsum(b)/sum(b));
set(gca,'Ylim',[0 1])
And if you really need those values on the x axis, simply rename the ticks of that axis:
a = [7 8 13 6];
set(gca,'xtick',1:length(b),'xticklabel',a)
Also grid on will add grid to the plot

Generalize this matlab code for non-square matrices

I am working on some fourier transform code in matlab, and have come across the following:
xx = meshgrid(1:N);
% Center on DC
xx = xx - dcN;
% normalize dynamic range from -1 to 1
xx = xx./max(abs(xx(:)));
% form y coordinate from negative transpose of x coordinate (maintains symmetry about DC)
yy = -xx';
% compute the related radius of the x/y coordinates centered on DC
rr = sqrt(xx.^2 + yy.^2);
How can I generalize this for non-square matrices? This code is assuming my matrix is square, so dcN is the center of the square matrix (in other words, with 11x11, dcN = 6).
The math doesnt work out for that yy variable when the transpose is taken for a non-square matrix.
I have tried to figure out if I can make a meshgrid going from "top to bottom" instead of left to right - but I havent been able to figure taht out either.
Thanks
I have tried to figure out if I can
make a meshgrid going from "top to
bottom" instead of left to right - but
I havent been able to figure taht out
either.
>> N=5
N =
5
>> rot90(meshgrid(N:-1:1))
ans =
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
From your question I guess that you want to find rr, i.e. the distance of any element in the matrix from the center.
If you want this for a M-by-N array, you'd do the following
%# note that using meshgrid instead of ndgrid will swap xx and yy
[xx,yy] = ndgrid(-(M-1)/2:(M-1)/2,-(N-1)/2:(N-1)/2);
%# normalize to the max of xx,yy
nrm = max((M-1)/2,(N-1)/2);
xx = xx./nrm;
yy = yy./nrm;
rr = sqrt(xx.^2+yy.^2)