Matlab - Scale down an image using an average of four pixels - image

I have just started learning image-processing and Matlab and I'm trying to scale down an image using an average of 4 pixels. That means that for every 4 original pixels I calculate the average and produce 1 output pixel.
So far I have the following code:
img = imread('bird.jpg');
row_size = size(img, 1);
col_size = size(img, 2);
res = zeros(floor(row_size/2), floor(col_size/2));
figure, imshow(img);
for i = 1:2:row_size
for j = 1:2:col_size
num = mean([img(i, j), img(i, j+1), img(i+1, j), img(i+1, j+1)]);
res(round(i/2), round(j/2)) = num;
end
end
figure, imshow(uint8(res));
This code manages to scale down the image but it converts it to grayscale.
I understand that I probably have to calculate the average of the RGB components for the output pixel but I don't know how to access them, calculate the average and insert them to the result matrix.

In Matlab, an RGB image is treated as a 3D array. You can check it with:
depth_size = size(img, 3)
depth_size =
3
The loop solution, as you have done, is explained in Sardar_Usama's answer. However, in Matlab it is recommended to avoid loops whenever you want to gain speed.
This is a vectorized solution to scale down an RGB image by a factor of n:
img = imread('bird.jpg');
n = 2; % n can only be integer
[row_size, col_size] = size(img(:, :, 1));
% getting rid of extra rows and columns that won't be counted in averaging:
I = img(1:n*floor(row_size / n), 1:n*floor(col_size / n), :);
[r, ~] = size(I(:, :, 1));
% separating and re-ordering the three colors of image in a way ...
% that averaging could be done with a single 'mean' command:
R = reshape(permute(reshape(I(:, :, 1), r, n, []), [2, 1, 3]), n*n, [], 1);
G = reshape(permute(reshape(I(:, :, 2), r, n, []), [2, 1, 3]), n*n, [], 1);
B = reshape(permute(reshape(I(:, :, 3), r, n, []), [2, 1, 3]), n*n, [], 1);
% averaging and reshaping the colors back to the image form:
R_avg = reshape(mean(R), r / n, []);
G_avg = reshape(mean(G), r / n, []);
B_avg = reshape(mean(B), r / n, []);
% concatenating the three colors together:
scaled_img = cat(3, R_avg, G_avg, B_avg);
% casting the result to the class of original image
scaled_img = cast(scaled_img, 'like', img);
Benchmarking:
If you want to know why vectorized solutions are more popular, take a look at how long it takes to process an RGB 768 x 1024 image with the two methods:
------------------- With vectorized solution:
Elapsed time is 0.024690 seconds.
------------------- With nested loop solution:
Elapsed time is 6.127933 seconds.
So there is more than 2 orders of magnitude difference of speed between the two solutions.

Another possible solution can be using the function blockproc as mentioned at this link. This will also avoid for loops.

You can take care of that using the modified code below:
img = imread('bird.jpg');
row_size = size(img, 1);
col_size = size(img, 2);
figure, imshow(img);
res = zeros(floor(row_size/2), floor(col_size/2), 3); %Pre-allocation
for p = 1:2:row_size
for q = 1:2:col_size
num = mean([img(p, q,:), img(p, q+1,:), img(p+1, q,:), img(p+1, q+1,:)]);
res(round(p/2), round(q/2),:) = num;
end
end
figure, imshow(uint8(res));
I took a sample image of 1200x1600x3 uint8 which is converted to 600x800x3 uint8 by the above code which is correct because (1200*1600)/4 = 480000 and 600*800 = 480000
P.S : I changed the variable names i and j to p and q respectively since i and j are reserved for imaginary numbers.

Related

Why am I getting black frames in this montage image?

