I am trying to create a movie in Matlab using series of functions under VideoWriter function. My code is somewhat like one shown below:
vidObj=VideoWriter('movie.avi');
open(vidObj);
for i=1:N %N is number of frames
[nx,ny]=coordinates(Lx,Ly,Nx,Ny,[x(i),-y(i)]);
%Lx and Ly refer to the length and height in meters.
%Nx and Ny are number of pixels (boxes) and fit into the respective L's.
%If Lx=10e-6 , with Nx=5, there will be 5 pixels in x dimension,
%each of length 2e-6 m.
[xf,yf]=ndgrid(nx,ny);
zf=zeros(size(xf))+z(i);
% generate a frame here
[E,H]=nfmie(an,bn,xf,yf,zf,rad,ns,nm,lambda,tf_flag,cc_flag);
Ecc=sqrt(real(E(:,:,1)).^2+real(E(:,:,2)).^2+real(E(:,:,3)).^2+imag(E(:,:,1)).^2+imag(E(:,:,2)).^2+imag(E(:,:,3)).^2);
clf
imagesc(nx/rad,ny/rad,Ecc)
rectangle('Position',[-rad(end),-rad(end),dia(end),dia(end)],'Curvature',[1,1]);
axis image;
axis off;
currFrame=getframe(gcf);
writeVideo(vidObj,currFrame);
end
close(vidObj);
return
This generated a movie called movie.avi. However, the movie (and the tif images generated from command window) has the dimensions of "420x560x3".
edit: the tif's are generated from the movie.avi using the following code:
obj = VideoReader('movie.avi');
vid = read(obj);
frames = obj.NumberOfFrames;
for x = 1 : frames
imwrite(vid(:,:,:,x),strcat('frame-',num2str(x),'.tif'));
end
I am trying to use these images in another software, IDL, following:
read Part 1 of this link
However, when runs on IDL, it detects the dimensions as [3x420x560], and therefore generates a really weird image when I run the normalization.
How do I fix this? Is using imwrite going to help?
I was able to successfully open the .tif in IDL, but it shows that the 420x560 is actually an image including an external grey boundary.
How do I remove this boundary? I tried seeing through my functions and they were fine. (I think)
I apologize in advance for asking so many questions. I am very new to this and need help. Thank you once again
I think that the grey boundary around your image is due to the fact that when the code grabs the frame (using getframe) it is being passed the handle to the current figure (gcf) which includes the grey boundary around the image. Try instead using the handle to the current axis
currFrame=getframe(gca);
I tried this and with gca there was no grey boundary around the image when I tried the
image(currFrame.cdata);
When I retrieved the frame, the data within that corresponded to the image was reduced in dimension. I'm not sure why the code does this but an alternative to getframe is to do the following
I = imread('someImage.jpg');
h = imagesc(I);
imageScData = get(h,'CData');
frameData = im2frame(imageScData);
Naturally, you won't have the first two lines since you are building your image at each iteration of the for loop. But I did notice that size(frameData.cdata)==size(I) - so no reduction.
You also mention how IDL (which I've never used) is reading in the files incorrectly or rather that the image dimensions are detected incorrectly. How are you writing out the image to a tif file? (I didn't see any MATLAB code above, like imwrite, indicating this.)
To fix interleave problem, i.e., 3 x 420 x 560 vs 420 vs 3 x 560 vs 420 x 560 x 3, when reading in IDL, use the INTERLEAVE keyword to READ_TIFF to specify how you want the result:
IDL> im = read_tiff(filename, interleave=2)
Related
I have a program that creates a lot of plots which I want to save as fig001, fig002, .... At the moment, the only way I can do this is to either save them one by one as I go along using print() or so save them into a graphics object FIG.
Unfortunately, I have to create the plots sequentially (can't parallelise it). If I save as I go, then this takes quite a long time; if I store in FIG then do a parfor-loop over all the figures, then this is faster. However, it's still pretty slow, and it's a major bottleneck in my code.
Matlab is automatically writing a 1200x900 pixel image. In reality, it's just a graph with some (horizontal) straight lines on it. I really do not need a high image quality whatsoever; maybe reducing this would help speed things up? I can't find how to do this either.
I've had a look online, in particular at other SE questions, but I haven't been able to come up with a solution. There's various stuff about "playing around with the hardcore function". I'm a mathematician who wants a code to get some intuition for the problem; I'm not a proper programmer! As such, "play around with a function" (that might cause Matlab to crash) is rather difficult for me!
I've sorted this by using the Matlab movie function. Thank you Cecilia for your assistance!
Since your intention is to use the images in a slideshow. I would suggest making a movie and using that instead. I've run a few tests, and saving a movie is much faster than saving individual pngs. I see about a 5x speed up in my simple test.
numImages = 25;
%Saving one figure at a time
tic;
for i= 1:numImages
x = 0:0.1:2*pi;
y = i*sin(x);
plot(x, y);
fig = gcf;
print('img.png', '-dpng', '-r50'); %Downsample resolution
end
toc;
tic;
%Saving a movie
v = VideoWriter('mov.avi');
open(v);
for i = 1:numImages
x = 0:0.1:2*pi;
y = i*sin(x);
plot(x, y);
drawnow; %Force the figure to render
frame = getframe; %Convert the figure to a movie frame
writeVideo(v, frame); %Write the frame to the movie file
end
close(v);
toc;
Using writeVideo also has the advantage of never storing the whole movie in memory. Instead, as each frame is captured, it is immediately written to the file. The one downside is that because the frames need to be written in the correct sequence, you can not use a parfor
You could also consider making a movie matrix. Each element would be a frame. So to initialize the size of the matrix, you would need something like
numImages = 25;
mov(numImages) = struct('cdata',[],'colormap',[]);
Then using the parfor, you can assign all the images to your mov matrix in the correct order. After your loop is finished, you can write the movie to a file using VideoWriter. However, while generating your figures, you will need to keep your whole movie in memory, and there is an overhead associated with parfor, so it may not end up being more efficient.
If you made Matlab output a file like this, called "lines.txt" with each line describing the x1,y1 and x2,y2 of a line on your output image:
10 100 600 100
50 400 800 400
100 820 350 820
Then you could use ImageMagick to draw the lines like this:
#!/bin/bash
# Create the output image, 1200x900
convert -size 1200x900 xc:gray80 result.png
# Loop, reading one line at a time from the file "lines.txt"
while read x1 y1 x2 y2; do
echo Read line $x1,$y1 $x2,$y2
# Tell ImageMagick to draw the line on the image
convert result.png -stroke blue -strokewidth 5 -draw "line $x1,$y1 $x2,$y2" result.png
done < lines.txt
Obviously, you can draw non-horizontal lines and you could output a colour after the x,y coordinates and/or a thickness...
ImageMagick is installed on most Linux distros and is available for OSX and Windows. There are more efficient ways of plotting the lines, but this may be plenty to get you started.
Another option may be to use Gnuplot, see my other answer here.
On top of Cecilia's answer which suggests using getframe to generate a movie, I'd like to add that getframe can also be used to improve speed for saving static images. Combine it with frame2im to convert the figure into a rasterized image, and then use imwrite to save the rasterized image to a file.
frame = getframe(fig); % fig is the figure handle to save
[raster, raster_map] = frame2im(frame); % raster is the rasterized image, raster_map is the colormap
if isempty(raster_map)
imwrite(raster, fig_file);
else
imwrite(raster, raster_map, fig_file); % fig_file is the path to the image
end
In my particular case the time used to save the figures is reduced from ~60s for 19 files (using saveas) down to 7~9s using this method.
Do note that this method
Does not change the background color to white as saveas does;
Does not seem to support resolution and dpi other than that of the current figure;
Does not support saving as a vector image (obviously).
I'm trying to find a starting point, but I can't seem to find the right answer. I'd be very grateful for some guidance. I also don't know the proper terminology, hence the title.
I took an image of a bag with a black background behind it.
And I want to extract the bag, similar to this.
And if possible, find the center, like this.
Essentially, I want to be able to extract the blob of pixels and then find the center point.
I know these are two separate questions, but I figured if someone can do the latter, then they can do the first. I am using MATLAB, but would like to write my own code and not use their image processing functions, like edge(). What methods/algorithms can I use? Any papers/links would be nice (:
Well, assuming that your image only consists of a black background and a bag inside it, a very common way to perform what you're asking is to threshold the image, then find the centroid of all of the white pixels.
I did a Google search and the closest thing that I can think of that matches what you want looks like this:
http://ak.picdn.net/shutterstock/videos/3455555/preview/stock-footage-single-blank-gray-shopping-bag-loop-rotate-on-black-background.jpg
This image is RGB for some reason, even though it's grayscale so we're going to convert this to grayscale. I'm assuming you can't use any built-in MATLAB functions and so rgb2gray is out. You can still implement it yourself though as rgb2gray implements the SMPTE Rec. 709 standard.
Once we read in the image, you can threshold the image and then find the centroid of all of the white pixels. That can be done using find to determine the non-zero row and column locations and then you'd just find the mean of both of them separately. Once we do that, we can show the image and plot a red circle where the centroid is located. As such:
im = imread('http://ak.picdn.net/shutterstock/videos/3455555/preview/stock-footage-single-blank-gray-shopping-bag-loop-rotate-on-black-background.jpg');
%// Convert colour image to grayscale
im = double(im);
im = 0.299*im(:,:,1) + 0.587*im(:,:,2) + 0.114*im(:,:,3);
im = uint8(im);
thresh = 30; %// Choose threshold here
%// Threshold image
im_thresh = im > thresh;
%// Find non-zero locations
[rows,cols] = find(im_thresh);
%// Find the centroid
mean_row = mean(rows);
mean_col = mean(cols);
%// Show the image and the centroid
imshow(im); hold on;
plot(mean_col, mean_row, 'r.', 'MarkerSize', 18);
When I run the above code, this is what we get:
Not bad! Now your next concern is the case of handling multiple objects. As you have intelligently determined, this code only detects one object. For the case of multiple objects, we're going to have to do something different. What you need to do is identify all of the objects in the image by an ID. This means that we need to create a matrix of IDs where each pixel in this matrix denotes which object the object belongs to. After, we iterate through each object ID and find each centroid. This is performed by creating a mask for each ID, finding the centroid of that mask and saving this result. This is what is known as finding the connected components.
regionprops is the most common way to do this in MATLAB, but as you want to implement this yourself, I will defer you to my post I wrote a while ago on how to find the connected components of a binary image:
How to find all connected components in a binary image in Matlab?
Mind you, that algorithm is not the most efficient one so it may take a few seconds, but I'm sure you don't mind the wait :) So let's deal with the case of multiple objects now. I also found this image on Google:
We'd threshold the image as normal, then what's going to be different is performing a connected components analysis, then we iterate through each label and find the centroid. However, an additional constraint that I'm going to enforce is that we're going to check the area of each object found in the connected components result. If it's less than some number, this means that the object is probably attributed to quantization noise and we should skip this result.
Therefore, assuming that you took the code in the above linked post and placed it into a function called conncomptest which has the following prototype:
B = conncomptest(A);
So, take the code in the referenced post, and place it into a function called conncomptest.m with a function header such that:
function B = conncomptest(A)
where A is the input binary image and B is the matrix of IDs, you would do something like this:
im = imread('http://cdn.c.photoshelter.com/img-get2/I0000dqEHPhmGs.w/fit=1000x750/84483552.jpg');
im = double(im);
im = 0.299*im(:,:,1) + 0.587*im(:,:,2) + 0.114*im(:,:,3);
im = uint8(im);
thresh = 30; %// Choose threshold here
%// Threshold image
im_thresh = im > thresh;
%// Perform connected components analysis
labels = conncomptest(im_thresh);
%// Find the total number of objects in the image
num_labels = max(labels(:));
%// Find centroids of each object and show the image
figure;
imshow(im);
hold on;
for idx = 1 : num_labels
%// Find the ith object mask
mask = labels == idx;
%// Find the area
arr = sum(mask(:));
%// If area is less than a threshold
%// don't process this object
if arr < 50
continue;
end
%// Else, find the centroid normally
%// Find non-zero locations
[rows,cols] = find(mask);
%// Find the centroid
mean_row = mean(rows);
mean_col = mean(cols);
%// Show the image and the centroid
plot(mean_col, mean_row, 'r.', 'MarkerSize', 18);
end
We get:
I have no intention of detracting from Ray's (#rayryeng) excellent advice and, as usual, beautifully crafted, reasoned and illustrated answer, however I note that you are interested in solutions other than Matlab and actually want to develop your own code, so I though I would provide some additional options for you.
You could look to the excellent ImageMagick, which is installed in most Linux distros and available for OS X, other good operating systems and Windows. It includes a "Connected Components" method and if you apply it to this image:
like this at the command-line:
convert bags.png -threshold 20% \
-define connected-components:verbose=true \
-define connected-components:area-threshold=600 \
-connected-components 8 -auto-level output.png
The output will be:
Objects (id: bounding-box centroid area mean-color):
2: 630x473+0+0 309.0,252.9 195140 srgb(0,0,0)
1: 248x220+0+0 131.8,105.5 40559 srgb(249,249,249)
7: 299x231+328+186 507.5,304.8 36620 srgb(254,254,254)
3: 140x171+403+0 458.0,80.2 13671 srgb(253,253,253)
12: 125x150+206+323 259.8,382.4 10940 srgb(253,253,253)
8: 40x50+339+221 357.0,248.0 1060 srgb(0,0,0)
which shows 6 objects, one per line, and gives the bounding boxes, centroids and mean-colour of each. So, the 3rd line means a box 299 pixels wide by 231 pixels tall, with its top-left corner at 328 across from the top-left of the image and 186 pixels down from the top-left corner.
If I draw in the bounding boxes, you can see them here:
The centroids are also listed for you.
The outout image from the command above is like this, showing each shape shaded in a different shade of grey. Note that the darkest one has come up black so is VERY hard to see - nearly impossible :-)
If, as you say, you wish to look at writing your own connected component code, you could look at my code in another answer of mine... here
Anyway, I hope this helps and you see it just as an addition to the excellent answer Ray has already provided.
I am writing a function which takes the following actions: extracting from video, convert the frames to grayscale and calculate the difference between first two images that i have extract.
I have done the code up to this point. But I have no idea how to extract the images that were extracted previously. Can anyone provide some guidance on this?
function [ vid ] = motion( input_args )
vid = (input_args);
readerobj = VideoReader(vid);
vidFrames = read(readerobj);
numFrames = get(readerobj, 'Number of Frames');
for k = 1 : numFrames
mov(k).cdata = vidFrames(:,:,:,k);
mov(k).colormap = [];
imagename=strcat(int2str(k), '.jpg');
%save inside output folder
imwrite(mov(k).cdata, strcat('output\frame-',imagename));
end
end
Reading your problem description, you want to do a difference of frames (in grayscale) between the previous frame and the current frame. What you can do is store the first frame as a temporary variable, then in your for loop, you would take the difference between the current frame and the previous frame. Before you iterate to the next frame, be sure to set this current frame as the previous frame, and then proceed. Obviously, you need to create the grayscale equivalent of each frame you have read in. As such, you should change your for loop to this:
prevFrame = rgb2gray(vidFrames(:,:,:,1)); %// Initialize and get grayscale
for k = 2 : numFrames %// Note we start at index 2
currFrame = rgb2gray(vidFrames(:,:,:,k)); %// Get current frame
%// and get grayscale
%// Find difference frame
diffFrame = uint8(abs(double(currFrame) - double(prevFrame)));
mov(k).cdata = cat(3,diffFrame,diffFrame,diffFrame); %// Now save to file
mov(k).colormap = [];
imagename=strcat(int2str(k), '.jpg');
%//save inside output folder
imwrite(mov(k).cdata, strcat('output\frame-',imagename));
prevFrame = currFrame; %// Save for next iteration
end
Pay special attention to how I calculated the difference frame. I casted each of the frames to double, then took the absolute difference, then recast it as uint8. The reason why is because if you don't do this, if there are any pixels that have a negative difference, MATLAB will saturate this difference to 0. For example, if one pixel was intensity 128 in one frame, then 255 in the next, the difference should be -127. We put an abs here because this is really a difference of 127. It doesn't matter which direction we are going in. However, MATLAB will consider this difference as 0 as anything that is less than 0 gets saturated to 0. As such, I need to convert both frames to double as the frames you read in from file will most likely be uint8. Once you find the absolute difference, we then recast as uint8 so we can save to file and also be able to display these images if desired.
Now the code will save the difference images to file. Note that you will be one frame short because we started at frame 2. This is necessary if you want to compare differences between consecutive frames. Note that when you are creating your frame, I had to replicate the difference frame and make it three channels to mimic a RGB frame. For a grayscale image, the RGB will have each channel to be all the same. This was done using the cat command, and I stacked the difference frame in the third dimension three times.
What's good about your mov structure now is that you can use this structure and create a video out of it using MATLAB's VideoWriter class for example. The structure is exactly formatted to be written to file. Simply loop through your stucture and write each structure element to file with the VideoWriter class. You'll then be able to produce a movie that shows the difference between consecutive frames.
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).
so I'm using the imread function in matlab and when I save the TIFF file and open it in photoshop, it has a white border and I can't understand why. I want to maintain its resolution as a 512 by 512 image. Any ideas why? And how I can fix that?
Here's a sample code:
B = imread('W_noise1.tif');
for n = 1:5,
B = medfilt2(B);
end
B = filter2(fspecial('average',3),B)/255;
imshow(B)
Are you sure it's an issue with imread? I'd be surprised if it is.
See this link about medfilt2 where it explains that "medfilt2 pads the image with 0s on the edges, so the median values for the points within [m n]/2 of the edges might appear distorted."
EDIT: I tried to replicate your problem. This is an issue with print where it puts a white frame around the image after you save it. This functionality, print is made for printing plots. If you want to save the image, you should use imwrite.