Normalized units for annotations dont add up - matlab

I am creating a color map for data of size(7x24) that I have , lets replace it with some random numbers
b = randi(50,7,24);
t = imagesc(b,[min(min(b)) max(max(b))]);
now inorder to add annotations I have to know the exact starting and ending point of my axes so that i can add a rectangle to select each point in the image
xPOSITION = get(gca,'Position')
xPOSITION =
0.1300 0.1100 0.7750 0.8150
annotation('rectangle',[0.13 0.11 (0.7750 - 0.13)/24 (0.8150 -0.11)/7],'FaceColor','blue','FaceAlpha',.2)
ok now when i try to add an annotation to the exact starting point of the data , the starting point seem to be fine but the size of the rectangular which should actually be equal to each point is alot smaller
according to my calculation each box is equal to (0.7750 - 0.13)/24 X(0.8150 -0.11)/7 , because the units are normalized , am I doing any mistake in calculation ? or the annotation works in a different way ? any help would be highly appreciated
UPDATE just to test I added 0.11 to each dimension of the annotation and it seem to be the exact size for the reason i cannot figure out
annotation('rectangle',[0.13 0.11 ((0.7750 - 0.13) +0.11)/24 ((0.8150 -0.11)+0.11)/7],'FaceColor','blue','FaceAlpha',.2)

The Position property is the [left bottom width height] not [left bottom right top] as it seems that you're treating it (since you're subtracting element 1 from 3 and 2 from 4). To correctly compute the rect for displaying you'll just want to divide the width and height components by the number of elements in those dimensions.
annotation('rectangle', [xPOSITION(1), xPOSITION(2), ...
xPOSITION(3)/size(b, 2), xPOSITION(4) / size(b,1)])
Or more simply:
annotation('rectangle', xPOSITION ./ [1 1 fliplr(size(b))])
That being said, if you're simply wanting to draw rectangles on your data, you're likely better off just creating a rectangle object which is automatically in the units of your data
rectangle('Position', [0.5 6.5 1 1], 'LineWidth', 5)

Related

Automatically convert pixels to millimeters in Mathematica