Here's the code I'm using:
function [filterResponses] = extractFilterResponses(img, filterBank)
% Extract filter responses for the given image.
% Inputs:
% img: a 3-channel RGB image with width W and height H
% filterBank: a cell array of N filters
% Outputs:
% filterResponses: a W x H x N*3 matrix of filter responses
if (~isa(img, 'double'))
img = double(img);
end
if (size(img, 3) == 1)
img = repmat(img, [1 1 3]);
end
img = img./255;
[L, a, b] = RGB2Lab(img(:, :, 1), img(:, :, 2), img(:, :, 3));
filterResponses = zeros(size(img,1), size(img, 2), length(filterBank)*3);
for k = 1:length(filterBank)
L = imfilter(L, filterBank{k}, 'same', 'conv', 'replicate');
filterResponses(:, :, k*3-2) = L;
a = imfilter(a, filterBank{k}, 'same', 'conv', 'replicate');
filterResponses(:, :, k*3-1) = a;
b = imfilter(b, filterBank{k}, 'same', 'conv', 'replicate');
filterResponses(:, :, k*3) = b;
end
end
The above function applies one filter at a time from a set of 20 filters on each of the L*a*b layers of the given RGB image.
The following script is used to execute the function:
img = imread('sun_advbapyfkehgemjf.jpg');
filterBank = createFilterBank();
filteredImg = extractFilterResponses(img, filterBank);
filteredImgCell = cell(20,1);
for k = 1:length(filterBank)
filteredImgCell{k} = cat(3, filteredImg(:, :, k*3-2), filteredImg(:, :, k*3-1), ...
filteredImg(:, :, k*3));
filteredImgCell{k} = repmat(filteredImgCell{k}, [1 1 1 1]);
end
montage(cat(4, filteredImgCell{:}), 'size', [4 5]);
This script concatenates the L*a*b layers from the matrix filterResponses and then repmats the image to add a fourth dimension to be used in the montage function and gets stored in a cell. The cell is used in the montage function.
The output I am getting is as follows:
Why do the rest of the frames appear black? I know they are there cause if I multiply each image with say 10, I can see a few more frames. So, must be something to do with normalization?
There are two possible issues:
You are adding the filters sequentially to the Lab components, such that at iteration k you have applied all of the filters from 1 to k to them. This is going to continuously reduce the amplitude of your image values, causing them to become small enough that, when added to a montage, the smaller-value images appear to have very little dynamic range and just show up as black.
I'm guessing you want to apply just filter k at iteration k, as opposed to all previous ones. If so, you should change your loop code to the following:
for k = 1:length(filterBank)
Lk = imfilter(L, filterBank{k}, 'same', 'conv', 'replicate');
filterResponses(:, :, k*3-2) = Lk;
ak = imfilter(a, filterBank{k}, 'same', 'conv', 'replicate');
filterResponses(:, :, k*3-1) = ak;
bk = imfilter(b, filterBank{k}, 'same', 'conv', 'replicate');
filterResponses(:, :, k*3) = bk;
end
Without knowing what the exact input image is, I see a potential problem with this particular line that scales the image values:
img = img./255;
You aren't checking the range of values in the input first. If the input image is already scaled from 0 to 1, this will reduce the maximum amplitude to a value much smaller than 1. As above, repeated applications of your filters could cause the values to become small enough that, when added to a montage, the smaller-value images appear to have very little dynamic range and just show up as black.
I'd suggest checking the range of the input image and scaling based on that. One option is to scale the image by its own maximum value to give a resulting range of 0 to 1:
img = img./max(img(:));

How to get the length of n curves present in an image in Matlab?

