Annotation - Enforce a square using imrect - matlab

I have a Matlab program that asks the user to draw a rectangle around a human on the scene (I later extract that region using imcrop). I need to enforce the user to draw a square. I am using the imrect function, but I am unable to force a square, nor find documentation on how to do it.

It seems imrect can take a position-constraining function as an input argument. This function is specified as follows:
Whenever the object is moved because
of a mouse drag, the constraint function is called using
the syntax:
constrained_position = fcn(new_position)
Position is a vector of the form [xleft ybottom width height].
So try this:
axis equal %// same sccale on both axes
axis manual %// freeze axes size
h = imrect('PositionConstraintFcn', #(x) [x(1) x(2) min(x(3),x(4))*[1 1]])

The simplest way would be to set the setFixedAspectRatioMode method to true during the rectangle creation, initially drawing a square. (Check here).
Example:
%// Make sure it's initially a square!!
hRect = imrect(gca, [10 10 100 100]);
setFixedAspectRatioMode(hRect,1)
Then no matter how you change the position, it will remain a square. Note, however, that as opposed to Luis' solution the user can't specify the initial placement of the square.

Related

'drawcircle' and 'roi.cricle' do not respect preset Radius

I am trying to make a circular roi with a specified R,but allowing to set the center and the position. I first thought of doing it with 'ginput' and later drawing the circle, but since I need to allow for interactive reposition, I switched to 'roi' creation (image.roi.circle).
However, for some reason, something happens with the Radius and it's never set as defined, or it allows interactive setting of the radius... I tried both with 'drawcircle' and 'draw' (predefining the roi), but none works for me as I want to.
This is the code for 'drawcircle':
figure
imagesc(background);
drawncircle = drawcircle('InteractionsAllowed', 'translate','Radius',6, 'LineWidth',1.5);
Warning: The ROI was not fully defined. 'Radius' will be ignored and interactive placement will begin.
> In drawcircle (line 187)
And here the code for roi definition + draw():
figure
imagesc(background);
drawnroi = images.roi.Circle('InteractionsAllowed', 'translate','Radius',6, 'LineWidth',1.5);
draw(drawnroi);
In both cases, the first time I run it, it yields the warning, sets the Radius to the minimum, and the field 'Radius' of the roi is set to zero.
However, if I run it again once the object has been created, what it does is to interactively allow the radius definition. (while in the permisions I only allow for moving it!).
Does anyone know what am I doing wrong or how can I get what I need? (thanks in advance)
The drawcircle function, requires the center coordinate of the circle.
You may use ginput for interactively getting the initial center position, then executing drawcircle with the selected center.
Here is a code sample:
background = zeros(32, 64, 'uint8'); % Create sample background
figure
imagesc(background);
axis image % Use axis image, for setting aspect ratio to 1:1
% Get the initial center position from the user
[x, y] = ginput(1);
% Draw a cicle, and allow the user to change the position
drawncircle = drawcircle('Center', [x, y], 'InteractionsAllowed', 'translate', 'Radius', 6, 'LineWidth', 1.5);

draw 2 pairs of resizable rectangles in matlab

I have a program that should draw 2 re sizable rectangle in matlab. drawing two resizable is ok, but I want to change size in the same time, I mean when I change size of the first one, the second'size change too. but I don know haw to connect them together. can any body help me?!
thanks.
here is my code:
figure,imshow('image.jpg');
h1 = imrect(gca, [10 10 300 500]);
h2 = imrect(gca, [200 200 400 300]);
To solve this you have to use the addNewPositionCallback.
Write your own function implementing the logic you need. This example sets all rectangles to the same size:
function myPositionFunction(allRects,changed,position)
%iterate over all rectangles
for idx=1:numel(allRects)
%skip the currently changed one
if idx~=changed
%get position, modify it and set it
thisP=allRects(idx).getPosition();
thisP(3:4)=position(3:4);
allRects(idx).setPosition(thisP);
end
end
end
Now the tricky part, how make practical usable callback:
figure
imshow('board.tif')
h1 = imrect(gca, [10 10 20 20]);
h2 = imrect(gca, [20 20 30 30]);
addNewPositionCallback(h1,#(p)myPositionFunction([h1,h2],1,p))
addNewPositionCallback(h2,#(p)myPositionFunction([h1,h2],2,p))
This way your callback is called with:
-first parameter a list of all rectangles (could be extended to more than two)
-second parameter the index of the changed rectangle
-third parameter the new boundaries of the changed rectangle
I see #Daniel has just posted an answer, but here is an alternative solution using addNewPositionCallback as well.
1) First create a function in which you draw a first rectangle, and add the callback:
function Draw2Rect(~)
global h1
A = imread('peppers.png');
imshow(A);
h1 = imrect(gca,[20 20 200 150]);
addNewPositionCallback(h1,#UpdateRect)
2) Then define the anonymous function called by the callback, in which you 1st use findobj in order to find rectangles drawn in the current axes. imrect objects are of the type 'hggroup'. Basically you look for all the rectangles present and as you move the first rectangle previous ones are deleted and a new rectangle is drawn. Here I used a dummy relation between the positions but you get the idea.
function UpdateRect(~)
global h1 % Simpler to use global variables but there are more robust alternatives!
Rect1Pos = getPosition(h1); % Get the position of your rectangle of interest
hRect = findobj(gca,'Type','hggroup'); % Find all rectangle objects
if numel(hRect>1) Once you start changing the first rectangle position, delete others.
delete(hRect(1:end-1))
h2 = imrect(gca,round(Rect1Pos/2));
end
I don't know how to post animated gifs in answers, but here are 2 images showing the 1st rectangle and then the 2 rectangles after moving the first one:
1:
2:
Hope that helps! As noted I used h1 as a global variable to easily pass data between functions, but there are more robust alternatives you can use.

Making a 2D Matlab figure that contains multiple images which start at a given point (x,y)

Problem
I am trying to make a 2D figure in Matlab which consists of multiple images and a graph with plot data (which I could eventually convert into an image too). For these images and graph, I need to be able to specify where they are located in my cartesion coordinate system.
For my specific case, it is sufficient to be able to "tell" Matlab where the left-bottom corner of the image is.
So for the example above. I would need some "trick" to let "bird1.jpg" start at position (a,b), "bird2.jpg" at position (c,d) and my plot at position (e,f) in one Matlab figure.
Solution to problem
Thanks to chappjc I was able to find a solution for my problem. Here is the code I used such that other people can use it in the future too.
figure_color = [.741 .717 .42];
axe_color = [1 1 1];
Screen.fig = figure('units','pixels',...
'name','Parallel projection',...
'menubar','none',...
'numbertitle','off',...
'position',[100 100 650 720],...
'color',figure_color,...
'busyaction','cancel',...
'renderer','opengl');
Screen.axes = axes('units','pix',...
'position',[420 460 200 200],... % (420,460) is the position of the first image
'ycolor',axe_color,...
'xcolor',axe_color,...
'color',axe_color,...
'xtick',[],'ytick',[],...
'xlim',[-.1 7.1],...
'ylim',[-.1 7.1],...
'visible','On');
Screen.img = imshow(phantom);
Screen.axes2 = axes('units','pix',...
'position',[0 0 200 200],... % (0,0) is the position of the second image
'ycolor',axe_color,...
'xcolor',axe_color,...
'color',axe_color,...
'xtick',[],'ytick',[],...
'xlim',[-.1 7.1],...
'ylim',[-.1 7.1],...
'visible','On');
Screen.img2 = imshow(phantom);
Basically what I do is first creating a (big) figure, and then create a first axe at a certain position in this big picture, and make it the default axe. In this axe I display my first image (made with the phantom function). After that I make a new axe at a another position and make it again the default axe. After I have done that, I place an image there too (the same picture, but you can also use another one if you want). You can also use handles which is the more clean method, as chappjc describes.
Positioning axes in a figure
One approach would be to manipulate the Position property of multiple axes in a figure. To make multiple axes in a figure:
hf = figure;
ha0 = axes('parent',hf,'Position',[x0 y0 w0 h0]);
ha1 = axes('parent',hf,'Position',[x1 y1 w1 h1]);
Then display your images and plots into the axes by specifying the handle (i.e. ha0 or ha1). For example: image(img0,'Parent',ha0) or imshow(img1,'parent',ha1).
Single Large Image
Another approach is to make a single large image and simply display it with image/imshow/etc.
First for the plots, you can use getframe followed by frame2im to get an image in a matrix format.
Next, decide what goes into your combined image and compute the largest box required to circumscribe the images (using their origins and sizes find the largest x and y coordinate), which includes the origin presumably. Use this info to make a blank image (e.g. img = zeros(h,w,3) for and RGB image).

How to add a circle to simple matlab figure?

I have a plot of some data (simple 2-d line) and I would like to add circles around some more interesting spots on it. Surprisingly matlab seems to have no simple way to create a physically round circles. I looked on the internet and most answers i found was to either use rectangle('Curvature',[1 1]) or pts = linspace(0,2*pi, 100); plot(sin(pts), cos(pts)); and fixing the aspect ration of a plot to 1. In my case axes have scales that differ by several orders of magnitude so fixing the aspect ration is no option.
I was trying different ways to get the right x/y scale factor but it still seems I'm missing something. My current attempt is:
function hc = circle(x, y, xr)
gca_ylim = get(gca, 'ylim');
gca_xlim = get(gca, 'xlim');
gca_pos = get(gca, 'Position');
gcf_pos = get(gcf, 'Position');
gcf_ar = get(gca, 'DataAspectRatio');
%mod = gca_pos(4)/gca_pos(3) *abs(gca_ylim(2)-gca_ylim(1))/abs(gca_xlim(2)-gca_xlim(1))*gcf_pos(3)/gcf_pos(4);
mod = gca_pos(4)/gca_pos(3)*gcf_ar(2)/gcf_ar(1)*gcf_pos(3)/gcf_pos(4);
yr = xr*mod;
rectangle('Position',[x-xr,y-yr,xr*2,xr*mod*2], 'Curvature',[1,1]);
end
The circles that i got that way are still a bit elongated and I have no idea why. If there is any simple method to get circles in a plot - please share.
PS I know that if I resize plot or add some more stuff to it and change scaling the circles will re-scale with the entire plot. This is not a problem in my case - Figure gets printed out without manual manipulation (no window resizing) and I can add them as the last objects.
Another option:
>> h = plot(rand(1,5),rand(1,5),'o');
>> set(h, 'MarkerSize', 100);
If you want scale invariant circles, you could use scatter command. You can also set the size smaller or larger.
scatter(X,Y,S) draws each circle with the size specified by S. To plot
each circle with equal size, specify S as a scalar. To plot each
circle with a specific size, specify S as a vector with length equal
to the length of X and Y.
http://www.mathworks.com/help/matlab/ref/scatter.html

In matlab, how can I zoom in on a plot in my script

I'd like to zoom in on a plot using a script. I'm only interested in horizontally constrained zooming. So I'd like to do something like
p = plot(myData);
z = zoom;
set(z, 'ZoomInToPoints' , [50 100]);
or
p = plot(myData);
myZoom([50, 100]);
So either of these functions would zoom into a plot like when you zoom in with the magnifying glass tool. I only specify two points because I only want to zoom horizontally.
Note, I've already tried to use xlim for this. While it works, it doesn't let me use the command text on my plots, which I need.
Calls to text will fix the text at a specific set of coordinates on the graph. Have you tried updating these after calling xlim?
EDIT: You can always adjust the text position:
x=1:.1:10;
y=sin(.1*x);
plot(x,y)
text(6,.8,'test') %#Sample figure
F=get(0,'children'); %#Figure handle
A=get(F,'Children'); %#Axes handle
T=findobj(A,'Type','text'); %# Text handle
oldxlim=xlim; %#grab the original x limits before zoom
oldpos=get(T,'Position'); %#get the old text position
set(A,'xlim',[5 15]); %#Adjust axes
newxlim=xlim;
newpos=[(oldpos(1)-oldxlim(1))*(diff(newxlim))...
/(diff(oldxlim))+newxlim(1) oldpos(2:end)];
%#interpolate to place the text at the same spot in the axes
set(T,'Position',newpos) %#Finally reset the text position
Not pretty, but it should work. If you have more than one annotation per axes or axes per figure, you can always throw the above code in a loop.
What is the problem with text and xlim? Is this not the type of behavior you want?
plot(1:100,randn(100,1))
text(80,1.5,'text')
set(gca,'XLim',[70 100]) % notice that text stays at same point in "data space" but moves in "axis space"
text(80,1,'text2'); % new text appears in axis space as well
If I'm misunderstanding and you want text to appear at a specific point in your axis space (not the data space that text uses) regardless of how zoomed in you are, you can create another set of axes for your text:
inset_h = axes('position',[0.5 0.5 0.2 0.2])
set(inset_h,'Color','none'); axis off
text(0,0,'text')