How do position coordinates work on a subplot?

I have a subplot filled with one video up top and two graphs underneath. In order to get the ratio right I used a subplot(4,4,1:12) for the video, subplot(4,4,13:14) for one graph and subplot(4,4,15:16) for the other. I am now trying to use insertText() and insertShape() to add labels on top of the video but I'm not sure how the position works in those two functions. Can anyone clariffy?

 Accepted Answer

insertText() and insertShape() work with array coordinates, and those are not necessarily the same as axes data coordinates. An array is being written in to, and then in this situation, the array has to be displayed on an axes.
The primary method of displaying an array on an axes is to use image() objects: imagesc() and imshow() both use image() internally. image objects have xdata and ydata properties https://www.mathworks.com/help/matlab/ref/image.html#buqdlnb-x that describe the transformation from array indices to data coordinates. The default mapping is to put the center of the lower left pixel at data coordinates (1,1) and increase by one data unit for each adjacent array coordinate. Other objects in the axes work with data coordinates (except for annotation objects, which are not actually inside the axes.)
It is entirely valid to have an array that is (say) 480 x 640 x 3, and to image() it specifying that the center of the lower left array element is to go at data coordinate (3,10) and that the center of the upper right array element is to go at data coordinate (9.4, 14.4) so that adjacent array elements become 1/100 data units apart, and then to plot(1:15, 10*rand(1,15)) and have the line be drawn across the full image.
Data units in turn get interpreted into physical screen pixels according the the axes Position and Units and whatever containers the axes is nested inside, including the figure position. Typically, if the user grabs the figure and resizes it then nothing needs to change at the lower levels (but the text might start to look bad.)

6 Comments