Currently I have been working on obtaining the length of a curve, with the following code I have managed to get the length of a curve present in an image.
test image one curve
Then I paste the code that I used to get the length of the curve of a simple image. What I did is the following:
I got the columns and rows of the image
I got the columns in x and the rows in y
I obtained the coefficients of the curve, based on the formula of the
parable
Build the equation
Implement the arc length formula to obtain the length of the curve
grayImage = imread(fullFileName);
[rows, columns, numberOfColorBands] = size(grayImage);
if numberOfColorBands > 1
grayImage = grayImage(:, :, 2); % Take green channel.
end
subplot(2, 2, 1);
imshow(grayImage, []);
% Get the rows (y) and columns (x).
[rows, columns] = find(binaryImage);
coefficients = polyfit(columns, rows, 2); % Gets coefficients of the formula.
% Fit a curve to 500 points in the range that x has.
fittedX = linspace(min(columns), max(columns), 500);
% Now get the y values.
fittedY = polyval(coefficients, fittedX);
% Plot the fitting:
subplot(2,2,3:4);
plot(fittedX, fittedY, 'b-', 'linewidth', 4);
grid on;
xlabel('X', 'FontSize', fontSize);
ylabel('Y', 'FontSize', fontSize);
% Overlay the original points in red.
hold on;
plot(columns, rows, 'r+', 'LineWidth', 2, 'MarkerSize', 10)
formula = poly2sym([coefficients(1),coefficients(2),coefficients(3)]);
% formulaD = vpa(formula)
df=diff(formula);
df = df^2;
f= (sqrt(1+df));
i = int(f,min(columns),max(columns));
j = double(i);
disp(j);
Now I have the image 2 which has n curves, I do not know how I can do to get the length of each curve
test image n curves
I suggest you to look at Hough Transformation:
https://uk.mathworks.com/help/images/hough-transform.html
You will need Image Processing Toolbox. Otherwise, you have to develop your own logic.
https://en.wikipedia.org/wiki/Hough_transform
Update 1
I had a two-hour thinking about your problem and I'm only able to extract the first curve. The problem is to locate the starting points of the curves. Anyway, here is the code I come up with and hopefully will give you some ideas for further development.
clc;clear;close all;
grayImage = imread('2.png');
[rows, columns, numberOfColorBands] = size(grayImage);
if numberOfColorBands > 1
grayImage = grayImage(:, :, 2); % Take green channel.
end
% find edge.
bw = edge(grayImage,'canny');
imshow(bw);
[x, y] = find(bw == 1);
P = [x,y];
% For each point, find a point that is of distance 1 or sqrt(2) to it, i.e.
% find its connectivity.
cP = cell(1,length(x));
for i = 1:length(x)
px = x(i);
py = y(i);
dx = x - px*ones(size(x));
dy = y - py*ones(size(y));
distances = (dx.^2 + dy.^2).^0.5;
cP{i} = [x(distances == 1), y(distances == 1);
x(distances == sqrt(2)), y(distances == sqrt(2))];
end
% pick the first point and a second point that is connected to it.
fP = P(1,:);
Q(1,:) = fP;
Q(2,:) = cP{1}(1,:);
m = 2;
while true
% take the previous point from point set Q, when current point is
% Q(m,1)
pP = Q(m-1,:);
% find the index of the current point in point set P.
i = find(P(:,1) == Q(m,1) & P(:,2) == Q(m,2));
% Find the distances from the previous points to all points connected
% to the current point.
dx = cP{i}(:,1) - pP(1)*ones(length(cP{i}),1);
dy = cP{i}(:,2) - pP(2)*ones(length(cP{i}),1);
distances = (dx.^2 + dy.^2).^0.5;
% Take the farthest point as the next point.
m = m+1;
p_cache = cP{i}(find(distances==max(distances),1),:);
% Calculate the distance of this point to the first point.
distance = ((p_cache(1) - fP(1))^2 + (p_cache(2) - fP(2))^2).^0.5;
if distance == 0 || distance == 1
break;
else
Q(m,:) = p_cache;
end
end
% By now we should have built the ordered point set Q for the first curve.
% However, there is a significant weakness and this weakness prevents us to
% build the second curve.
Update 2
Some more work since the last update. I'm able to separate each curve now. The only problem I can see here is to have a good curve fitting. I would suggest B-spline or Bezier curves than polynomial fit. I think I will stop here and leave you to figure out the rest. Hope this helps.
Note that the following script uses Image Processing Toolbox to find the edges of the curves.
clc;clear;close all;
grayImage = imread('2.png');
[rows, columns, numberOfColorBands] = size(grayImage);
if numberOfColorBands > 1
grayImage = grayImage(:, :, 2); % Take green channel.
end
% find edge.
bw = edge(grayImage,'canny');
imshow(bw);
[x, y] = find(bw == 1);
P = [x,y];
% For each point, find a point that is of distance 1 or sqrt(2) to it, i.e.
% find its connectivity.
cP =[0,0]; % add a place holder
for i = 1:length(x)
px = x(i);
py = y(i);
dx = x - px*ones(size(x));
dy = y - py*ones(size(y));
distances = (dx.^2 + dy.^2).^0.5;
c = [find(distances == 1); find(distances == sqrt(2))];
cP(end+1:end+length(c),:) = [ones(length(c),1)*i, c];
end
cP (1,:) = [];% remove the place holder
% remove duplicates
cP = unique(sort(cP,2),'rows');
% seperating curves
Q{1} = cP(1,:);
for i = 2:length(cP)
cp = cP(i,:);
% search for points in cp in Q.
for j = 1:length(Q)
check = ismember(cp,Q{j});
if ~any(check) && j == length(Q) % if neither has been saved in Q
Q{end+1} = cp;
break;
elseif sum(check) == 2 % if both points cp has been saved in Q
break;
elseif sum(check) == 1 % if only one of the points exists in Q, add the one missing.
Q{j} = [Q{j}, cp(~check)];
break;
end
end
% review sets in Q, merge the ones having common points
for j = 1:length(Q)-1
q = Q{j};
for m = j+1:length(Q)
check = ismember(q,Q{m});
if sum(check)>=1 % if there are common points
Q{m} = [Q{m}, q(~check)]; % merge
Q{j} = []; % delete the merged set
break;
end
end
end
Q = Q(~cellfun('isempty',Q)); % remove empty cells;
end
% each cell in Q represents a curve. Note that points are not ordered.
figure;hold on;axis equal;grid on;
for i = 1:length(Q)
x_ = x(Q{i});
y_ = y(Q{i});
coefficients = polyfit(y_, x_, 3); % Gets coefficients of the formula.
% Fit a curve to 500 points in the range that x has.
fittedX = linspace(min(y_), max(y_), 500);
% Now get the y values.
fittedY = polyval(coefficients, fittedX);
plot(fittedX, fittedY, 'b-', 'linewidth', 4);
% Overlay the original points in red.
plot(y_, x_, 'r.', 'LineWidth', 2, 'MarkerSize', 1)
formula = poly2sym([coefficients(1),coefficients(2),coefficients(3)]);
% formulaD = vpa(formula)
df=diff(formula);
lengthOfCurve(i) = double(int((sqrt(1+df^2)),min(y_),max(y_)));
end
Result:
You can get a good approximation of the arc lengths using regionprops to estimate the perimeter of each region (i.e. arc) and then dividing that by 2. Here's how you would do this (requires the Image Processing Toolbox):
img = imread('6khWw.png'); % Load sample RGB image
bw = ~imbinarize(rgb2gray(img)); % Convert to grayscale, then binary, then invert it
data = regionprops(bw, 'PixelList', 'Perimeter'); % Get perimeter (and pixel coordinate
% list, for plotting later)
lens = [data.Perimeter]./2; % Compute lengths
imshow(bw) % Plot image
hold on;
for iLine = 1:numel(data),
xy = mean(data(iLine).PixelList); % Get mean of coordinates
text(xy(1), xy(2), num2str(lens(iLine), '%.2f'), 'Color', 'r'); % Plot text
end
And here's the plot this makes:
As a sanity check, we can use a simple test image to see how good an approximation this gives us:
testImage = zeros(100); % 100-by-100 image
testImage(5:95, 5) = 1; % Add a vertical line, 91 pixels long
testImage(5, 10:90) = 1; % Add a horizontal line, 81 pixels long
testImage(2020:101:6060) = 1; % Add a diagonal line 41-by-41 pixels
testImage = logical(imdilate(testImage, strel('disk', 1))); % Thicken lines slightly
Running the above code on this image, we get the following:
As you can see the horizontal and vertical line lengths come out close to what we expect, and the diagonal line is a little bit more than sqrt(2)*41 due to the dilation step extending its length slightly.
I try with this post but i don´t understand so much, but the idea Colours123 sounds great, this post talk about GUI https://www.mathworks.com/matlabcentral/fileexchange/24195-gui-utility-to-extract-x--y-data-series-from-matlab-figures
I think that you should go through the image and ask if there is a '1' if yes, ask the following and thus identify the beginning of a curve, get the length and save it in a BD, I am not very good with the code , But that's my idea

