Efficiently segmenting an image into grid in matlab - matlab

Currently, I am using the code below to segment an image into a grid of cellSizeX pixels times cellSizeY pixels:
grid = zeros(cellSizeX, cellSizeY, ColorCount, cellTotalX, cellTotalY);
for i = 1:cellSizeX:(HorRowCount)
for j = 1:cellSizeY:(VertColumnCount)
try
grid(:,:,:,icount, jcount) = img(i:i+cellSizeX-1, j:j+cellSizeY-1, :);
catch
end
jcount = jcount + 1;
end
icount = icount + 1;
jcount = 1;
end
While this code runs fine and satisfactorily, there are things that nag me:
Via some testing with tic and toc, comparing switching index positions such as grid(:,:,:,icount,jcount) and grid(icount,jcount,:,:,:), I see that grid(:,:,:,icount,jcount) is fastest. But can anything be improved here?
The code will work only if the requested cellSizeX and cellSizeY are proportional to the image img. So requesting cellSizeX and cellSizeY of 9 x 9 on image with size 40 x 40 will result in matlab complaining about exceeding matrix's dimension. Any suggestion regarding this? I do not want to simply fill in blank area for those cells. These cells will be used further in Vlfeat SIFT.

How about converting the image into a cellarray with each cell of size CellSizeX x CellSizeY x ColorCount, then stacking all these cells to a single array grid?
ca = mat2cell( img, cellSizeY * ones(1, cellTotalY), ...
cellSizeX * ones(1, cellTotalX), ...
ColorCount );
grid = reshape( cat( 4, ca{:} ),...
cellSizeX, cellSizeY, ColorCount, cellTotalX, cellTotalY);
It is accustomed in the image processing community to pad image with non-zero values depending on the values of the image at the boundary. Look at the function padarray for more information. You may pad your input image such that its padded size will be proportional to CellSizeX and CellSizeY (padding does not have to be identical at both axes).

Related

How to convert RGB images to grayscale in matlab without using rgb2gray

I'm currently using code:
i = imread('/usr/share/icons/matlab.png');
for k=1:1:m
for l=1:1:n
%a(k,l)=m*n;
a(k,l) = (.299*i(k,l,1))+(.587*i(k,l,2))+(.114*i(k,l,3));
end
end
imshow(a);
It shows only a white screen. Also the newly generated dimensions are n x m x 3 whereas it should be only m x n x 1.
If I use mat2gray it display the image like this
Since the image is a PNG, imread() is returning an integer image, with intensity values in the range [0 255] or equivalent, depending on the original bit depth. The conversion formula makes a a double image, which is expected to have intensities in the range [0 1]. Since all the pixel values in a are probably much greater than 1, they get clipped to 1 (white) by imshow().
The best option is to explicitly convert the image format before you start - this will take care of scaling things correctly:
i = imread('/usr/share/icons/matlab.png');
i = im2double(i);
a = .299*i(:,:,1) + .587*i(:,:,2) + .114*i(:,:,3); % no need for loops
imshow(a);
input=imread('test.jpg');
subplot(1,2,1), imshow(input), title('RGB Scale image');
[x,y,~] = size(input);
for i = 1:1:x
for j = 1:1:y
output(i,j) = 0.40*input(i,j,1) + 0.50*input(i,j,2) + 0.30*input(i,j,3);
end
end
subplot(1,2,2), imshow(output), title('Gray Scale image');

A way to extract hands from a video