Okay, so this is the part of the while loop where I am plotting the video. I am using imagesc() so the xdata and y data should be preserved right?
"The default mapping is to put the center of the lower left pixel at data coordinates (1,1)"
so this means the bottom left of the video is (1,1)?
while k <= (length(t)/dps)
%read in frame
singleFrame = readFrame(inputVid);
% display frame
gaugeposition=[10, -5, 20, -15; 30, -25, 40, -35]; %I've used multiple random values to
%try and track how it changes but it
%seems to be stuck in the upper left corner
textposition=[15,-20; 25, -10];
labelgauges=['Gauge 4', 'Gauge 11'];
RGB1=insertShape(singleFrame,'line',gaugeposition, 'Color', 'red');
RGB2=insertText(RGB1, textposition, labelgauges,'FontSize', 12, ...
'BoxColor', 'white', 'BoxOpacity', 1, 'TextColor', 'red');
subplot(4,4,1:12),imagesc(singleFrame), axis off, axis equal;
imshow(RGB2);
I put the full script below if you it helps:
%this script is used in conjunction with function grabfromexcel,
%the only changes that need to be made on this script is specifying the input
%video and the output video (on line 13)
%some changes to the graph limits and titles may also need to be made
%open the video
inputVid = VideoReader('760-255-20 Test B04 - 3000 fps.wmv');
%calculate total number of frames, mostly to show you what data is
%available in the object.
TotnumFrames = round(inputVid.FrameRate*inputVid.Duration);
%%now add stats to it.
%initiate new composite video file
mergedobj = VideoWriter('compositevid2','Motion JPEG AVI');
mergedobj.FrameRate = inputVid.FrameRate; %match same framerate
mergedobj.Quality=100;
open(mergedobj);
%start the stitch
hfig = figure;
k = 1; %use for my data
[t, p4, p11, dps] = grabfromexcel(TotnumFrames); %pull the data from excel
[maxp4Value, indexAtMaxp4]=max(p4);
tValueAtMaxp4Value = t(indexAtMaxp4(1));
[maxp11Value, indexAtMaxp11]=max(p11);
tValueAtMaxp11Value = t(indexAtMaxp11(1));
%while loop until there are no more frames
while k <= (length(t)/dps)
%read in frame
singleFrame = readFrame(inputVid);
% display frame
gaugeposition=[10, -5, 20, -15; 30, -25, 40, -35];
textposition=[15,-20; 25, -10];
labelgauges=['Gauge 4', 'Gauge 11'];
RGB1=insertShape(singleFrame,'line',gaugeposition, 'Color', 'red');
RGB2=insertText(RGB1, textposition, labelgauges,'FontSize', 12, ...
'BoxColor', 'white', 'BoxOpacity', 1, 'TextColor', 'red');
subplot(4,4,1:12),imagesc(singleFrame), axis off, axis equal;
imshow(RGB2);
%plot the data
subplot(4,4,13:14),plot(t(1,((k-1)*dps)+(1:dps)),p4(1,((k-1)*dps)+(1:dps)),'k-.'),hold on;
title('Pressure Gauge 4 vs Time');
ylim([-1 (max(p4)+.25)])
xlim([0 max(t)])
commentp4= ['Max Pressure= ', num2str(maxp4Value), ' \rightarrow '];
if any(t(1,((k-1)*dps)+(1:dps)) == tValueAtMaxp4Value)
text(tValueAtMaxp4Value, max(p4), commentp4, 'HorizontalAlignment', 'right')
pause 'on'
pause(frame) %this is currently wrong...trying to find the fix
pause(10)
pause 'off'
end
subplot(4,4,15:16),plot(t(1,((k-1)*dps)+(1:dps)),p11(1,((k-1)*dps)+(1:dps)),'k-.'),hold on;
title('Pressure Gauge 11 vs Time');
ylim([-1 (max(p4)+.25)])
xlim([0 max(t)])
commentp11= [' \leftarrow Max Pressure= ', num2str(maxp11Value)];
if any(t(1,((k-1)*dps)+(1:dps)) == tValueAtMaxp11Value)
text(tValueAtMaxp11Value, max(p11), commentp11, 'HorizontalAlignment', 'left')
pause 'on'
pause(frame) %this is currently wrong...trying to find the fix
pause(1)
pause 'off'
end
set(gcf, 'WindowState', 'maximized');
%grab what the figure looks like
frame = getframe(hfig);
%write to file.
writeVideo(mergedobj, frame);
%increment k for my data.
k = k+1;
end
%close the object
close(mergedobj)
You have
gaugeposition=[10, -5, 20, -15; 30, -25, 40, -35]; %I've used multiple random values to
and
RGB1=insertShape(singleFrame,'line',gaugeposition, 'Color', 'red');
When using insertShape of a line, the position array is N x 4, with N lines being drawn. Within a row, the coordinates are x1, y1, x2, y2, with the line being drawn from (x1,y1) to (x2,y2) .
These coordinates are relative to the array. So when you request y values of -5 and -15, you are requesting that lines be drawn "below" the array.
The first column of the position information should be the array column number (x) of the line start; the second column of the position information should be the array row number (y) of the line start; the third should be the array column number (x) of the line end; the 4th should be the array row number (y) of the line end. These will never be negative.
If you need to have information appear outside of the frame that you read in from the video, then you will need to pad the frame and write into the area created from the padding.
Thank you for your help, I was able to figure it out. The numbers were much larger than I was anticipating (in between 600 and 1400) and I did not expect positive values to be increasing in the downward direction.
"... and I did not expect positive values to be increasing in the downward direction."
That's what I told you initially-- "those [x,y] positions are the image indices from the upper left of the image. If you look at the two examples and where the text is inserted, the y position is lower in the image as the position increases and moves to the right as x postion increases. That would be consistent with images in MATLAB on axes with y-axis decreasing."
Yea, I got that now... I don't know it just didn't click in my head
There is an inherent conflict in how people use arrays and graphics. People very often treat array index (1,1) as the upper left corner of an image when they are building code, and think of increasing y coordinates as being further down in the image. But cartesian coordinates have (0,0) as the lower left and increasing y coordinates being further up away from the bottom. Low y as being top of the image, vs low y being the bottom of the image. Both points of view are valid, but they conflict.
Because of this, the standard image operations image(), imagesc(), and imshow(), check to see if "hold" is on, and if not, they automatically reverse the coordinate system for the Y axes by setting the axes YDir property to 'reverse', so that increasing y coordinates are lower on the display, in keeping with how most people compute arrays when building images.
You can deliberately set YDir to 'normal' so that coordinates increase up going up the display. Both perspectives are "right" -- you just have to be aware of which you are using and how to switch between them.

Sign in to comment.

More Answers (1)

dpb
dpb on 22 Jan 2021
Edited: dpb on 25 Jan 2021
It appears that those [x,y] positions are the image indices from the upper left of the image. If you look at the two examples and where the text is inserted, the y position is lower in the image as the position increases and moves to the right as x postion increases. That would be consistent with images in MATLAB on axes with y-axis decreasing.
I don't have the TB so that's just reading the doc and looking at the examples...a little experimentation out to answer the question pretty quickly.

Products

Release

R2020a

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!