Image deblurring using gaussian filter in matlab without additive noise

I have to use an inverse filter to remove the blurring from this image
.
Unfortunately, I have to figure out the transfer function H of the imaging
system used to get these sharper images, It should be Gaussian. So, I should determine the approximate width of the Gaussian by trying different Gaussian widths in an inverse filter and judging which resulting images look the “best”.
The best result will be optimally sharp – i.e., edges will look sharp but will not have visible ringing.
I tried by using 3 approaches:
I created a transfer function with N dimensions (odd number, for simplicity), by creating a grid of N dimensions, and then applying the Gaussian function to this grid. After that, we add zeroes to this transfer function in order to get the same size as the original image. However, after applying the filter to the original image, I just see noise (too many artifacts).
I created the transfer function with size as high as the original image, by creating a grid of the same size as the original image. If sigma is too small, then the PSF FFT magnitude is wide. Otherwise it gets thinner. If sigma is small, then the image is even more blurred, but if we set a very high sigma value then we get the same image (not better at all).
I used the fspecial function, playing with sizes of sigma and h. But still I do not get anything sharper than the original blurred image.
Any ideas?
Here is the code used for creating the transfer function in Approach 1:
%Create Gaussian Filter
function h = transfer_function(N, sigma, I) %N is the dimension of the kernel
%create a 2D-grid that is the same size as the Gaussian filter matrix
grid = -floor(N/2) : floor(N/2);
[x, y] = meshgrid(grid, grid);
arg = -(x.*x + y.*y)/(2*sigma*sigma);
h = exp(arg); %gaussian 2D-function
kernel = h/sum(h(:)); %Normalize so that total weight equals 1
[rows,cols] = size(I);
add_zeros_w = (rows - N)/2;
add_zeros_h = (cols - N)/2;
h = padarray(kernel,[add_zeros_w add_zeros_h],0,'both'); % h = kernel_final_matrix
end
And this is the code for every approach:
I = imread('lena_blur.jpg');
I1 = rgb2gray(I);
figure(1),
I1 = double(I1);
%---------------Approach 1
% N = 5; %Dimension Assume is an odd number
% sigma = 20; %The bigger number, the thinner the PSF in FREQ
% H = transfer_function(N, sigma, I1);
%I1=I1(2:end,2:end); %To simplify operations
imagesc(I1); colormap('gray'); title('Original Blurred Image')
I_fft = fftshift(fft2(I1)); %Shift the image in Fourier domain to let its DC part in the center of the image
% %FILTER-----------Approach 2---------------
% N = 5; %Dimension Assume is an odd number
% sigma = 20; %The bigger number, the thinner the PSF in FREQ
%
%
% [x,y] = meshgrid(-size(I,2)/2:size(I,2)/2-1, -size(I,1)/2:size(I,1)/2-1);
% H = exp(-(x.^2+y.^2)*sigma/2);
% %// Normalize so that total area (sum of all weights) is 1
% H = H /sum(H(:));
%
% %Avoid zero freqs
% for i = 1:size(I,2) %Cols
% for j = 1:size(I,1) %Rows
% if (H(i,j) == 0)
% H(i,j) = 1e-8;
% end
% end
% end
%
% [rows columns z] = size(I);
% G_filter_fft = fft2(H,rows,columns);
%FILTER---------------------------------
%Filter--------- Aproach 3------------
N = 21; %Dimension Assume is an odd number
sigma = 1.25; %The bigger number, the thinner the PSF in FREQ
H = fspecial('gaussian',N,sigma)
[rows columns z] = size(I);
G_filter_fft = fft2(H,rows,columns);
%Filter--------- Aproach 3------------
%DISPLAY FFT PSF MAGNITUDE
figure(2),
imshow(fftshift(abs(G_filter_fft)),[]); title('FFT PSF magnitude 2D');
% Yest = Y_blurred/Gaussian_Filter
I_restoration_fft = I_fft./G_filter_fft;
I_restoration = (ifft2(I_restoration_fft));
I_restoration = abs(I_restoration);
I_fft = abs(I_fft);
% Display of Frequency domain (To compare with the slides)
figure(3),
subplot(1,3,1);
imagesc(I_fft);colormap('gray');title('|DFT Blurred Image|')
subplot(1,3,2)
imshow(log(fftshift(abs(G_filter_fft))+1),[]) ;title('| Log DFT Point Spread Function + 1|');
subplot(1,3,3)
imagesc(abs(I_restoration_fft));colormap('gray'); title('|DFT Deblurred|')
% imshow(log(I_restoration+1),[])
%Display PSF FFT in 3D
figure(4)
hf_abs = abs(G_filter_fft);
%270x270
surf([-134:135]/135,[-134:135]/135,fftshift(hf_abs));
% surf([-134:134]/134,[-134:134]/134,fftshift(hf_abs));
shading interp, camlight, colormap jet
xlabel('PSF FFT magnitude')
%Display Result (it should be the de-blurred image)
figure(5),
%imshow(fftshift(I_restoration));
imagesc(I_restoration);colormap('gray'); title('Deblurred Image')
%Pseudo Inverse restoration
% cam_pinv = real(ifft2((abs(G_filter_fft) > 0.1).*I_fft./G_filter_fft));
% imshow(fftshift(cam_pinv));
% xlabel('pseudo-inverse restoration')
A possible solution is deconvwr. I will first show its performance starting from an undistorted lena image. So, I know exactly the gaussian blurring function. Note that setting estimated_nsr to zero will destroy the performance completely due to quantisation noise.
I_ori = imread('lenaTest3.jpg'); % Download an original undistorted lena file
N = 19;
sigma = 5;
H = fspecial('gaussian',N,sigma)
estimated_nsr = 0.05;
I = imfilter(I_ori, H)
wnr3 = deconvwnr(I, H, estimated_nsr);
figure
subplot(1, 4, 1);
imshow(I_ori)
subplot(1, 4, 2);
imshow(I)
subplot(1, 4, 3);
imshow(wnr3)
title('Restoration of Blurred, Noisy Image Using Estimated NSR');
subplot(1, 4, 4);
imshow(H, []);
The best parameters I found for your problem by trial and error.
N = 19;
sigma = 2;
H = fspecial('gaussian',N,sigma)
estimated_nsr = 0.05;
EDIT: calculating exactly the used blurring filter
If you download an undistorted lena (I_original_fft), you can calculate the used blurring filter as follows:
G_filter_fft = I_fft./I_original_fft

