Extract images from video, change them to grayscale and find the differences - matlab

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.

Related

Saving kinect depth frame (uint16) using MATLAB but why is it too dark?

Recently I work on kinect using MATLAB. I take depth frame which is in uint16 format. But when I display it or save it using MATLAB command like: imshow & imwrite respectively, it shows too dark image. But when set the display range or convert it in uint8 format it becomes brighter. But I want to save it as a brighter format without converting in uint8 format like scaling the range between 0 to 4500.
vid = videoinput('kinect',1);
vid2 = videoinput('kinect',2);
vid.FramesPerTrigger = 1;
vid2.FramesPerTrigger = 1;
% % Set the trigger repeat for both devices to 200, in order to acquire 201 frames from both the color sensor and the depth sensor.
vid.TriggerRepeat = 200;
vid2.TriggerRepeat = 200;
% % Configure the camera for manual triggering for both sensors.
triggerconfig([vid vid2],'manual');
% % Start both video objects.
start([vid vid2]);
trigger([vid vid2])
[imgDepth, ts_depth, metaData_Depth] = getdata(vid2);
f=imgDepth;
figure,imshow(f);
figure,imshow(f,[0 4500]);
imwrite(f,'C:\Users\sufi\Desktop\matlab_kinect\Data_image\output\depth\fo.tiff');
stop([vid vid2]);
When I set the display range:
Without setting the display range:
The values in a 16bit image range from 0 to 65535.
If we take a look at the histogram of your image:
We see that the max value is 7995. But that's just a few outliers. Most information is somewhere between 700 and 4300.
So all our values are in 5-10% of our value range. That makes it look very dark.
In order to make it look better for humans we have to normalize it. (Some image viewer do this automatically).
So in order to get a nicer image into your power point presentation you have two options.
a) display it in an image viewer that can display it nicely and take a screenshot
b) normalize the image in matlab and save it to a file.
You can further improve the image by removing those outliers befor normalization.
One simple way can be scaling the image based on following formula:
Pixel_value=Pixel_value/4500*65535
If you want see the exact image that you get from uint8 ; I guess the following steps will work for you.
Probably while casting the image to uint8 matlab firstly clip the values above some threshold lets say 4095=2**12-1 (i'm not sure about value) and then it makes right shifts (4 shifts in our case) to make it inside the range of 0-255.
So i guess multiplying the value of uint8 with 256 and casting it as uint16 will help you get the same image
Pixel_uint16_value= Pixel_uint8_value*256 //or Pixel_uint16_value= Pixel_uint8_value<<8
//dont forget to cast the result as uint16

Histogram normalization for normalizing background changes

I recorded a sequence of depth images using Kinect v2. But the background brightness is not constant. But It keeps changing from dark to light and light to dark (i.e) .
So I was thinking to use Histogram normalization of each image in a sequence to normalise the background to the same level. Can anyone please tell me how I can do this?
Matlab has a function for histogram matching and their site has some great examples too
Just use any frame as the reference (I suggest using the first one, but there is no real reason to do so), and keep it for all the remaining frames. If you want to decrease processing time you can also try lowering the number of bins. For a uint8 image there are usually 256 bins, but as you'll see in the link reducing it still produces favorable results
I don't know if kinect images are rgb or grayscale, for this example Im assuming they are grayscale
kinect_images = Depth;
num_frames = size(kinect_images,3); %maybe 4, I don't know if kinect images
%are grayscale(3) or RGB(4)
num_of_bins = 32;
%imhistmatch is a recent addition to matlab, use this variable to
%indicate whether or not you have it
I_have_imhistmatch = true;
%output variable
equalized_images = cast(zeros(size(kinect_images)),class(kinect_images));
%stores first frame as reference
ref_image = kinect_images(:,:,1); %if rgb you may need (:,:,:,1)
ref_hist = imhist(ref_image);
%goes through every frame and matches the histof
for ii=1:1:num_frames
if (I_have_imhistmatch)
%use this with newer versions of matlab
equalized_images(:,:,ii) = imhistmatch(kinect_images(:,:,ii), ref_image, num_of_bins);
else
%use this line with older versions that dont have imhistmatch
equalized_images(:,:,ii) = histeq(kinect_images(:,:,ii), ref_hist);
end
end
implay(equalized_images)

comparing images pixel by pixel

I am working on some images. I am given an abc.tif image ( color image) . I read it as follows:
Mat test_image=imread("abc.tif",IMREAD_UNCHANGED);
I perform some operations on it and convert it into some binary image (using threshold) containing only two values 0 and 255 which are stored in img image where img is created as following:
Mat img(584,565,CV_8UC1); %//(so now img contains only 0 and 255)
I save this image using imwrite("myimage.jpg",img);
I want to compare the myimage.jpg image with another binary image manual.gif pixel by pixel to check whether one image is duplicate of another but as you can notice the problem is OpenCv doesnot support .gif format so I need to convert it into .jpg and because of that the image changes and now both the images will be concluded as different images may be even though they are same. What to do now?
Actually I am working on retinal blood vessel segmentation and these images are found in the DRIVE database.
I am given these images. Original image:
I perform some operations on it and extract blood vessels from it and then create a binary image and store in some Mat variable img as discussed earlier. Now I have got another image (.gif image) which I cannot load as shown below:
Now I want to compare my img image (binary) with the given .gif image (above) which I cannot load.
Use ImageMagic for converting your .gif to .PNG in batch mode. You could also convert it on the fly using system("convert img.gif img.png") call.
I'm not sure, if pixel comparison will give you good result. An offset shift of the same image will result in bad match.
EDIT As an idea. Maybe calculating centers of gravity and shifting/rotating both images to have the same origin may help here.
Consider using moments, freeman chain or other mode robust shape comparison methods.
first off you will want to use the images in the same format as each other #Adi mentioned jpg is lossy in the comments which is correct so shouldn't be used until possibly after any work is done. MATLAB - image conversion
you will also want the images to be of the same size. you can compare them using the size function and then pad them to add pixels to make the dimensions the same. the padding can always be removed later, just watch how the padding is added so as not to affect your operations.
you will also need to look into rotations, consider putting the image into the frequency domain and rotate the image to align the spectrum's.
below is a simple pixel comparison code, pixel comparison is not particularly accurate for comparing. even the slightest miss alignment will cause false negatives or false positives.
%read image
test_image1 = imread('C:\Users\Public\Pictures\Sample Pictures\Desert.jpg');
test_image2 = imread('C:\Users\Public\Pictures\Sample Pictures\Hydrangeas.jpg');
%convert to gray scale
gray_img1 = rgb2gray(test_image1);
gray_img2 = rgb2gray(test_image2);
% threshold image to put all values greater than 125 to 255 and all values
% below 125 to 0
binary_image1 = gray_img1 > 125;
binary_image2 = gray_img2 > 125;
%binary image to size to allow pixel by pixel checking
[row, col] = size(binary_image1);
% initialize the counters for similar and different pixelse to zero
similar = 0;
different = 0;
%two loops to scan through all rows and columns of the image.
for kk = 1 : row
for yy = 1 : col
%using if statement with isequal function to compare corresponding
%pixel values and count them depending ont he logical output of
%isequal
if isequal(binary_image1(kk,yy), binary_image2(kk,yy))
similar = similar + 1;
else
different = different + 1;
end
end
end
% calculate the percentage difference between the images and print it
total_pixels = row*col;
difference_percentage = (different / total_pixels) * 100;
fprintf('%f%% difference between the compared images \n%d pixels being different to %d total pixels\n', difference_percentage, different, total_pixels )
% simple supbtraction of the two images
diff_image = binary_image1 - binary_image2;
%generate figure to show the original gray and corresponding binary images
%as well as the subtraction
figure
subplot(2,3,1)
imshow(gray_img1);
title('gray img1');
subplot(2,3,2)
imshow(gray_img2);
title('gray img2');
subplot(2,3,4)
imshow(binary_image1);
title('binary image1');
subplot(2,3,5)
imshow(binary_image2);
title('binary image2');
subplot(2,3,[3,6])
imshow(diff_image);
title('diff image');

fusing more than 2 images in matlab

In MATLAB, how do I fuse more than two images? For example, I want to do what imfuse does but for more than 2 images. Using two images, this is the code I have:
A = imread('file1.jpg');
B = imread('file2.jpg');
C = imfuse(A,B,'blend','Scaling','joint');
C will be fused version of A and B. I have about 50 images to fuse. How do I achieve this?
You could write a for loop, then simply have a single image that stores all of the fused results and repeatedly fusing this image with a new image you read in. As such, let's say your images were named from file1.jpg to file50.jpg. You could do something like this:
A = imread('file1.jpg');
for idx = 2 : 50
B = imread(['file' num2str(idx) '.jpg']); %// Read in the next image
A = imfuse(A, B, 'blend', 'Scaling', 'joint'); %// Fuse and store into A
end
What the above code will do is that it will repeatedly read in the next image, and fuse it with the image stored in A. At each iteration, it will take what is currently in A, fuse it with a new image, then store it back in A. That way, as we keep reading in images, we will keep fusing new images on top of those images that were fused from before. After this for loop finishes, you will have 50 images that are all fused together.
imfuse with the 'blend' method performs alpha blending on two images. In the absence of an alpha channel on the images, this is nothing more than the arithmetic mean of each pair of corresponding pixels. Therefore, one way of interpreting the fusion of N images is to simply average N corresponding pixels, one from each image, to get the output image.
Assuming that:
All images are of size imgSize (e.g., [480,640])
All images have the same pixel value range (e.g., 0-255 for uint8 or 0-1 for double)
the following should give you something reasonable:
numImages = 50;
A = zeros(imgSize,'double');
for idx = 1:numImages
% Borrowing rayryeng's nice filename construction
B = imread(['file' num2str(idx) '.jpg']);
A = A + double(B);
end
A = A/numImages;
The result will be in the array A with type double after the loop and may need to be cast back to the proper type for your image.
Piggy-backing on rayryeng's solution:
What you want to do is increase the alpha at every step in proportion to how much that image is contributing to the already stored images. For Example:
Adding 1 image to 1 existing image, you would want an alpha of 0.5 so that they are equal.
Now adding one image to the 2 existing images, it should contribute 33% to the image and therefore an alpha of 0.33. 4th image should contribute 25% (Alpha=0.25) and so on.
The result follows an x^-1 trend. So at the 20th image, 1/20 = 0.05, so an alpha of 0.05 would be necessary.

Dimension of a TIF file as "420x560x3" - Matlab to IDL

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)