MATLAB: Finding minimum within a selected histogram region - matlab

So I created an image histogram in Matlab, and the user is able to use imrect to select a desired region.
I would like to be able to find the minimum y-value from the region that the user selects.
Here's what I have so far:
handles.pet_hist_rect = imrect(gca);
[xmin ymin width height] = getPosition(handles.pet_hist_rect);
xdata = get(findobj(gcf,'type','patch'),'xdata');
ydata = get(findobj(gcf,'type','patch'),'ydata');
I'm not sure how to extract the minimum y-value (from ydata) in the range [xmin, xmin+width]
Thanks in advance!

I think your code doesn't run in the first place, cause you are trying to assign the output of getPosition (a 1x4 array) to the single entries of another array, which doesn't work. After correcting this to
position = getPosition(handles.pet_hist_rect);
you can now access xmin as position(1), ymin as position(2) and so on.
Now, ymin = position(2) is already what you are asking for (minimum of y), but I'm not sure, I'm getting you right here. There is no need to query the graphics properties. If this is not what you are looking for, you are going to have to rephrase the question a little bit.
Edit: The following is super crude, should work, does not work however, if any of the histogram counts are zero!
close all;
hist(rand(1000, 1));
handles.pet_hist_rect = imrect(gca);
position = getPosition(handles.pet_hist_rect);
xdata = get(findobj(gcf,'type','patch'),'xdata');
ydata = get(findobj(gcf,'type','patch'),'ydata');
x = xdata{1};
x(x < position(1))=0;
x(x > position(1) + position(3))=0;
x(x>0) = 1;
y = ydata{1}(logical(x));
y(y==0) = NaN;
m = min(y);

Related

Find the real time co-ordinates of the four points marked in red in the image