Efficiently calculate optical flow parameters - MATLAB

I am implementing the partial derivative equations from the Horn & Schunck paper on optical flow. However, even for relative small images (320x568), it takes a frustratingly long time (~30-40 seconds) to complete. I assume this is due to the 320 x 568 = 181760 loop iterations, but I can't figure out a more efficient way to do this (short of a MEX file).
Is there some way to turn this into a more efficient MATLAB operation (a convolution perhaps)? I can figure out how to do this as a convolution for It but not Ix and Iy. I've also considered matrix shifting, but that only works for It as well, as far as I can figure out.
Has anyone else run into this issue and found a solution?
My code is below:
function [Ix, Iy, It] = getFlowParams(img1, img2)
% Make sure image dimensions match up
assert(size(img1, 1) == size(img2, 1) && size(img1, 2) == size(img2, 2), ...
'Images must be the same size');
assert(size(img1, 3) == 1, 'Images must be grayscale');
% Dimensions of original image
[rows, cols] = size(img1);
Ix = zeros(numel(img1), 1);
Iy = zeros(numel(img1), 1);
It = zeros(numel(img1), 1);
% Pad images to handle edge cases
img1 = padarray(img1, [1,1], 'post');
img2 = padarray(img2, [1,1], 'post');
% Concatenate i-th image with i-th + 1 image
imgs = cat(3, img1, img2);
% Calculate energy for each pixel
for i = 1 : rows
for j = 1 : cols
cube = imgs(i:i+1, j:j+1, :);
Ix(sub2ind([rows, cols], i, j)) = mean(mean(cube(:, 2, :) - cube(:, 1, :)));
Iy(sub2ind([rows, cols], i, j)) = mean(mean(cube(2, :, :) - cube(1, :, :)));
It(sub2ind([rows, cols], i, j)) = mean(mean(cube(:, :, 2) - cube(:, :, 1)));
end
end
2D convolution is the way to go here as also predicted in the question to replace those heavy mean/average calculations. Also, those iterative differentiations could be replaced by MATLAB's diff. Thus, incorporating all that, a vectorized implementation would be -
%// Pad images to handle edge cases
img1 = padarray(img1, [1,1], 'post');
img2 = padarray(img2, [1,1], 'post');
%// Store size parameters for later usage
[m,n] = size(img1);
%// Differentiation along dim-2 on input imgs for Ix calculations
df1 = diff(img1,[],2)
df2 = diff(img2,[],2)
%// 2D Convolution to simulate average calculations & reshape to col vector
Ixvals = (conv2(df1,ones(2,1),'same') + conv2(df2,ones(2,1),'same'))./4;
Ixout = reshape(Ixvals(1:m-1,:),[],1);
%// Differentiation along dim-1 on input imgs for Iy calculations
df1 = diff(img1,[],1)
df2 = diff(img2,[],1)
%// 2D Convolution to simulate average calculations & reshape to col vector
Iyvals = (conv2(df1,ones(1,2),'same') + conv2(df2,ones(1,2),'same'))./4
Iyout = reshape(Iyvals(:,1:n-1),[],1);
%// It just needs elementwise diffentiation between input imgs.
%// 2D convolution to simulate mean calculations & reshape to col vector
Itvals = conv2(img2-img1,ones(2,2),'same')./4
Itout = reshape(Itvals(1:m-1,1:n-1),[],1)
Benefits with such a vectorized implementation would be :
Memory efficiency : No more concatenation along the third dimension that would incur memory overhead. Again, performance wise it would be a benefit as we won't need to index into such heavy arrays.
The iterative differentiations inside the loopy codes are replaced by differentiation with diff, so this should be another improvement.
Those expensive average calculations are replaced by very fast convolution calculations and this should be the major improvement section.
A faster method, with improved results (in the cases I have observed) is the following:
function [Ix, Iy, It] = getFlowParams(imNew,imPrev)
gg = [0.2163, 0.5674, 0.2163];
f = imNew + imPrev;
Ix = f(:,[2:end end]) - f(:,[1 1:(end-1)]);
Ix = conv2(Ix,gg','same');
Iy = f([2:end end],:) - f([1 1:(end-1)],:);
Iy = conv2(Iy,gg ,'same');
It = 2*conv2(gg,gg,imNew - imPrev,'same');
This handles the boundary cases elegantly.
I made this as part of my optical flow toolbox, where you can easily view H&S, Lucas Kanade and more in realtime. In the toolbox, the function is called grad3D.m. You may also want to check out grad3Drec.m, in the same toolbox, which adds simple temporal blurring.

Inexplicable results after using ind2sub in Matlab

I am having some problems in matlab i don't understand. The following piece of code analyses a collection of images, and should return a coherent image (and always did).
But since I've put an if-condition in the second for-loop (for optimisation purposes) it returns an interlaced image.
I don't understand why, and am getting ready to throw my computer out the window. I suspect it has something to do with ind2sub, but as far as i can see everything is working just fine! Does anyone know why it's doing this?
function imageMedoid(imageList, resizeFolder, outputFolder, x, y)
% local variables
medoidImage = zeros([1, y*x, 3]);
alphaImage = zeros([y x]);
medoidContainer = zeros([y*x, length(imageList), 3]);
% loop through all images in the resizeFolder
for i=1:length(imageList)
% get filename and load image and alpha channel
fname = imageList(i).name;
[container, ~, alpha] = imread([resizeFolder fname]);
% convert alpha channel to zeros and ones, add to alphaImage
alphaImage = alphaImage + (double(alpha) / 255);
% add (r,g,b) values to medoidContainer and reshape to single line
medoidContainer(:, i, :) = reshape(im2double(container), [y*x 3]);
end
% loop through every pixel
for i=1:(y * x)
% convert i to coordinates for alphaImage
[xCoord, yCoord] = ind2sub([x y],i);
if alphaImage(yCoord, xCoord) == 0
% write default value to medoidImage if alpha is zero
medoidImage(1, i, 1:3) = 0;
else
% calculate distances between all values for current pixel
distances = pdist(squeeze(medoidContainer(i,:,1:3)));
% convert found distances to matrix of distances
distanceMatrix = squareform(distances);
% find index of image with the medoid value
[~, j] = min(mean(distanceMatrix,2));
% write found medoid value to medoidImage
medoidImage(1, i, 1:3) = medoidContainer(i, j, 1:3);
end
end
% replace values larger than one (in alpha channel) by one
alphaImage(alphaImage > 1) = 1;
% reshape image to original proportions
medoidImage = reshape(medoidImage, y, x, 3);
% save medoid image
imwrite(medoidImage, [outputFolder 'medoid_modified.png'], 'Alpha', alphaImage);
end
I didn't include the whole code, just this function (for brevity's sake), if anyone needs more (for a better understanding of it), please let me know and i'll include it.
When you call ind2sub, you give the size [x y], but the actual size of alphaImage is [y x] so you are not indexing the correct location with xCoord and yCoord.

Resources