Efficient computation of the sum of pairwise absolute differences
6 views (last 30 days)
Show older comments
Santiago Benito
on 14 Oct 2021
Commented: Santiago Benito
on 15 Oct 2021
Hi,
I am working on a project that requires the computation of the sum of all pairwise absolute differences between elements at either end of a randomly placed vector with coordinates . For instance, starting with the following 3x3 matrix:
A = magic(3)
A vector with coordinates will produce the absolute differences:
b = sum(abs([A(1,1)-A(1,2),A(1,2)-A(1,3),A(2,1)-A(2,2),A(2,2)-A(2,3),...
A(3,1)-A(3,2),A(3,2)-A(3,3)]))
Note that I took the differences between all "horizontal" matrix elements separated by a distance of .
The idea is to save this value in a matrix, let's say, C, in the position corresponding to the vector coordinate differences, i.e., with respect to the center of the matrix A. Thus, if we were to do the same with the vector defined by , we get:
C = zeros(size(A));
C(1,3) = sum(abs([A(2,1)-A(1,2),A(2,2)-A(1,3),A(3,1)-A(2,2),A(3,2)-A(2,3)]));
And adding in the previously calculated value:
C(2,3) = b
As a proof of concept, I borrowed and adapted a function from another answer that does the computation. I have called it diffMap and is at the bottom of this question/script.
dMap = diffMap(A)
As one can probably easily notice, this is just a modification of a rudimentary for-loopy cross-correlation.
I have been giving some thought to this problem in the hope of making this work with xcorr2 and discrete Fourier transforms. This is what I have got until now:
h = ones(size(A));
dMap_ = abs(xcorr2(A,h)-xcorr2(h,A))
It would be great if this worked, because it could be easily adapted to work with fft2, ifft2, and fftshift thanks to the convolution theorem. However, this does not return what i was hoping for: it computes the absolute value of the sum of the differences and NOT the sum of the absolute differences.
The question: is there any way to make this work efficiently, ideally with conv2 or xcorr2? With big matrices four for-loops takes forever, obviously.
Thanks!!
Here's the diffMap function:
%% Function
function dMap = diffMap(A)
%DIFFMAP Compute the absolute differences map
% Get the size of the matrix A
[r,c] = size(A);
% Initialize and index matrix
h = ones([r,c]);
% Padding
Rep = zeros(r + r*2-2, c + c*2-2);
Rep_ = zeros(r + r*2-2, c + c*2-2);
for x = r : r+r-1
for y = c : c+r-1
Rep(x,y) = A(x-r+1, y-c+1);
Rep_(x,y) = h(x-r+1, y-c+1);
end
end
dMap = zeros(r+r-1,c+c-1);
for x = 1 : r+r-1
for y = 1 : c+c-1
for i = 1 : r
for j = 1 : c
dMap(x, y) = dMap(x, y) + abs((Rep(x+i-1, y+j-1) * h(i, j)) -...
(Rep_(x+i-1, y+j-1) * A(i, j)));
end
end
end
end
end
0 Comments
Accepted Answer
Matt J
on 15 Oct 2021
Edited: Matt J
on 15 Oct 2021
I think parallelization might be the only way to accelerate things. Parfor seems to work well with the rewritten version of diffMap() below. With 12 cores, I can do a 250x250 matrix in about 2.7 sec., whereas the original diffMap code takes about 60 sec.
A = magic(250);
tic;
dMap = diffMap(A);
toc
%% Function
function dMap = diffMap(A)
%DIFFMAP Compute the absolute differences map
[r,c] = size(A);
[D1,D2]=deal(zeros(r,c));
parfor k=1:r*c
[i,j]=ind2sub([r,c],k);
if i==1 && j==1, continue; end
kernel=zeros(i,j);
kernel([1,end])=[-1,1];
D1(k)=sum(abs(conv2(A,kernel,'valid')),'all');
D2(k)=sum(abs(conv2(A,flipud(kernel),'valid')),'all');
end
dMap=[flipud(D2);D1];
dMap(end/2,:)=[];
dMap=[rot90(dMap,2),dMap];
dMap(:,end/2)=[];
end
More Answers (2)
Matt J
on 14 Oct 2021
Edited: Matt J
on 15 Oct 2021
The question: is there any way to make this work efficiently, ideally with conv2 or xcorr2?
If you were taking the sum of squared differences, it could be done with xcorr2 in just a few lines:
W=ones(size(A));
tmp=xcorr2(A.^2,W);
dMap=tmp+rot90(tmp,2)-2*xcorr2(A);
However, because you are taking the sum of absolute differences, there is no simple connection with xcorr2.
With big matrices four for-loops takes forever, obviously.
Is there an upper bound on the size of the matrix that you will need to process?
Matt J
on 15 Oct 2021
This GPU implementation may also be useful. I was able to process a 500x500 matrix in 20 seconds on the GTX 1080 Ti.
A = magic(500);
gd=gpuDevice;
tic;
dMap = diffMap(A);
wait(gd);
toc
%% Function
function dMap = diffMap(A)
%DIFFMAP Compute the absolute differences map
% Get the size of the matrix A
[r,c] = size(A);
A=gpuArray(A);
T=gpuArray(toeplitz(1:r,[1,zeros(1,r-1)]));
t=nonzeros(T);
W=reshape(logical(T),r,1,[]);
R=double(W);
% Initialize and index matrix
% Padding
dMap = gpuArray.zeros(r,2*c-1);
for j=1:c
col=A(:,j);
R(W)=col(t);
tmp=permute(sum( abs(R-A).*W,1) ,[3,2,1]);
%ctr=c
dMap(:,c-(j-1):2*c-j) = dMap(:,c-(j-1):2*c-j) +tmp;
end
dMap=[rot90(dMap,2);dMap];
dMap(end/2,:)=[];
end
See Also
Categories
Find more on Startup and Shutdown in Help Center and File Exchange
Community Treasure Hunt
Find the treasures in MATLAB Central and discover how the community can help you!
Start Hunting!