To be exact I need the four end points of the road in the image below.
I used find[x y]. It does not provide satisfying result in real time.
I'm assuming the images are already annotated. In this case we just find the marked points and extract coordinates (if you need to find the red points dynamically through code, this won't work at all)
The first thing you have to do is find a good feature to use for segmentation. See my SO answer here what-should-i-use-hsv-hsb-or-rgb-and-why for code and details. That produces the following image:
we can see that saturation (and a few others) are good candidate colors spaces. So now you must transfer your image to the new color space and do thresholding to find your points.
Points are obtained using matlab's region properties looking specifically for the centroid. At that point you are done.
Here is complete code and results
im = imread('http://i.stack.imgur.com/eajRb.jpg');
HUE = 1;
SATURATION = 2;
BRIGHTNESS = 3;
%see https://stackoverflow.com/questions/30022377/what-should-i-use-hsv-hsb-or-rgb-and-why/30036455#30036455
ViewColoredSpaces(im)
%convert image to hsv
him = rgb2hsv(im);
%threshold, all rows, all columns,
my_threshold = 0.8; %determined empirically
thresh_sat = him(:,:,SATURATION) > my_threshold;
%remove small blobs using a 3 pixel disk
se = strel('disk',3');
cleaned_sat = imopen(thresh_sat, se);% imopen = imdilate(imerode(im,se),se)
%find the centroids of the remaining blobs
s = regionprops(cleaned_sat, 'centroid');
centroids = cat(1, s.Centroid);
%plot the results
figure();
subplot(2,2,1) ;imshow(thresh_sat) ;title('Thresholded saturation channel')
subplot(2,2,2) ;imshow(cleaned_sat);title('After morpphological opening')
subplot(2,2,3:4);imshow(im) ;title('Annotated img')
hold on
for (curr_centroid = 1:1:size(centroids,1))
%prints coordinate
x = round(centroids(curr_centroid,1));
y = round(centroids(curr_centroid,2));
text(x,y,sprintf('[%d,%d]',x,y),'Color','y');
end
%plots centroids
scatter(centroids(:,1),centroids(:,2),[],'y')
hold off
%prints out centroids
centroids
centroids =
7.4593 143.0000
383.0000 87.9911
435.3106 355.9255
494.6491 91.1491
Some sample code would make it much easier to tailor a specific solution to your problem.
One solution to this general problem is using impoint.
Something like
h = figure();
ax = gca;
% ... drawing your image
points = {};
points = [points; impoint(ax,initialX,initialY)];
% ... generate more points
indx = 1 % or whatever point you care about
[currentX,currentY] = getPosition(points{indx});
should do the trick.
Edit: First argument of impoint is an axis object, not a figure object.

3d plot with axes of same length

I have some 3D trajectories I want to plot.
Since they vary a lot in XY, but much less in Z, the default plot3 is misleading, because it automatically scales axes.
I've been told to use axes equal but it has no effect (see the commented line where I used it).
I came up with this code, which in my opinion is very long to achieve a so simple task:
[D,rate]=read_vicon_ascii('csvdata/a1-0.csv');
% or replace above line with D=csvread('stackoverflow-31289872.csv');
% get stackoverflow-31289872.csv at https://drive.google.com/file/d/0B5GjKiDZk3F5UHlVQUxKeFo4SG8/view?pli=1
% indices of X,Y,Z columns
X = 1+(2:3:14);
Y = 2+(2:3:14);
Z = 3+(2:3:14);
Bounds = [ min(min(D(:,X))) max(max(D(:,X)))
min(min(D(:,Y))) max(max(D(:,Y)))
min(min(D(:,Z))) max(max(D(:,Z))) ];
MaxDelta = max(Bounds(:,2)-Bounds(:,1));
SquareBounds = Bounds;
for xyz=1:3
Delta = SquareBounds(xyz,2) - SquareBounds(xyz,1);
SquareBounds(xyz,:) = SquareBounds(xyz,:) + (MaxDelta - Delta) * [-0.5 0.5];
end
figure
hold on
for i=1:5
plot3(D(:,X(i)),D(:,Y(i)),D(:,Z(i)),'r-')
end
xlim(SquareBounds(1,:))
ylim(SquareBounds(2,:))
zlim(SquareBounds(3,:))
%axes equal
hold off
Is there any way to make it better. (or a correct usage of axes equal if that does what is supoosed to do?)

Save cropped figure in Matlab

I have opened a highly complex .fig figure from file in matlab with openfig.
After this I have cropped the figure with
axis([X1, X2, Y1, Y2])
I now want save the cropped figure so that it is of a reduced size when saved. The problem is that savefig will save the complete image zoomed to the axis specified. How can I save the figure, permanently cropped to the axis I have specified? e.g. it should not be possible to zoom out when opening the new image.
I don't have MATLAB in front of me, and I don't have your data to test this with, so this is an approximate answer, but as #natan mentions in the comments above, you can harvest the data from the figure, which is stored in 'XData' and 'YData'. You can then crop the data to just the parts you want and then replace the existing data with the cropped data. It will end up looking something like this:
kids = findobj(gca,'Type','line');
K = length(kids);
Xrange = get(gca,'XLim');
Yrange = get(gca,'YLim');
for k = 1:K
X = get(kids(k),'XData');
Y = get(kids(k),'YData');
idx = X >= min(Xrange) & X <= max(Xrange) & Y >= min(Yrange) & Y <= max(Yrange);
set(kids(k),'XDATA',X(idx),'YDATA',Y(idx));
end
You'll have to modify this if your plots have patch objects, bar objects, etc., but the idea is there.
Edge cases:
There are a few edge cases to consider as #Jan has rightly pointed out. The first is that the approach above assumes a rather high density of points even in the expanded scales and will leave a small gap between the end of the line and the axes. For low-point-density lines, you need to expand the idx variable to capture the next point outside the axes in either direction:
idx_center = X >= min(Xrange) & X <= max(Xrange) & Y >= min(Yrange) & Y <= max(Yrange);
idx_plus = [false idx(1:end-1)];
idx_minus = [idx(2:end) false];
idx = idx_center | idx_plus | idx_minus;
That will only expand the number of points if there is at least one point inside the window. The other edge case is where the line passes through the window but does not contain any points inside the window, which would be the case if idx is all false. In that case, you need the largest point outside the left axis and the smallest point outside the right axis. If either of these searches come up empty, then the line doesn't pass through the window and can be discarded.
if ~any(idx)
idx_low = find(X < min(Xrange),1,'last');
idx_high = find(X > max(Xrange),1,'first');
if isempty(idx_low) | isempty(idx_high)
% if there aren't points outside either side of the axes then the line doesn't pass through
idx = false(size(X));
else
idx = idx_low:idx_high;
end
end
This doesn't test whether the line is inside the y-bounds, so it may be possible that the line will pass above or below the window without intersecting. If that is important you will need to test the line connecting the found points. An extra 2 points in memory should not be a big deal. If it is an issue, then I leave it as an exercise to the student to work out the test of whether a line passes through the axes.
Putting it all together:
kids = findobj(gca,'Type','line'); % find children of the axes that are lines
K = length(kids); % count them
Xrange = get(gca,'XLim'); % get the axis limits
Yrange = get(gca,'YLim');
for k = 1:K
X = get(kids(k),'XData'); % pull the x-y data for the line
Y = get(kids(k),'YData');
% find the points inside the window and then dilate the window by one in
% each direction
idx_center = X >= min(Xrange) & X <= max(Xrange) & Y >= min(Yrange) & Y <= max(Yrange);
idx_plus = [false idx(1:end-1)];
idx_minus = [idx(2:end) false];
idx = idx_center | idx_plus | idx_minus;
% handle the edge case where there are no points in the window but the line still passes through
if ~any(idx)
idx_low = find(X < min(Xrange),1,'last');
idx_high = find(X > max(Xrange),1,'first');
if isempty(idx_low) | isempty(idx_high)
% if there aren't points outside either side of the axes then the line doesn't pass
% through
idx = false(size(X));
else
% numerical indexing instead of logical, but it all works the same
idx = idx_low:idx_high;
end
end
if any(idx)
% reset the line with just the desired data
set(kids(k),'XDATA',X(idx),'YDATA',Y(idx));
else
% if there still aren't points in the window, remove the object from the figure
delete(kids(k));
end
end

Positive & Negitive Log10 Scale Y axis in Matlab

Hi i'm having a problem where I have a dataset which ranges between -10^3 to 10^3
I need to be able to plot this as with a log scale but semilogy cannot plot negative values
Say for example my data is:
x = [-3,-2,-1,0,1,2,3];
y = [-1000,-100,-10,1,10,100,1000];
(or in general y=sign(x).*10.^abs(x);)
How can I plot this in MATLAB with a log scale? If possible It would be great if the log scale ticks could be on the Y-axis too
Use your actual data as labels, but scale the plotted data with log10.
% data
x = -3:0.1:3;
y = sign(x).*10.^abs(x);
% scaling function
scale = #(x) sign(x).*log10(abs(x));
N = 7; % number of ticks desired
% picking of adequate values for the labels
TickMask = linspace(1,numel(y),N);
YTickLabels = y(TickMask);
% scale labels and plotdata, remove NaN ->inconsistency, do you really want that?
YTick = scale( YTickLabels );
Y = scale(y);
YTick(isnan(YTick)) = 0;
Y(isnan(Y)) = 0;
% plot
plot(x,Y)
set(gca,'YTick',YTick,'YTickLabels',YTickLabels)
grid on
For N = 7:
For N = 11
How to find a valid value for N?
The following function (thanks to gnovice) will return all possible values you could choose for N:
n = numel(x);
N = find(rem(n./(1:n), 1) == 0) + 1;
about the semilogy-style labels: by adding the following line before the plot:
YTickLabels = cellfun(#(x) ['10^' num2str(x)], num2cell(YTick),'UniformOutput',false)
you could at least achieve something like this:
not beautiful and not generic, but a good point to start for you.
The reason you can't make a logarithmic axis that crosses zero, is that it doesn't make sense!
Since a logarithmic scale is generally displayed as eg. 100 - 10 - 1 - 1/10 - 1/100 - ..., you would need an infinite amount of space to make the axis cross zero.
How about this:
x=logspace(-3,3);
y=sign(x).*10.^abs(x);
loglog(x,y)
#thewaywewalk has already given a beautiful solution to it. The one I'm suggesting is an epsilon improvement on it. If you make two changes
(a) Define a new MATLAB function signia that basically extracts the sign before a number.
function value = signia(x)
if(x>=0)
value = '';
else
value = '-';
end
and (b) make this little change that instead of
YTickLabels = cellfun(#(x) ['10^' num2str(x)], num2cell(YTick),'UniformOutput',false)
you use
YTickLabels = cellfun(#(x) [signia(x) '10^{' num2str(x) '}'], num2cell(YTick),'UniformOutput',false);
(notice the presence of curly braces), you'll get an improvement in the Y ticks display. I got the following.
enter image description here

In Matlab how do I change the arrow head style in quiver plot?

I would like to change the default arrow head style in quiver plot. How can I change it?
For Matlab Version > R2014b
Since R2014b version, Matlab has modified the structure of its graphical components. Here is the up-to-date code that uses Matlab's annotations.
is produced by
headWidth = 8;
headLength = 8;
LineLength = 0.08;
%some data
[x,y] = meshgrid(0:0.2:2,0:0.2:2);
u = cos(x).*y;
v = sin(x).*y;
%quiver plots
figure('Position',[10 10 1000 600],'Color','w');
hax_1 = subplot(1,2,1);
hq = quiver(x,y,u,v); %get the handle of quiver
title('Regular Quiver plot','FontSize',16);
%get the data from regular quiver
U = hq.UData;
V = hq.VData;
X = hq.XData;
Y = hq.YData;
%right version (with annotation)
hax_2 = subplot(1,2,2);
%hold on;
for ii = 1:length(X)
for ij = 1:length(X)
headWidth = 5;
ah = annotation('arrow',...
'headStyle','cback1','HeadLength',headLength,'HeadWidth',headWidth);
set(ah,'parent',gca);
set(ah,'position',[X(ii,ij) Y(ii,ij) LineLength*U(ii,ij) LineLength*V(ii,ij)]);
end
end
%axis off;
title('Quiver - annotations ','FontSize',16);
linkaxes([hax_1 hax_2],'xy');
Please note that this piece of code changes the head style and controls for the length of the line (in the left panel, you can see that arrows overlap on the upper-left part of the left subplot, while it does not on the right subplot). The length and width of the arrow heads are not modified.
For this edit, I didn't keep the colors scheme that coded for the angle, and discarded the dynamic head size. It makes things clearer.
For Matlab Version < R2014b
Quiver plots are hard to modify. As #Luis Mendo said, you can modify the quiver function within the matlab install. However, you will still be limited by the complexity of programmatically drawing arrows with nice patches/lines. There might be an easier route using annotation - see the "Quiver - annotation" subplot that sets the headStyle property to cback1.
Annotations are graphical objects (lines, textboxes, arrows, ...) that you can be easily inserted by hand once a plot is done. They display additional text or point to a particular area for example. You can also insert them programmatically by defining their positions - and that's the option we will take. We first draw a regular quiver plot (left panel), get the blue lines' X and Y data, and use these coordinates to insert annotation arrows, each of them being displayed at the exact same location (same position, same angle, same size; right panel).
Annotation arrows have some nice properties you can easily modify, such as Color, HeadWidth, HeadLength, and HeadStyle. In the following plot, I modified each arrow's color depending on its angle against the x-axis, and headWidth that depends length.
The following picture
is produced by
%some data
[x,y] = meshgrid(0:0.2:2,0:0.2:2);
u = cos(x).*y;
v = sin(x).*y;
%quiver plots
figure('Position',[10 10 1000 600],'Color','w');
hax_1 = subplot(1,2,1);
%left version (regular)
hq1 = quiver(x,y,u,v);
%get the line position (first handle)
hkid = get(hq1,'children');
X = get(hkid(1),'XData');
Y = get(hkid(1),'YData');
axis off;
title('Quiver - regular ','FontSize',16);
%right version (with annotation)
hax_2 = subplot(1,2,2);
cmap = jet(116); %colormap, 116 because angles goes up to 115 degrees
for ii = 1:3:length(X)-1
headWidth = 200 * sqrt((X(ii+1)-X(ii)).^2 + (Y(ii+1)-Y(ii)).^2); % set the headWidth, function of length of arrow
angled = floor(atan2(Y(ii+1)-Y(ii),X(ii+1)-X(ii))*180/pi) + 1; %get the angle
ah = annotation('arrow',...
'Color', cmap(angled,:),...
'headStyle','cback1','HeadLength',50,'HeadWidth',headWidth);
set(ah,'parent',gca);
set(ah,'position',[X(ii) Y(ii) X(ii+1)-X(ii) Y(ii+1)-Y(ii)]);
end
axis off;
title('Quiver - annotations ','FontSize',16);
linkaxes([hax_1 hax_2],'xy');
The file refresh.m located in folder ...\MATLAB\...\toolbox\matlab\specgraph\#specgraph\#quivergroup\#quivergroup contains the following lines:
%// Arrow head parameters
alpha = .33; %// Size of arrow head relative to the length of the vector
beta = .25; %// Width of the base of the arrow head relative to the length
Changing the values of alpha and beta achieves the desired effect.
However, this entails modifying Matlab's files, and thus it's not recommended. If you do it, keep a copy of the original refresh.m file.
Results using the example code that appears in quiver's help:
[x,y] = meshgrid(-2:.2:2,-1:.15:1);
z = x .* exp(-x.^2 - y.^2); [px,py] = gradient(z,.2,.15);
quiver(x,y,px,py), hold off, axis image
With original parameters (alpha = .33; beta = .25;):
With alpha = .5; beta = .5;:
You can start here:
http://www.mathworks.com/help/matlab/ref/quiver.html
and then you can look for the available properties of quiver here:
http://www.mathworks.com/help/matlab/ref/quivergroupproperties.html
For example, the property MaxHeadSize allows to change the size of the arrow-heads.
EDIT: More information in this link: Arrow properties
Read at bottom, where says
You can select an annotation and then choose Show M-code to obtain a
code snippet that you can insert in a function or script to reproduce
the annotation.
This answer of pablo1977 was the most instructive for me. I managed to get bigger arrow heads by adjusting the quiver group properties, namely by these 2 lines of code:
h = quiver(...);
set(h,'MaxHeadSize',1e2,'AutoScaleFactor',1);
Check out arrow3() from the MATLAB file-exchange
https://www.mathworks.com/matlabcentral/fileexchange/14056-arrow3
In addition to these examples.
https://kr.mathworks.com/matlabcentral/mlc-downloads/downloads/submissions/14056/versions/16/previews/arrow3_examples.html
It is faster than the annotation command, and produces similar results.
Using the above examples
headWidth =0.8; % 1/10 of annotation
headLength=0.8; % 1/10 of annotation
LineLength = 0.08; % same as annotation
[x,y] = meshgrid(0:0.2:2,0:0.2:2);
u = cos(x).*y;
v = sin(x).*y;
figure();
%hq = quiver(x,y,u,v);
p1 = [x(:) y(:)]; % data start point
u = u(:); v=v(:);
arrow3(p1,p1+LineLength*[u,v],'k',headWidth,headLength);
Sorry I can't post a picture of this plotted, since I need to earn more reputation points. The arrowheads are closed and all similar size, like the annotation command would give.