比较两幅图片之间的距离或者相似性,我们常以Matlab pdist函数或者pdist2函数计算。计算大规模图片数据集的两两图片之间的距离,很自然想到双重for循环完成了。不过,这很慢:
function KMatrix = getL1( X, Y ) %L1距离
m = size(X,1); n = size(Y,1);
KMatrix = zeros(m,n);
for i = 1: m %按行
for j = 1: n
KMatrix(i,j) = sum( abs( X(i,:)-Y(j,:)) );
end
fprintf('%d.',i); if mod(i,10)==0, fprintf('\n'); end
end
其中,X、Y分别是图片集X、Y的特征文件矩阵。
以L1距离为例,特征文件是4096维的RGB彩色描述子,两万幅图片之间的L1距离矩阵20000×20000,在 8CPU + 16GB内存的X64 Windows 7系统上计算,MATLAB 2012b运行时间5小时。比较慢。
向量化是一个考虑。不过,童鞋们,如何向量化呢?这个比起点积要复杂一点点。通过一行一行计算距离,去掉一个内循环:
function KMatrix = getL1( X, Y ) %L1距离 向量化
m = size(X,1); n = size(Y,1);
nOnes = ones(1,n); KMatrix = zeros(m,n);
for i = 1: m %按行
xi = X(i,:); xi = xi( nOnes, : );
KMatrix(i,:) = sum( abs( xi-Y),2 );%计算一行L1距离
fprintf('%d.',i); if mod(i,10)==0, fprintf('\n'); end
end
似乎无法继续向量化了?还好,arrayfun帮助我们继续向量化。
function KMatrix = getL1( X, Y ) %L1距离 arrayfun向量化
m = size(X,1); n = size(Y,1);
KMatrix = zeros(m,n);
CC = arrayfun(@(i) getL1Vec(X(i,:), Y, i), 1:m, 'UniformOutput', false);%只能得到Cells
for i = 1: m %Cells->矩阵,极快
KMatrix(i,:) = CC{i};
end
end
function KVec = getL1Vec(XVec, Y, i)%第i行L1
n = size(Y,1);
nOnes = ones(1,n);
XVec = XVec( nOnes, : );
KVec = sum( abs( XVec-Y),2 );%计算一行L1距离
fprintf('%d.', i);if mod(i,10)==0, fprintf('\n'); end
end
至此,arrayfun完成了向量化。计算L1距离没有循环了。只是Cells转换为矩阵,有一个循环,极快。不是瓶颈。
向量化还能否改进下?
似乎内存优化不够,试试matlab函数:bsxfun(),改善:
n = size(Y,1);
nOnes = ones(1,n);
XVec = XVec( nOnes, : );
KVec = sum( abs( XVec-Y),2 );%计算一行L1距离
于是,得到:
function KMatrix = getL1( X, Y ) %L1距离
m = size(X,1); n = size(Y,1);
KMatrix = zeros(m,n);
CC = arrayfun(@(i) getL1Vec(X(i,:), Y, i), 1:m, 'UniformOutput', false);%只能得到Cells
for i = 1: m %Cells->矩阵,极快
KMatrix(i,:) = CC{i};
end
end
function KVec = getL1Vec(XVec, Y, i)%第i行L1
KVec = sum( abs(bsxfun(@minus,XVec,Y)),2 );
fprintf('%d.', i);if mod(i,10)==0, fprintf('\n'); end
end
完成了。在 8CPU + 16GB内存的X64 Windows 7系统上计算,MATLAB 2012b运行时间3小时。节省了2个小时。