Using Matlab, I want to write a number in a specific position inside an image shown by imshow. At the moment, I have:
myimage = imread('Route of my image');
myimage = im2double(myimage);
imshow(myimage)
MyBox = uicontrol('style','text');
set(MyBox,'String',mynumber);
set(MyBox,'Position',[25,25,15,15]);
My problem is that positions given in 'set' are relatives to all window that manage the figure window so it also includes the gray borders. How can I write them relative to only the figure (without the gray borders)?
You could use text instead?
imshow(image);
text(x,y,'your text')
You can follow the steps described here to remove the grey border from the figure so as to obtain right coordinates when placing the text. Basically fetch the dimensions of both the figure and axes containing the image and make the figure fit exactly the axes.
Beware that when specifying the position of a uicontrol object the 0 position is at the BOTTOM left, whereas the pixel coordinates inside an image start from the TOP left. Therefore you will need to get the dimensions of the image and subtract the actual y-coordinate from the number of rows forming the image, i.e. the 1st dimension.
Here is an example:
clear
clc
close all
myimage = imread('coins.png');
myimage = im2double(myimage);
imshow(myimage);
[r,c,~] = size(myimage);
%// Copied/pasted from http://www.mathworks.com/matlabcentral/answers/100366-how-can-i-remove-the-grey-borders-from-the-imshow-plot-in-matlab-7-4-r2007a
set(gca,'units','pixels'); %// set the axes units to pixels
x = get(gca,'position'); %// get the position of the axes
set(gcf,'units','pixels'); %// set the figure units to pixels
y = get(gcf,'position'); %// get the figure position
set(gcf,'position',[y(1) y(2) x(3) x(4)]);% set the position of the figure to the length and width of the axes
set(gca,'units','normalized','position',[0 0 1 1]) % set the axes units to pixels
%// Example
hold on
mynumber = 20;
%// Set up position/size of the box
Box_x = 25;
Box_y = 25;
Box_size = [15 15];
%// Beware! For y-position you want to start at the top left, i.e. r -
%// Box_y
BoxPos = [Box_x r-Box_y Box_size(1) Box_size(2)];
MyBox = uicontrol('style','text','Position',BoxPos);
set(MyBox,'String',mynumber);
and output:
yay!
The answers above add text on the figure window. If you want to modify your image itself and change the pixels so that you get the text where ever you display the image you can use insertText function available in Computer Vision toolbox.
myimage = imread('Route of my image');
myimage = im2double(myimage);
myimage = insertText(myimage, [25 25], 'your text');
imshow(myimage)
Related
I am building a demonstration application using the matlab application designer.
It has two axes. One shows an image. The other shows the profile of the image, and the derivative of the profile.
In the example below, I have a 200 column, by 5 row image.
Below the image, I have a plot of the image profile, and a plot of the derivative. The image profile has (not surprisingly) 200 elements. I'd like the X/Y plots to align consistently with the image axis.
Is there a way to control the image axes, to align correctly with the plot axes?
Obviously, I could remove the axis(app.AxesImageView,'off') line below, and force the axes on. But, that doesn't work either, because the axis labels are very different, and so they don't line up. I want this to be flexible to work for any size image, and the label sizes impact this as well.
TLDR: Is it possible to force the axes in a plot or an imagesc to reside in a particular spot in the region the axes have set aside for rendering?
The current code for rendering the image and the plot are:
w = 5; l = 200;
parms.filterHalfWidth = 20;
img = zeros(w,l);
img(:,ceil(l/2):end)=255;
img = uint8(img);
imagesc(app.AxesImageView,img,[0,255]);
colormap(app.AxesImageView, 'gray');
axis(app.AxesImageView,'off');
title(app.AxesImageView,'Image to find edge');
profile = mean(img,2);
vals = derivative(profile, parms.filterHalfWidth);
[~, peak] = max(vals);
plot(app.AxesProfiles, profile);
hold(app.AxesProfiles, 'all');
plot(app.AxesProfiles, parms.filterHalfWidth + (1:numel(vals)), vals);
plot(app.AxesProfiles, peak*[1,1], [0,255],'Color','Red','LineWidth',2);
hold(app.AxesProfiles, 'off');
i wasn't able to reproduce your code but I wrote a piece of code with uifigure and ... (which is primary used in appdesigner)
it is completely flexible
p.s: I don't have required toolbox to calculate derivative so I used another plot.
f=uifigure;
f.Units = "normalized"; % essential
f.Position= [0.3 0.2 0.5 0.7];
f.AutoResizeChildren='off'; % essential
ax2=uiaxes(f);
ax2.Units = "normalized";
ax2.InnerPosition=[0.1 0.1 0.8 0.4]; % set Innerposition of axes at your desired position relative to the figureto ensure axes alignment
ax1=uiaxes(f);
ax1.Units = "normalized";
ax1.InnerPosition=[0.1 0.55 0.8 0.4];
w = 5; l = 200;
parms.filterHalfWidth = 20;
img = zeros(w,l);
img(:,ceil(l/2):end)=255;
img = uint8(img);
imagesc(ax1,img,[0,255]);
colormap(ax1, 'gray');
axis(ax1,'off');
title(ax1,'Image to find edge');
profile = mean(img,2);
vals = derivative(profile, parms.filterHalfWidth);
[~, peak] = max(vals);
plot(ax2, profile);
hold(ax2, 'all');
plot(ax2, parms.filterHalfWidth + (1:numel(vals)), vals);
plot(ax2, peak*[1,1], [0,255],'Color','Red','LineWidth',2);
hold(ax2, 'off');
New to Matlab here. I'm trying to implement some code to detect a face in an image and crop it. I have the script running, but the bounding box that it places around the detected face is a bit small. Is there any way to change the dimensions of the bounding box to capture more of the faces?
clc;
% cd into the a folder with pictures
cd 'C:\Users\abc\Desktop\folder'
files = dir('*.jpg');
for file = files'
img = imread(file.name);
figure(1),imshow(img);
FaceDetect = vision.CascadeObjectDetector;
FaceDetect.MergeThreshold = 7;
BB = step(FaceDetect,img);
figure(2),imshow(img);
for i = 1:size(BB,1)
rectangle('Position',BB(i,:),'LineWidth',2,'LineStyle','- ','EdgeColor','r');
end
for i = 1:size(BB,1)
rectangle('Position',BB(i,:),'LineWidth',2,'LineStyle','- ','EdgeColor','r');
J = imcrop(img,BB(i,:));
figure(3);
imshow(J);
a = 'edited\'
b = file.name
output = strcat(a,b);
imwrite(J,output);
end
%Code End
end
Currently, the script finds a face like so:
And outputs an image such as this:
This is good, I just want to extend the boundaries of the cropping zone to capture more of the face (e.g., hair and chin).
From the MATLAB rectangle function documentation.
rectangle('Position',pos) creates a rectangle in 2-D coordinates.
Specify pos as a four-element vector of the form [x y w h] in data
units. The x and y elements determine the location and the w and h
elements determine the size. The function plots into the current axes
without clearing existing content from the axes.
If you are just looking to increase the bounding box by some scale factor about the center of the rectangle, you could scale the w and h components in BB and adjust the rectangle origin x and y by subtracting half the scale difference. The following code should work if you place it right after the BB = step(FaceDetect,img); line in your code. I don't have MATLAB available to me at the moment but I'm pretty sure this will work.
% Scale the rectangle to 1.2 times its original size
scale = 1.2;
% Adjust the lower left corner of the rectangles
BB(:,1:2) = BB(:,1:2) - BB(:,3:4)*0.5*(scale - 1)
% Adjust the width and height of the rectangles
BB(:,3:4) = BB(:,3:4)*scale;
You can use imresize function in Matlab as described in this link and bboxresize to resize the bounding box
Below is the simple code to resize your image into 3 times the original one
%% clean workspace
clc;
clear;
cd 'C:\Users\abc\Desktop\folder';
files = dir('*.jpg');
for file = files'
img = imread(file.name) ;
figure(1),imshow(img);
FaceDetect = vision.CascadeObjectDetector;
FaceDetect.MergeThreshold =7;
BB = step(FaceDetect,img);
BB2 = BB;
%% Scale the rectangle to 3 times its original size
scale = 3;
%% Resize image
ImgResized = imresize(img,scale);
%% Resize bound box using the function named bboxresize in Matlab
BBResized = bboxresize(BB,scale);
figure(2),imshow(ImgResized);
%% Draw Bounding Box
for i=1:size(BBResized,1)
rectangle('position',BBResized(i,:),'lineWidth',2,'LineStyle','- ','EdgeColor','y');
end
end
I have a bar graph with 2 different data (each with a different color as you can see on the picture). I would like to move the x-axis so it crosses at y=-100 (for example). So if a data = -40, I would like to have a bar from -100 to -40.
An other question : is it possible to write each value of the x-axis vertically (because with all the values, we can't see anything).
The last question : is it possible to have 2 different scales for the x_axis ?
Thank you in advance,
Best regards,
Here is some code to get you going. Everything is commented so that should be easy to follow:
clear
clc
close all
%// Generate dummy data
y = -90*rand(1,20);
NumY = numel(y);
HalfData = round(NumY/2);
%// Loop to color half in pink and half in blue
hold all
for k = 1:NumY
hBar = bar(k,y(k));
if k <= HalfData
set(hBar,'FaceColor',[1 0 1])
else
set(hBar,'FaceColor',[0 0 1])
end
end
hold off
%// Get xtick labels and position for future use
xtLabels = cellstr(get(gca,'XTickLabel')).';
xtPos = get(gca,'XTick');
%// Change baseline value
set(hBar,'BaseValue',-40)
%// Get baseline to change its properties if you want
hBaseL = get(hBar,'Baseline');
set(hBaseL,'LineStyle','--','Color','k','LineWidth',3)
%// Adjust axis limits. Remove labels to place them vertically
set(gca,'XLim',[0 NumY],'XTickLabel',{''})
%// Get correct position for xlabel text
YLimPoArrays = min(get(gca,'YLim'));
YLimPoArrays = repmat(YLimPoArrays,numel(xtPos),1);
%// Place text positioned vertically with a small y offset
text(xtPos,YLimPoArrays-3,xtLabels,'HorizontalAlignment','center','Rotation',90,'FontSize',15)
And the output:
Hope that helps!
I have a code that detects a face in an image and places a bounding box around the image like below.
But I want to go further and colour the area outside the bounding box black so that only the face can be seen and the background becomes black.
Original code..
FDetect = vision.CascadeObjectDetector;
I = imread('PresidentClinton.jpg');
%Returns Bounding Box values based on number of objects
BB = step(FDetect,I);
figure,
imshow(I); hold on
for i = 1:size(BB,1)
rectangle('Position',BB(i,:),'LineWidth',5,'LineStyle','-','EdgeColor','r');
end
title('Face Detection');
hold off;
Here is a simple method in which you first create a target image of the same size/class as your original image and fill it with black. Then you get the rectangle coordinates and assign the data from the original image to the target image:
clear
close all
A = imread('peppers.png');
B = zeros(size(A),class(A)); % //Pre-define target image of identical size and class than original.
%// You could also use this line:
%//B = zeros(size(A),'like',A);
hRect = rectangle('Position',[100 100 200 160],'LineWidth',3); %// Define rectangle
RectPos = get(hRect, 'Position'); %// Get the coordinates of the rectangle.
x = RectPos(1):RectPos(1)+RectPos(3); %// Define x- and y-span
y = RectPos(2):RectPos(2)+RectPos(4);
B(x,y,:) = A(x,y,:); %// Assign the selected part of the image to B
figure
subplot(1,2,1)
imshow(A)
subplot(1,2,2)
imshow(B)
Giving something like this:
There are other ways of course but I think this one is straightforward and easy to implement in a loop.
I have an X Y Z dataset.
X and Y are the 2D coordinates and Z is the intensity.
I plot the data using the scatter function:
markerSize=100;
scatter(x,y,markerSize,z,'s','fill');
I use the options 's' and 'fill' to get filled squares.
My problem is the markerSize value corresponds to the area of the marker, and its unit is points (1/72 of one inch).
The marker size is constant, even if I resize the figure plot. So that the gap between the data points increases when I increase the figure size.
What I would like is a constant marker size which is a constant of the axis unit. For instance, the marker size should be 5x5 (5 in X axis and 5 in Y axis).
Thanks for your help.
You want to make the size of markers proportional to the figure size.
The size of markers is controlled by SizeData parameter of the scattergroup object. The size of figure is stored in Position parameter of the figure object. The difficult part is to interactively resize the marker when the figure size is changed. So you need to use ResizeFcn callback and call setmarkersize function that you define.
function [ ] = setmarkersize( src, evnt )
% # get position of the figure (pos = [x, y, width, height])
pos = get(src, 'Position');
% # get the scattergroup object
h = get(get(src,'children'),'children');
% # resize the marker
relativesize = 0.5;
set(h,'SizeData', pos(3)*relativesize);
end
================================================
% # attach the callback to figure
f = figure('ResizeFcn', #setmarkersize);
h = scatter(x,y,markerSize,z,'s','fill');
You will have to set the marker size manually according to the actual figure size on screen. Using axes property Position you can convert data units to relative figure units. In the next step this size can be converted to the absolute size in points on screen. With that information you can set the marker size accordingly. In the following code snippet I've set the x/y axis limits and width/height of the axis to identical values, because the square marker area can only be calculated reasonably if the marker width is equal to the marker height.
Set marker size relative to data units
% test data
x = [25*rand(1,10) 2.5];
y = [25*rand(1,10) 2.5];
z = [rand(1,10) 0.5];
% relative marker size in squared normalized figure units
marker_rel = 5;
%% Set relative marker size (approximately)
scatter(x,y,100,z,'s','fill');
% Set identical x/y limits and set axes height=widht, so that markers
% really represent squares in data units
xlim([0 25]);
ylim([0 25]);
set(gca, 'Units', 'Points');
axpos_pt = get(gca, 'Position');
axpos_pt = [axpos_pt(1) axpos_pt(2) min(axpos(3:4)) min(axpos(3:4))];
set(gca, 'Position', axpos_pt);
grid on;
grid minor;
% Set marker size relative to data units
markerSize = (marker_rel * axpos_pt(3) / diff(xlim))^2;
set(get(gca, 'children'), 'sizedata', markerSize);
As it turns out, the displayed marker size is slightly smaller than expected. Obviously, there's some bounding box of unknown (at least to me) size, see here.
An alternative approach is to plot rectangles "manually" as shown in the following code snippet (same test data is used). When the figure is resized, the rectangles are resized as well without any special callback function being needed.
Draw rectangles manually
%% Use rectangles (exact)
figure;
axes;
cmp = colormap;
z_range = max(z) - min(z);
line_style = 'none'; % set to '-' to make the edges visible
for k = 1:length(x)
x_pos = x(k) - marker_rel/2;
y_pos = y(k) - marker_rel/2;
w = marker_rel;
h = marker_rel;
color = cmp( round(((z(k) - min(z))/z_range)*(length(cmp) - 1)) + 1, : );
rectangle('Position', [x_pos y_pos w h], 'FaceColor', color,...
'LineStyle', line_style);
end
grid on;
grid minor;
The above code produces the desired marker size:
In general, these are no squares. They can (and will) only be squares if xlim = ylim and absolute height of axis = absolute width of axis. I have shown in my first code snippet how to achieve this.
I found an answer on the Matlab central forum which does not use the useful dsxy2figxy function. Here is the link to it (link)
The code is the following:
x = rand(1,50);
y = rand(1,50);
s = 5; %Marker width in units of X
h = scatter(x,y); % Create a scatter plot and return a handle to the 'hggroup' object
%Obtain the axes size (in axpos) in Points
currentunits = get(gca,'Units');
set(gca, 'Units', 'Points');
axpos = get(gca,'Position');
set(gca, 'Units', currentunits);
markerWidth = s/diff(xlim)*axpos(3); % Calculate Marker width in points
set(h, 'SizeData', markerWidth^2)
The answer by ysakamoto doesn't work in Matlab 2014, where Mathworks changed figure handles from double to object. The following modification makes it work again:
function [ ] = setmarkersize( src, ~ )
% get position of the figure (pos = [x, y, width, height])
pos = get(src, 'Position');
% marker size
relativesize = 0.01;
% axes is not necessarily the only child of figure (e.g. colorbar may be first)
for i = 1:numel(src.Children)
if strcmpi(src.Children(i).Type, 'axes')
% make marker size depend on figure width
src.Children(i).Children.SizeData = pos(3) * relativesize;
break
end
end
end
Also, when creating the figure it will not set the marker size to the correct value until resized. So call setmarkersize explicitly:
f = figure('ResizeFcn', #setmarkersize);
...
setmarkersize(f)
Consider abandoning 'markers' and drawing directly on the axis using rectangles or circles. The 'rectangle' command with 'curvature' set to [1 1] is essentially an open circle. Units are in plot units, so each rectangle can be scaled:
rectangle('position', [rectCentX rectCentY widthInPlotUnits, heightInPlotUnits],'curvature',[1 1])