Fast redrawing of image in MATLAB using Image.CData - image

I am want to draw once, and then update (very) often and redraw (less) often an image in MATLAB. My image is a vector which is updated and redrawn. To show this image, I used I = imagesc(reshape(data, nVoxels)) to draw and I.CData(:) = data to update. (Redrawing is taken care of seperately.) This worked fine.
Now, in order to make the correspondence to an x-y-coordinate system (x horizontal, y vertical - very standard), where the first dimension of reshape(data, nVoxels) is x and the second is y, I need to draw like this:
I = imagesc(reshape(data, nVoxels)');
axis('xy');
But how can I make a quick update of the image data now?
So far, I have found I need to do
I.CData = reshape(data, nVoxels)';
but I would prefer to do something like before, updating CData without having to reallocate and without having to transpose the data.
Is that possible? I am specifically interested in updating very often in a loop; redrawing is handled independently using a timer.

The transpose can be avoided by setting x and y limits when you create the image to flip it, and rotating the axes:
I = imagesc([nVoxels(2) 1], [1 nVoxels(1)], reshape(data, nVoxels));
camroll(90);
then using
I.CData(:) = data;
again.
However, the transpose time is probably negligible compared to updating the figure using drawnow().

Related

Make images overlap, despite being translated

I will have two images.
They will be either the same or almost the same.
But sometimes either of the images may have been moved by a few pixels on either axis.
What would be the best way to detect if there is such a move going on?
Or better still, what would be the best way to manipulate the images so that they fix for this unwanted movement?
If the images are really nearly identical, and are simply translated (i.e. not skewed, rotated, scaled, etc), you could try using cross-correlation.
When you cross-correlate an image with itself (this is the auto-correlation), the maximum value will be at the center of the resulting matrix. If you shift the image vertically or horizontally and then cross-correlate with the original image the position of the maximum value will shift accordingly. By measuring the shift in the position of the maximum value, relative to the expected position, you can determine how far an image has been translated vertically and horizontally.
Here's a toy example in python. Start by importing some stuff, generating a test image, and examining the auto-correlation:
import numpy as np
from scipy.signal import correlate2d
# generate a test image
num_rows, num_cols = 40, 60
image = np.random.random((num_rows, num_cols))
# get the auto-correlation
correlated = correlate2d(image, image, mode='full')
# get the coordinates of the maximum value
max_coords = np.unravel_index(correlated.argmax(), correlated.shape)
This produces coordinates max_coords = (39, 59). Now to test the approach, shift the image to the right one column, add some random values on the left, and find the max value in the cross-correlation again:
image_translated = np.concatenate(
(np.random.random((image.shape[0], 1)), image[:, :-1]),
axis=1)
correlated = correlate2d(image_translated, image, mode='full')
new_max_coords = np.unravel_index(correlated.argmax(), correlated.shape)
This gives new_max_coords = (39, 60), correctly indicating the image is offset horizontally by 1 (because np.array(new_max_coords) - np.array(max_coords) is [0, 1]). Using this information you can shift images to compensate for translation.
Note that, should you decide to go this way, you may have a lot of kinks to work out. Off-by-one errors abound when determining, given the dimensions of an image, where the max coordinate 'should' be following correlation (i.e. to avoid computing the auto-correlation and determining these coordinates empirically), especially if the images have an even number of rows/columns. In the example above, the center is just [num_rows-1, num_cols-1] but I'm not sure if that's a safe assumption more generally.
But for many cases -- especially those with images that are almost exactly the same and only translated -- this approach should work quite well.

Why is subplot much faster than figure?

I'm building a data analysis platform in MATLAB. One of the system's features need to create many plots. At any given time only one plot is available and the user can traverse to the next/previous upon request (the emphasis here is that there is no need for multiple windows to be open).
Initially I used the figure command each time a new plot was shown, but I noticed that, as the user traverse to the next plot, this command took a bit longer than I wanted. Degrading usability. So I tried using subplot instead and it worked much faster.
Seeing this behavior I ran a little experiment, timing both. The first time figure runs it takes about 0.3 seconds and subplot takes 0.1 seconds. The mean run time for figure is 0.06 seconds with standard deviation of 0.05, while subplot take only 0.002 with standard deviation of 0.001. It seems that subplot is an order of magnitude faster.
The question is: In situation when only one window will be available at any given time, is there any reason to use figure?
Is there any value lost in using `subplot' in general?
(similar consideration can be made even if you can either only once).
The call of subplot does nothing else than creating a new axes object with some convenient positioning options wrapped around.
Axes objects are always children of figure objects, so if there is no figure window open, subplot will open one. This action takes a little time. So instead of opening a new figure window for every new plot, it's faster to just create a new axes object by using subplot, as you determined correctly. To save some memory you can clear the previous plot by clf as suggested by Daniel.
As I understood, you don't want to Create axes in tiled positions, rather you just want to create one axes object. So it would be even faster to use the axes command directly. subplot is actually overkill.
If all your plots have the same axes limits and labels, even clf is not necessary. Use cla (clear axes) to delete the previous plot, but keep labels, limits and grid.
Example:
%// plot #1
plot( x1, y2 );
xlim( [0,100] ); ylim( [0,100] );
xlabel( 'x' );
ylabel( 'y' );
%// clear plot #1, keep all settings of axes
%// plot #2
plot( x2, y2 );
...
Use figure once to create a figure and clf to clear it's content before repainting.

copyobj copies entire image instead of axes only

What I have is a plot showing the area of connected components. What I want to do is to further work on the plot figure such as clean it up a bit or imcomplement it etc. and then be able to apply the axes from the original plot to this image and be able to extract the ylabel.
Let me explain the above issue with my code and some examples.
This is the plot I have, the y-axis represents the object areas. This is the important axis that I want to transfer to the new image.
Since I am interested in the axes only I copy that using
h = findobj(gcf,'type','axes');
So that I can work with the figure without the axes and borders interfering I save it without these attributes
set(gca, 'visible', 'off'); % Hide the axis and borders
hgexport(gcf, 'plot1.jpg', hgexport('factorystyle'), 'Format', 'jpeg');
This is what I get:
So far so good.
Now comes the processing or in other words changing the plot to my needs.
plot_img = rgb2gray(imread('plot1.jpg'));
img_bw_plot = im2bw(plot_img, graythresh(plot_img));
[rows cols] = size(plot_img);
new = zeros(size(plot_img));
for i = 1: rows
for j = 1: cols
if (img_bw_plot(i,j) == 0)
new(i, 1:10) = 255;
end
end
end
f = figure;
imshow(new);
copyobj(h,f)
This produces a weird overlapped image where instead of copying only the axes, the entire image is copied on top of the new. The datacursormode also fails to work beyond the over lapping image.
First of all I'm a little bit confused that if you have the figure in the first place why aren't you extracting your data from it using something like:
lines=findobj(gca,'type','line');
y=zeros(1,length(lines));
for i=1:length(lines)
y(i)=get(lines(i),'ydata');
end
and there you'll have all the data.
But let's say the original figure isn't like a figure figure where you'd have access to the children of the axes object (though all of them being copied together kind of suggests that this is not the case). What you need to realize is that an "axes" object in MATLAB isn't just the axes of the graph, but the whole graph. For example when you have 5 subplots, each of those smaller plots is an axes object and the graph itself is one of its children which is a "line" object (refer to my example above).
So after this long lecture :), one solution is that you could manually create those axes around your newly drawn image instead of copying the axes object as such:
set(gca,'visible','on');
s=size(new);
set(gca,'ytick',linspace(1,s(1),7),'yticklabel',linspace(6000,0,7));
This should do the trick of placing 7 ticks on the y-axis in the same manner as you have on your original figure. The same method would apply to manually creating the labels for the x-axis.
(I tried putting the resulting image here but I don't have the enough reputations to do so. That's on stackoverflow bro!)
Mind you, though, that this creates the labels on the graph giving you the illusion of the same axis while the actual coordinates of the points are actually determined by the size of the image you're saving. So if you want to make sure the image is the same size, you need to work on resizing your original figure to end up being the same size, which given then 0-6000, would be a really big image.

To make all peaks clearly visible in Matlab

I finally solved my problem here with lennon310.
I have a picture of thousands of thin peaks in Time-Frequency picture.
I cannot see all the same time in one picture.
Depending on the physical width of my time window, some windows appear and some come visible.
Pictures of my data which I plot by imagesc
All pictures are from the same data points T, F, B.
How can you plot all peaks at once in a picture in Matlab?
You need to resize the image using resampling to prevent the aliasing effect (that craigim described as unavoidable).
For example, the MATLAB imresize function can perform anti-aliasing. Don't use the "nearest" resize method, that's what you have now.
Extension to #BenVoigt's answer
My try
B = abs(B);
F1 = filter2(B,T); % you need a different filter kernel because resolution is lower
T = filter2(B,F);
F = F1;
image1 = imagesc(B);
display1 = imresize(image1, [600 600], 'bilinear');
imshow(T*t, F*fs, display1);
where are some problems.
I get again picture where the resolution is not enough
2nd Extension to BenVoigt's answer
My suggestion for one kernel filter is with convolution of relative random error
data(find(data ~= 0)) = sin(pi .* data(find(data ~= 0))) ./ (pi*data(find(data ~= 0)));
data(find(data == 0)) = 1; % removing lastly the discontinuity
data1 = data + 0.0000001 * mean(abs(data(:))) * randn(size(data));
data = conv(data, data1);
Is this what BenVoigt means by the kernel filter for distribution?
This gives results like
where the resolution is still a problem.
The central peaks tend to multiply easily if I resize the window.
I had old code active in the above picture but it did not change the result.
The above code is still not enough for the kernel filter of the display.
Probably, some filter functions has to be applied to the time and frequency axis separately still, something like:
F1 = filter2(B,T); % you need a different kernel filter because resolution is lower
T = filter2(B,F);
F = F1;
These filters mess up the values on the both axis.
I need to understand them better to fix this.
But first to understand if they are the right way to go.
The figure has be resized still.
The size of the data was 5001x1 double and those of F and T 13635x1 double.
So I think I should resize lastly after setting axis, labels and title by
imresize(image, [13635 13635], 'bilinear');
since the distirbution is bilinear.
3rd Extension to BenVoigt's answer
I plot the picture now by
imagesc([0 numel(T)], [0 numel(F)], B);
I have a big aliasing problem in my pictures.
Probably, something like this should be to manipulate the Time-Frequency Representation
T = filter2(B,t); % you need a different filter kernel because resolution is lower
F = filter2(B,fs);
4th extension to BenVoigt's answer and comment
I remove the filters and addition of random relative errors.
I set the size of T,F,B, and run
imagesc([0 numel(T)], [0 numel(F)], B, [0 numel(B)])
I get still with significant aliasing but different picture
This isn't being flippant, but I think the only way is to get a wider monitor with a higher pixel density. MATLAB and your video card can only show so many pixels on the screen, and must decide which to show and which to leave out. The data is still there, it just isn't getting displayed. Since you have a lot of very narrow lines, some of them are going to get skipped when decisions are made as to which pixels to light up. Changing the time window size changes which lines get aliased away and which ones get lucky enough to show up on the screen.
My suggestions are, in no particular order: convolute your lines with a Gaussian along the time axis to broaden them, thus increasing the likelihood that part of the peak will appear on the screen. Print them out on a 600 dpi printer and see if they appear. Make several plots, each zooming in on a separate time window.
3rd Extension to BenVoigt's answer
My attempt to tell imagesc how big to make the image, instead of letting to use the monitor size, so it won't away data.
imagesc(T*t, F*fs, B, [50 50]);
% imagesc(T*t, F*fs, B');
% This must be last
imresize(image, 'bilinear', [64 30],);
I am not sure where I should specify the amount of pixels in x-axis and y-axis, either in the command imagesc or imresize.
What is the correct way of resizing the image here?

Animate trajectory using Octave

I have a set of (x,y) coordinates that describe the trajectory of an object. I'd like to animate this trajectory using GNU Octave.
The data set is quite large so I won't be able to redraw the entire plot at every iteration if I want the animation to be smooth. What functions are there that would allow me to "update" a plot rather than redraw it?
Also, I have another set of (vx,vy) points, which describe the speed of the object. I'd like my animated trajectory to take speed into account. What function should I use to have the program sleep for a couple of milliseconds as to make the trajectory animate at the same speed as the object?
(I already know Octave has functions such as comet, but I need to write my own animator.)
Edit: Here's what I have up until now. I expected this to run too fast and require me to use pause, but it's still pretty slow (x and y have 10001 elements).
bounds = [min(x) max(x) min(y) max(y)];
axis(bounds);
hold on
for k = 2 : length(x)
plot(x(k-1:k), y(k-1:k));
drawnow("expose");
end
hold off
You can use the set command to change just the XData and YData data for a certain plot object h:
h = plot(my_xdata(0),my_ydata(0))
for i_=1:length(my_xdata)
set(h, 'YData', my_ydata(i_))
set(h, 'XData', my_xdata(i_))
pause(sqrt(vx(i_)^2+vy(i_)^2))
end
The pause(x) command pauses for x seconds, which can be less than 1.
I think you are looking for the "hold" command. holding the plot keeps all previous data on the plot and the new data is added on top.

Resources