Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Fast computation of a gradient of an image in matlab

I was trying to optimize my code and found that one of my code is a bottleneck. My code was :

function [] = one(x)
I = imread('coins.png');
I = double(I);
I = imresize(I,[x x]);
sig=.8;    % scale parameter in Gaussian kernel
G=fspecial('gaussian',15,sig); % Caussian kernel
Img_smooth=conv2(I,G,'same');  % smooth image by Gaussiin convolution
[Ix,Iy]=gradient(Img_smooth);
f=Ix.^2+Iy.^2;
g=1./(1+f);  % edge indicator function.
end

I tried to run it like this : clear all;close all;

x=4000;N=1;
tic
for i=1:N
    one(x);
end
toc

I found that the largest amount of time was consumed by the gradient operator statement(about 60%) of the total time. So it got me thinking how can I optimize my code further ....

I consulted few sites like : Dgradient and http://regularize.wordpress.com/2013/06/19/how-fast-can-you-calculate-the-gradient-of-an-image-in-matlab/

However Dgradient is a MEX file and I do not want to use it. I wanted to write my own gradient function. I read in the blogs that the gradient operator in matlab is significantly slower and be speeded up by using shift & substract and sparse matrix.

I do not have knowledge about sparse matrix. However I did try doing it by using shift and substract method. However I am preety sure my code is wrong. Please could anyone clarify what kind of difference matlab uses for calculation of its gradient ? And show me how to do it in my code ?

clc;clear all;close all;
I = imread('coins.png');
I = double(I(:,:,1));
I = imresize(I,[4 4]);

tic
[dx dy] = gradient(I);
toc

tic
%//Doing forward difference on both directions
dx1 = [I(:,2:end) I(:,end)] - I;
dy1 = [I(2:end,:);I(end,:)] - I;
toc

Please could you guys look into the code and suggest me how to correctly apply it ? Or guide me how to do so using sparse matrix ?

like image 735
roni Avatar asked Sep 23 '13 11:09

roni


3 Answers

Thanks to all of your answers and helpful suggestions . I took the suggestions of pseudoDust, Hugues , Dima and High Performance Mark and wrote my own code. My code is given below :

clc;clear all;close all;
x=32;
I = imread('coins.png');
I = imresize(I,[x x]);
I = double(I(:,:,1));

tic
[dx dy] = gradient(I);
toc

tic
[m,n]=size(I);
A = [I(:,2:end) zeros(m,1)];
B = [zeros(m,1) I(:,1:end-1)];
dx1 = [I(:,2)-I(:,1) (A(:,2:end-1)-B(:,2:end-1))./2 I(:,end)-I(:,end-1)];
A = [I(2:end,:) ; zeros(1,n)];
B = [zeros(1,n) ; I(1:end-1,:)];
dy1 = [I(2,:)-I(1,:) ; (A(2:end-1,:)-B(2:end-1,:))./2 ; I(end,:)-I(end-1,:)];
toc

nnz(dx-dx1)
nnz(dy-dy1)

My basic idea was that : Gradient averages the 2 adjacent positions (left and right or top and bottom), except for the edges where it takes the difference between the value and the adjacent position. I then checked the matrix generated by me (dx1,dy1) with the matrix generated by matlab gradient function (dx,dy).

Elapsed time is 0.010232 seconds.
Elapsed time is 0.000066 seconds.
ans =
     0
ans =
     0

So I believe my code is correct. Also the timing results was surprising to say the least. I then timed my code with the matlab one for different sizes of the images.

I got this results :


%x=16
Elapsed time is 0.010790 seconds.
Elapsed time is 0.000057 seconds.
%x=32
Elapsed time is 0.010564 seconds.
Elapsed time is 0.000069 seconds.
%x=64
Elapsed time is 0.010627 seconds.
Elapsed time is 0.000152 seconds.
%x=128
Elapsed time is 0.011346 seconds.
Elapsed time is 0.000669 seconds.
%x=256
Elapsed time is 0.017311 seconds.
Elapsed time is 0.004468 seconds.
%x=512
Elapsed time is 0.044148 seconds.
Elapsed time is 0.030435 seconds.
%x=1024
Elapsed time is 0.093386 seconds.
Elapsed time is 0.093029 seconds.
%x=2048
Elapsed time is 0.345423 seconds.
Elapsed time is 0.387762 seconds.

So my conclusion was this : For image size upto 1024X1024 my code was faster than the gradient command inbuilt in matlab.

Edit : I updated my answer and added this graph:

It clearly shows that for smaller array size my code is significantly faster than the matlab gradient function.

enter image description here

Is my code correct ? Guys please look through it and check. Do give your feedback. I am literally a novice in matlab and am highly surprised by this result. Please check whether what I am doing is correct or not ?

like image 151
roni Avatar answered Nov 14 '22 09:11

roni


dx1 = (I(:,[1:end end]) - I(:,[1 1:end]));
dx1(:,2:(end-1))=dx1(:,2:(end-1))*0.5;
dy1 = (I([1:end end],:) - I([1 1:end],:));
dy1(2:(end-1),:)=dy1(2:(end-1),:)*0.5;

Should work, gradient averages the 2 adjacent positions (left and right or top and bottom), except for the edges where it takes the difference between the value and the adjacent position.

like image 21
pseudoDust Avatar answered Nov 14 '22 08:11

pseudoDust


It is nice that you want to write your own gradient function, and as mentioned in the blogs, some methods are better than others. However the blog entry compares nested for-loops to shift and subtract and sparse matrix; nowhere does it say that gradient is slow or un-optimized. Usualy Matlab functions are implemented in C++ and make use of BLAS and LAPACK libraries. They should beat any of the techniques that you suggest, but go ahead and please check that out for us :-)

Regarding your code, I doubt that you want to resize your image to [4 4]! Otherwise your code is correct.

You have implicitly used a Robert operator for your gradient ( dI/dx = I(x+1) - I(x) )

The gradient function uses central differences, [- 1 0 1] in the x-direction.

The numerical results for each operators will be a bit different.

Note that in Matlab R2013a, the imgradient function uses a Sobel operator, [1 0 -1; 2 0 -2;1 0 -1] in the x-direction.

Consider taking a look at imgradient

like image 1
Hugues Fontenelle Avatar answered Nov 14 '22 07:11

Hugues Fontenelle