I wonder whether it would be possible to extract only hands from a video with matlab. In the video hands perform some gesture. Because first frames are only background I tried in this way:
readerObj = VideoReader('VideoWithHands.mp4');
nFrames = readerObj.NumberOfFrames;
fr = get(readerObj, 'FrameRate');
writerObj = VideoWriter('Hands.mp4', 'MPEG-4');
set(writerObj, 'FrameRate', fr);
open(writerObj);
bg = read(readerObj, 1); %background
for k = 1 : nFrames
frame = read(readerObj, k);
hands = imabsdiff(frame,bg);
writeVideo(writerObj,hands);
end
close(writerObj);
But I realized that colors of the hands are not "real" and they are transparent. Is there a better way to extract them from video keeping colors and opacity level exploiting the first frames (background)?
EDIT: Well, I have found a good setting for vision.ForegroundDetector object, now hands are white logical regions but when I try to visualize them with:
videoSource = vision.VideoFileReader('VideoWithHands.mp4', 'VideoOutputDataType', 'uint8');
detector = vision.ForegroundDetector('NumTrainingFrames', 46, 'InitialVariance', 4000, 'MinimumBackgroundRatio', 0.2);
videoplayer = vision.VideoPlayer();
hands = uint8(zeros(720,1280,3));
while ~isDone(videoSource)
frame = step(videoSource);
fgMask = step(detector, frame);
[m,n] = find(fgMask);
a = [m n];
if isempty(a)==true
hands(:,:,:) = uint8(zeros(720,1280,3));
else
hands(m,n,1) = frame(m,n,1);
hands(m,n,2) = frame(m,n,2);
hands(m,n,3) = frame(m,n,3);
end
step(videoplayer, hands)
end
release(videoplayer)
release(videoSource)
or put them into a videofile with:
eaderObj = VideoReader('Video 9.mp4');
nFrames = readerObj.NumberOfFrames;
fr = get(readerObj, 'FrameRate');
writerObj = VideoWriter('hands.mp4', 'MPEG-4');
set(writerObj, 'FrameRate', fr);
detector = vision.ForegroundDetector('NumTrainingFrames', 46, 'InitialVariance', 4000, 'MinimumBackgroundRatio', 0.2);
open(writerObj);
bg = read(readerObj, 1);
frame = uint8(zeros(size(bg)));
for k = 1 : nFrames
frame = read(readerObj, k);
fgMask = step(detector, frame);
[m,n] = find(fgMask);
hands = uint8(zeros(720,1280));
if isempty([m n]) == true
hands(:,:) = uint8(zeros(720,1280));
else
hands(m,n) = frame(m,n);
end
writeVideo(writerObj,mani);
end
close(writerObj);
...my PC crashes. Some suggestion?
So you're trying to cancel out the background, making it black, right?
The easiest way to do this should be to filter it, you can do that by comparing your difference data to a threshold value and then using the result as indices to set a custom background.
filtered = imabsdiff(frame,bg);
bgindex = find( filtered < 10 );
frame(bgindex) = custombackground(bgindex);
where custombackground is whatever image file you want to put into the background. If you want it to be just black or white, use 0 or 255 instead of custombackground(bgindex). Note that the numbers depend on your video data's format and could be inaccurate (except 0, this one should always be right). If too much gets filtered out, lower the 10 above, if too much remains unfiltered, increase the 10.
At the end, you write your altered frame back into the video, so it just replaces the hands variable in your code.
Also, depending on your format, you might have to do the comparison across RGB values. This is slightly more complicated as it involves checking 3 values at the same time and doing some magic with the indices. This is the RGB version (works with anything containing 3 color bands):
filtered = imabsdiff(frame,bg); % differences at each pixel in each color band
totalfiltered = sum(filtered,3); % sums up the differences
% in each color band (RGB)
bgindex = find( totalfiltered < 10 ); % extracts indices of pixels
% with color close to bg
allind = sub2ind( [numel(totalfiltered),3] , repmat(bgindex,1,3) , ...
repmat(1:3,numel(bgindex),1) ); % index magic
frame(allind) = custombackground(allind); % copy custom background into frame
EDIT :
Here's a detailed explanation of the index magic.
Let's assume a 50x50 image. Say the pixel at row 2, column 5 is found to be background, then bgindex will contain the number 202 (linear index corresponding to [2,5] = (5-1)*50+2 ). What we need is a set of 3 indices corresponding to the matrix coordinates [2,5,1], [2,5,2] and [2,5,3]. That way, we can change all 3 color bands corresponding to that pixel. To make calculations easier, this approach actually assumes linear indexing for the image and thus converts it to a 2500x1 image. Then it expands the 3 color bands, creating a 2500x3 matrix. We now construct the indices [202,1], [202,2] and [202,3] instead.
To do that, we first construct a matrix of indices by repeating our values. repmat does this for us, it creates the matrices [202 202 202] and [1 2 3]. If there were more pixels in bgindex, the first matrix would contain more rows, each repeating the linear pixel coordinates 3 times. The second matrix would contain additional [1 2 3] rows. The first argument to sub2ind is the size of the matrix, in this case, 2500x3, so we calculate the number of pixels with numel applied to the sum vector (which collapses the image's 3 bands into 1 value and thus has 1 value per pixel) and add a static 3 in the second dimension.
sub2ind now takes each element from the first matrix as a row index, each corresponding element from the second matrix as a column index and converts them to linear indices into a matrix of the size we determined earlier. In our example, this results in the indices [202 2702 5202]. sub2ind preserves the shape of the inputs, so if we had 10 background pixels, this result would have the size 10x3. But since linear indexing doesn't care about the shape of the index matrix, it just takes all of those values.
To confirm this is correct, let's revert the values in the example. The original image data would have the size 50x50x3. For an NxMxP matrix, a linear index to the subscript [n m p] can be calculated as ind = (p-1)*M*N + (m-1)*N + n. Using our values, we get the following:
[2 5 1] => 202
[2 5 2] => 2702
[2 5 3] => 5202
ind2sub confirms this.
Yes, there is a better way. The computer vision system toolbox includes a vision.ForegroundDetector object that does what you need. It implements the Gaussian Mixture Model algorithm for background subtraction.

How can thin horizontal lines be added between each row in a grayscale image?

I need to create an nth-order Hadamard matrix, row double it, within each row randomly permute the elements of the matrix, and then display it. So far, I have accomplished all of these things. What I end up with when I imshow(matrix) is a nice picture of black and white boxes. But I haven't figured out how to insert a fine line to divide each row. I can create something like the first image on the left, but not the image on the right (these are Figures 1 and 2 from this paper)
Any help or comments would be thoroughly appreciated.
I've found using vector approaches (e.g., patch and rectangle) for this sort of problem unnecessarily challenging. I think that it's more straightforward to build a new image. This avoids floating-point rounding issues and other things that crop up with vector graphics. My solution below relies on some functions in the Image Processing Toolbox, but is simple and fast:
% Create data similarly to #TryHard
H = hadamard(48);
C = (1+[H;-H])/2;
rng(0); % Set seed
C(:) = C(randperm(numel(C))); % For demo, just permute all values, not rows
% Scale image and lines
scl = 10; % Amount to vertically scale each row
pad = 2; % Number of pixels to add between each row
C = imresize(C,scl,'nearest');
C = blockproc(C,[scl size(C,2)],#(x)[x.data;zeros(pad,size(C,2))]);
C = C(1:end-pad,:); % Remove last line added
% Dispay image
imshow(C)
This results in an image like this
The scl and pad parameters can be easily adjusted to obtain different sizes and relative sizes. You can call imresize(...,'nearest') again after adding the lines to further scale the image if desired. The blocproc line could potentially be made more efficient with various options (see the help). It could also be replaced by calls to im2col and col2im, which possibly could be faster, if messier.
I did not try the code, but I think that something like that should work:
sizeOfACube = 6;
numberOfRows = 47;
RGB = imread('image.png');
RGB = imresize(A, [(numRows+numberOfRows) numCols]);
for i=1:1:NumberOfRows
RGB(i*6,:,:) = 0;
end
imagesc(RGB);
imwrite(RGB,'newImage.png');
with:
sizeOfAcube the size of one cube on the QRcode.
numRows and numCols the number of Rows and Column of the original image.
One solution is to use patches, for instance as follows:
% set up example array
xl = 24; yl = xl;
[X Y] = find(hadamard(xl)==1);
% generate figure
figure, hold on
for ii=1:length(X)
patch(X(ii) + [0 0 1 1],Y(ii) + [0.1 0.9 0.9 0.1],[1 1 1],'Edgecolor',[1 1 1])
end
axis([0 xl+1 0 yl+1])
axis('square')
The patch command patch(x,y, color) accepts the vertices of the polygon element as x and y. In this example you can modify the term [0.1 0.9 0.9 0.1] to set the thickness of the bounding black line.
This generates
Edited
For the particular instance provided by the OP:
H=Hadamard(48); %# now to row-double the matrix
A=(1+H)/2;
B=(1-H)/2;
C=[A; B]; %# the code below randomly permutes elements within the rows of the matrix
[nRows,nCols] = size(C);
[junk,idx] = sort(rand(nRows,nCols),2); %# convert column indices into linear indices
idx = (idx-1)*nRows + ndgrid(1:nRows,1:nCols); %# rearrange whatever matrix
E = C;
E(:) = E(idx);
[X Y] = find(logical(E));
xl = length(X);
yl = length(Y);
figure, hold on
for ii=1:xl
rectangle('Position',[X(ii) Y(ii)+.2 1 0.8],'facecolor',[1 1 1],'edgecolor',[1 1 1])
end
axis([0 max(X)+1 0 max(Y)+1])
axis('square')
set(gca,'color',[0 0 0])
set(gca,'XTickLabel',[],'YTickLabel',[],'XTick',[],'YTick',[])
This example uses rectangle instead of patch to generate sharp corners.
The image:

Making two images of equal dimensions in MATLAB

i am trying to apply SURF algorithm, which is the algo to find the key points and matches corresponding two images, but the problem is that in my case , i want to apply that algo with two images, but they are of different dimension and so it fails to work, could you please tell what shall i do so that both images become equal dimensions.
That depends on your problem. If the images have the same resolution, I suggest you either crop one image or extend one image (with zeros?) to match the number of pixels.
Otherwise I suggest you interpolate the images so that they are defined at the same points, for example with interp2.
Can you give a minimum working code example of what you are trying to do?
Edit:
The code example you gave starts with loading to images.
% Example 2, Corresponding points
% Load images
I1=imread('TestImages/lena1.png');
I2=imread('TestImages/testc2.png');
Here are a few examples of how you can make the images match. You have to decide which one of these makes sense in your case.
Method 1: crop both images
nrows = min(size(I1,1), size(I2,1));
ncols = min(size(I1,2), size(I2,2));
% The + floor( ... ) is only for centering the bounding box
croppedI1 = I1( (1:nrows) + floor((size(I1,1)-nrows)/2), ...
(1:ncols)+floor((size(I1,2)-ncols)/2), ...
:);
croppedI2 = I2( (1:nrows) + floor((size(I2,1)-nrows)/2, ...
(1:ncols) + floor((size(I2,2)-ncols)/2), ...
:);
I1=croppedI1;
I2=croppedI2;
Method 2: Extend the images with zeros until they match
nrows = max(size(I1,1), size(I2,1));
ncols = max(size(I1,2), size(I2,2));
nchannels = size(I1,3);
extendedI1 = [ I1, zeros(size(I1,1), ncols-size(I1,2), nchannels); ...
zeros(nrows-size(I1,1), ncols, nchannels)];
extendedI2 = [ I2, zeros(size(I2,1), ncols-size(I2,2), nchannels); ...
zeros(nrows-size(I2,1), ncols, nchannels)];
I1=extendedI1;
I2=extendedI2;
Method 3: Scale the second image to the size of the first one (which destroys the aspect ratio)
% Scale the coordinates so that they range from 0 to 1 each.
[X1, Y1]=meshgrid( linspace(0, 1, size(I1,2)), linspace(0, 1, size(I1,1)));
[X2, Y2]=meshgrid( linspace(0, 1, size(I2,2)), linspace(0, 1, size(I2,1)));
nchannels = size(I1,3);
% interpolate each color plane separately
for k=1:nchannels
scaledI2(:,:,k)=interp2(X2, Y2, double(I2(:,:,k)), X1, Y1);
end
%I1=I1;
I2=scaledI2;
% code for to play the images or to take same size images into array
r=0;c=0;
a1=imread(filename1);
[r c]=size(a1);
b=imread(filename2);
a2= imresize(b, [r c/3]);
% re-sized second image according to 1st one if u have specific
row col number then you can give like that
a=cat(4,a1,a2); % "a" array having a1 a2 images
implay(a,framerate ); % to play images

Image processing using MATLAB, matrices

I'm currently working with MATLAB to do some image processing. I've been set a task to basically recreate the convolution function for applying filters. I managed to get the code working okay and everything seemed to be fine.
The next part was for me to do the following..
Write your own m-function for unsharp masking of a given image to produce a new output image.
Your function should apply the following steps:
Apply smoothing to produce a blurred version of the original image,
Subtract the blurred image from the original image to produce an edge image,
Add the edge image to the original image to produce a sharpened image.
Again I've got code mocked up to do this but I run into a few problems. When carrying out the convolution, my image is cropped down by one pixel, this means when I go to carry out the subtraction for the unsharpening the images are not the same size and the subtraction cannot take place.
To overcome this I want to create a blank matrix in the convolution function that is the same size as the image being inputted, the new image will then go on top of this matrix so in affect the new image has a one pixel border around it to make it to its original size. When I try and implement this, all I get as an output is the blank matrix I just created. Why is this happening and if so would you be able to help me fix it?
My code is as follows.
Convolution
function [ imgout ] = convolution( img, filter )
%UNTITLED Summary of this function goes here
% Detailed explanation goes here
[height, width] = size(img); % height, width: number of im rows, etc.
[filter_height, filter_width] = size(filter);
for height_bound = 1:height - filter_height + 1; % Loop over output elements
for width_bound = 1:width - filter_width + 1;
imgout = zeros(height_bound, width_bound); % Makes an empty matrix the correct size of the image.
sum = 0;
for fh = 1:filter_height % Loop over mask elements
for fw = 1:filter_width
sum = sum + img(height_bound - fh + filter_height, width_bound - fw + filter_width) * filter(fh, fw);
end
end
imgout(height_bound, width_bound) = sum; % Store the result
end
end
imshow(imgout)
end
Unsharpen
function sharpen_image = img_sharpen(img)
blur_image = medfilt2(img);
convolution(img, filter);
edge_image = img - blur_image;
sharpen_image = img + edge_image;
end
Yes. Concatenation, e.g.:
A = [1 2 3; 4 5 6]; % Matrix
B = [7; 8]; % Column vector
C = [A B]; % Concatenate