Fast computation of a gradient of an image in matl

2019-02-10 00:03发布

问题:

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 ?

回答1:

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.

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 ?



回答2:

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.



回答3:

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



回答4:

If you have a recent version of Matlab (R2012b and up, IIRC) and the Image Processing Toolbox, then you can use the imgradient function.