I can get the drop contour through a GetDropProfile command.
However, I can't find the conversion factor from pixels to millimeters. As the contour of the drop is obtained point by point starting from left to right, then the first ordered pair in the list gives the coordinates of the first pixel on the left. Consequently, the last ordered pair gives the value of the last pixel on the right. Since they are opposite each other, they therefore have the same y, so the difference in x of these two points is the diameter of the drop. How can I automate this process of converting pixels into millimeters and viewing the graph in millimeters, smoothing the contour of the discrete curve automatically giving us how many points to the right and left we should take?
It follows the image of the drop and the contour in pixels obtained.
As posted here, assuming the axes are in millimetres, the scale can be obtained from the x-axis ticks, which can be sampled from the row 33 from the bottom. As can be observed by executing the code below, the left- and rightmost ticks occupy one pixel each, coloured RGB {0.4, 0.4, 0.4}. So there are 427 pixels per 80mm.
img = Import["https://i.stack.imgur.com/GIuYq.png"];
{wd, ht} = ImageDimensions[img];
data = ImageData[img];
(* View the left- and rightmost pixel data *)
Take[data[[-33]], 20]
Take[data[[-33]], -20]
p1 = LengthWhile[data[[-33]], # == {1., 1., 1.} &];
p2 = LengthWhile[Reverse[data[[-33]]], # == {1., 1., 1.} &];
p120 = wd - p1 - p2 - 1
427
(* Showing the sampled row in the graphic *)
data[[-33]] = ConstantArray[{1, 0, 0}, wd];
Graphics[Raster[Reverse[data]]]
You might ask about smoothing the curve here https://mathematica.stackexchange.com

Consistently generate line at at a specified angle as measured by user in MATLAB

Consider the simple code below that generates a straight downward sloping line in MATLAB.
clear, clc, close all
t = 0:0.1:1;
y = -t+1;
plot(t,y)
ax = gca
This is a line with slope -1, so the (acute) angle between the horizontal axis and the line is 45 degrees. Except it isn't when you measure with a protractor on your monitor.
Without changing the range of values displayed on the x and y axes or the height of the figure window, how could I ensure I would measure 45 degrees from the horizontal axis to the line if I held a protractor up to the screen?
My current approach is to change the width of the figure window. In the limit as the figure window is infinitely thin, the line x is a vertical line. Conversely, if the figure window is stretched to the edges of a monitor, it flattens out. Somewhere in the middle, the line has the angle I want. I just can't find a good way to mathematically find this point and instantiate it in code.
Edit: A slightly more generic solution for any acute angle. (I didn't test obtuse angles.)
clear, clc, close all
ang_deg = 70;
slope = tand(ang_deg);
t = 0:0.1:1;
y = -slope*t+1;
f = figure;
f.Position(3) = f.Position(3)*1.5;
plot(t,y)
% For a given height, change the width
ax = gca;
ax.Units = 'pixels';
ax.Position(3) = ax.Position(4)/slope;
You can achieve that with
axis equal
which, according to the documentation,
uses the same length for the data units along each axis.
You may want to also use
axis tight
which
fits the axes box tightly around the data by setting the axis limits equal to the range of the data
Follow up your commands with a declaration that you'll be working in pixels, and then set the width to the height:
ax.Units = 'pixels';
ax.Position(3) = ax.Position(4);

MATLAB: The exact size and position of the axis box in the case of `axis equal`?

How to know the exact size and position of the axis box (without axis labels and numbers)? For example, if I use
figure
contourf(x,y,u,100,'linestyle','none')
axis equal
set(gca,'position',[0.1,0.1,0.7,0.8]) %normalized units
The size of the axis frame/box is varyed in the case of the figure window resizing (or using axis equal), but the value of get(gca,'position') remains unchanged. For example:
figure
Z = peaks(20);
contourf(Z,10)
set(gca,'Units','pixels')
get(gca,'position')
axis equal
get(gca,'position')
ans =
0.1300 0.1100 0.7750 0.8150
after axis equal, the axis box is changed, but get(gca,'position') gives the same coordinates:
ans =
0.1300 0.1100 0.7750 0.8150
I need these to align the colorbar to the axis box (with fixed gap between them) in the case of axis equal.
When you call axis equal, the axis box aspect ratio is fixed and the Position property is treated as a maximum size. When you resize the figure window, the axis box will remain centered in the Position rectangle, but in order to maintain the same aspect ratio as before, it may not take up the entire Position rectangle.
If you want it to take up the entire Position rectangle, you can call axis equal again. (this may depend on your MATLAB version; it worked for me in R2015b).
This is also discussed in a bit more detail on MATLAB Central.
To answer your original question, it's a bit complicated. You'd have to get the plot box aspect ratio (using pbaspect() or the axes PlotBoxAspectRatio property) and figure it out:
ah = gca();
% Get the axes Position rectangle in units of pixels
old_units = get(ah,'Units');
set(ah,'Units','pixels');
pos = get(ah,'Position');
set(ah,'Units',old_units);
% Figure the PlotBox and axes Position aspect ratios
pos_aspectRatio = pos(3) / pos(4);
box_aspect = pbaspect(ah);
box_aspectRatio = box_aspect(1) / box_aspect(2);
if (box_aspectRatio > pos_aspectRatio)
% PlotBox is wider than the Position rectangle
box_height = pos(3) / box_aspectRatio;
box_dy = (pos(4)-box_height) / 2;
box_position = [pos(1), pos(2)+box_dy, pos(3), box_height];
else
% PlotBox is taller than the Position rectangle
box_width = pos(4) * box_aspectRatio;
box_dx = (pos(3)-box_width) / 2;
box_position = [pos(1)+box_dx, pos(2), box_width, pos(4)];
end
Note that this will give you the box position in pixels; if you want it in the normalized units that are the axes default, you'll need to normalize it:
fig_pos = get(get(ah,'Parent'),'Position');
box_position = box_position ./ fig_pos([3 4 3 4]);

Using rectangle in Matlab. Using Sum()

I have performed rgb2gray on an image and did a sobel edge detection on the image.
then did
faceEdges = faceNoNoise(:,:) > 50; %binary threshold
so it sets the outline of the image (a picture of a face), to black and white. Values 1 is white pixel, and 0 is black pixel. Someone said I could use this,
mouthsquare = rectangle('position',[recX-mouthBoxBuffer, recY-mouthBoxBuffer, recXDiff*2+mouthBoxBuffer/2, recYDiff*2+mouthBoxBuffer/2],... % see the change in coordinates
'edgecolor','r');
numWhite = sum(sum(mouthsquare));
He said to use two sum()'s because it gets the columns and rows of the contained pixels within the rectangle. numWhite always returns 178 and some decimal numbers.
If you have a 2D matrix M (this being -- for exmple -- an image), the way to count how many elements have the value 1 is:
count_1 = sum(M(:)==1)
or
count_1 = sum(reshape(M,1,[])==1)
If the target values are not exactly 1, but have a Δ-threshold of, let's say, +/- 0.02, then one should ask for:
count_1_pm02 = sum((M(:)>=0.98) & (M(:)<=1.02))
or the equivalent using reshape.

distribute same size circles evenly inside a square using Matlab

I have a figure of size 14 x 14 square drawn inside an axis of 20 x 20, in matlab.
I am trying to draw circles of radius 0.7 inside the square and need to arrange them evenly. I need to draw 233 circles. Please let me know how can I do it?
Currently I can draw them randomly but couldn't get 233 circle. Please see my below code.
Your reply is appreciated.
% Urban, sub urban, Rural areas
x_area =[3, 12, 6];
y_area = [6, 8, 16];
r_area = [1, 7, 2];
f = figure;
hAxs = axes('Parent',f);
hold on, box on, axis equal
xlabel('x')
ylabel('y','Rotation',0)
title('Compute the area of circles a vectorized way for several cicles')
axis([0 20 0 20])
rectangle('Position',[5,1,14,14])
rectangle('Position',[3,1,2,2])
rectangle('Position',[1,3,4,4])
hold on, box on, axis equal
a = 233;
x_base_urban = randi([6 18], 1, a);
b = rand([10 8], 1);
y_base_urban = randi([2 14],1, a);
r_base_urban = 0.9;
size_x = size(x_base_urban);
size_x = size_x(2);
size_y = size(y_base_urban);
size_y = size_y(2);
colour = rand(size_x,3);
for t = 1: size_x
plot(x_base_urban(t)+ r_base_urban.*cos(0:2*pi/100:2*pi),...
y_base_urban(t)+ r_base_urban.*sin(0:2*pi/100:2*pi),'parent',hAxs)
plot(x_base_urban(t),y_base_urban(t),'+','parent',hAxs)
end
Thanks
Randomly plotting everything won't work. Actually, if your circles can't overlap, nothing will work. To show that, just compare the results of following calculations:
lSquare = 14;
rCircle = 0.7;
nCircles = 233;
areaCircles = nCircles * pi * rCircle^2
areaSquare = lSquare^2
You will see that areaCircles > areaSquare, so it is impossible to fit them all in. On the other hand, if areaSquare >= areaCircles does not guarantee you that a solution exists!
Try your setup with a smaller example to come up with a solution. E.g. take a square box and a bunch of spherical objects (balls, marbles, oranges, apples, ... if need be) and try to fit as much of those in your box. If that works, you might even want to draw their positions on a sheet of paper before trying to implement it.
If you do this correctly, you will get an idea of how to stack round objects in a square container. That is also exactly what you need to do in your exercise. Then try to make a model/algorithm of what you did manually and implement that in MATLAB. It won't be hard, but you will need some small calculations: Pythagoras and intersection of circles.
I also suggest you use a function to draw a circle as #Andrey shows, so something of the form function drawCircle(center, radius). That allows you to keep complexity down.
If your circles can overlap, then the solution is quite easy: look at a circle as an object with a center point and distribute these center points evenly over the square. Don't use rand to do this, but calculate their positions yourself.
If you can't find a solution, I might expand my answer in a few days.
Without diving too deep into your code, I think that you need to add a hold function, after the first plot
for t = 1: size_x
plot(x_base_urban(t)+ r_base_urban.*cos(0:2*pi/100:2*pi),...
y_base_urban(t)+ r_base_urban.*sin(0:2*pi/100:2*pi),'parent',hAxs)
plot(x_base_urban(t),y_base_urban(t),'+','parent',hAxs)
hold(hAxs,'on');
end
By the way, the best way to draw circle is by using rectangle command.
rectangle('Curvature',[1 1],'Position',[1 3 4 5])
So you can create a PlotCircle function (like #EgonGeerardyn suggests) like this:
function plotCircle(x,y,r)
rectangle('Position',[x-r y-r 2*r 2*r],'Curvature',[1 1]);
end