【问题标题】:Speed-efficient classification in MatlabMatlab 中的高效分类
【发布时间】:2015-01-15 15:24:51
【问题描述】:

我有一个大小为 RGB uint8(576,720,3) 的图像,我想将每个像素分类为一组颜色。我已经使用 rgb2lab 从 RGB 空间转换到 LAB 空间,然后删除了 L 层,所以它现在是由 AB 组成的 double(576,720,2)

现在,我想将其分类为我在另一张图像上训练过的一些颜色,并将它们各自的 AB 表示计算为:

Cluster 1: -17.7903  -13.1170
Cluster 2: -30.1957   40.3520
Cluster 3:  -4.4608   47.2543
Cluster 4:  46.3738   36.5225
Cluster 5:  43.3134  -17.6443
Cluster 6:  -0.9003    1.4042
Cluster 7:   7.3884   11.5584

现在,为了将每个像素分类/标记到集群 1-7,我目前执行以下操作(伪代码):

clusters;
for each x
  for each y
    ab = im(x,y,2:3);
    dist = norm(ab - clusters); // norm of dist between ab and each cluster
    [~, idx] = min(dist);
  end
end

但是,由于图像分辨率的原因,这非常慢(52 秒),而且我手动循环遍历每个 x 和 y。

是否有一些我可以使用的内置函数来执行相同的工作?必须有。

总结一下:我需要一种分类方法,将像素图像分类到一组已定义的集群中。

【问题讨论】:

  • 如果您确实编写了代码,是否对已接受的解决方案与您发布的伪代码的实际代码的运行情况进行了比较?
  • @Divakar 是的,这实际上很有趣。我的第一次尝试:52 秒。我的第一次尝试,但迁移到使用并行计算(4 个池):10 秒。方法#1:0.06 秒。相当惊人。
  • 对于方法 2,您也有机会尝试一下吗?很抱歉对这些数字很吵,但这些让我很兴奋:)
  • 呵呵,这很酷:) 我真的很喜欢矩阵编程的性能甚至比并行计算好那么多!为了留在内存中,矩阵的大小是否有任何限制?我还没有尝试过方法 #2,但我可以在今天晚些时候尝试一下。
  • 使用方法 #1,您很快就会达到内存带宽限制,但使用方法 #2,它应该更适合大数据大小。我很想知道方法 #2 的运行时比较,特别是对于大数据大小,如果您想测试,请告诉我!顺便说一下,我们称之为矢量化的矩阵编程的巨大加速魔法在bsxfun 中发挥了重要作用,这是最通用的矢量化工具!

标签: performance matlab machine-learning classification data-mining


【解决方案1】:

使用pdist2(统计工具箱)以矢量化方式计算距离:

ab = im(:,:,2:3);                              % // get A, B components
ab = reshape(ab, [size(im,1)*size(im,2) 2]);   % // reshape into 2-column
dist = pdist2(clusters, ab);                   % // compute distances
[~, idx] = min(dist);                          % // find minimizer for each pixel
idx = reshape(idx, size(im,1), size(im,2));    % // reshape result

如果没有Statistics Toolbox,可以将第三行替换为

dist = squeeze(sum(bsxfun(@minus, clusters, permute(ab, [3 2 1])).^2, 2));

这给出了平方距离而不是距离,但为了最小化它并不重要。

【讨论】:

    【解决方案2】:

    方法#1

    对于N x 2 大小的点/像素数组,您可以按照other solution by Luis 中的建议避免使用permute,这可能会使事情变慢一点,以拥有一种"permute-unrolled" 版本,让我们bsxfun 朝着2D 数组而不是3D 数组工作,这必须具有更好的性能。

    因此,假设集群被排序为N x 2 大小的数组,您可以尝试其他基于bsxfun 的方法 -

    %// Get a's and b's
    im_a = im(:,:,2);
    im_b = im(:,:,3);
    
    %// Get the minimum indices that correspond to the cluster IDs
    [~,idx]  = min(bsxfun(@minus,im_a(:),clusters(:,1).').^2 + ...
        bsxfun(@minus,im_b(:),clusters(:,2).').^2,[],2);
    idx = reshape(idx,size(im,1),[]);
    

    方法#2

    您可以尝试利用fast matrix multiplication in MATLAB 并基于this smart solution 的另一种方法-

    d = 2; %// dimension of the problem size
    
    im23 = reshape(im(:,:,2:3),[],2);
    
    numA = size(im23,1);
    numB = size(clusters,1);
    
    A_ext = zeros(numA,3*d);
    B_ext = zeros(numB,3*d);
    for id = 1:d
        A_ext(:,3*id-2:3*id) = [ones(numA,1), -2*im23(:,id), im23(:,id).^2 ];
        B_ext(:,3*id-2:3*id) = [clusters(:,id).^2 ,  clusters(:,id), ones(numB,1)];
    end
    [~, idx] = min(A_ext * B_ext',[],2); %//'
    idx = reshape(idx, size(im,1),[]); %// Desired IDs
    

    基于矩阵乘法的距离矩阵计算是怎么回事?

    让我们考虑两个矩阵AB,我们要计算它们之间的距离矩阵。为了接下来的解释更容易,让我们将A 视为3 x 2B 视为4 x 2 大小的数组,从而表明我们正在使用X-Y 点。如果我们有 A 作为 N x 3B 作为 M x 3 大小的数组,那么这些将是 X-Y-Z 点。

    现在,如果我们必须手动计算距离矩阵的平方的第一个元素,它看起来像这样 –

    first_element = ( A(1,1) – B(1,1) )^2 + ( A(1,2) – B(1,2) )^2         
    

    应该是——

    first_element = A(1,1)^2 + B(1,1)^2 -2*A(1,1)* B(1,1)   +  ...
                    A(1,2)^2 + B(1,2)^2 -2*A(1,2)* B(1,2)    … Equation  (1)
    

    现在,根据我们提出的矩阵乘法,如果在前面代码中的循环结束后检查A_extB_ext 的输出,它们将如下所示——

    因此,如果您在A_extB_ext 的转置之间执行矩阵乘法,则乘积的第一个元素将是A_extB_ext 的第一行之间的元素乘法之和,即这些——

    结果将与之前从Equation (1) 获得的结果相同。对于A 的所有元素,与A 位于同一列中的B 的所有元素,这将继续。因此,我们最终会得到完整的平方距离矩阵。就是这样!!

    矢量化变体

    基于矩阵乘法的距离矩阵计算的矢量化变体是可能的,尽管它们并没有看到任何显着的性能改进。下面列出了两个这样的变体。

    变体 #1

    [nA,dim] = size(A);
    nB = size(B,1);
    
    A_ext = ones(nA,dim*3);
    A_ext(:,2:3:end) = -2*A;
    A_ext(:,3:3:end) = A.^2;
    
    B_ext = ones(nB,dim*3);
    B_ext(:,1:3:end) = B.^2;
    B_ext(:,2:3:end) = B;
    
    distmat = A_ext * B_ext.';
    

    变体 #2

    [nA,dim] = size(A);
    nB = size(B,1);
    
    A_ext = [ones(nA*dim,1) -2*A(:) A(:).^2];
    B_ext = [B(:).^2 B(:) ones(nB*dim,1)];
    
    A_ext = reshape(permute(reshape(A_ext,nA,dim,[]),[1 3 2]),nA,[]);
    B_ext = reshape(permute(reshape(B_ext,nB,dim,[]),[1 3 2]),nB,[]);
    
    distmat = A_ext * B_ext.';
    

    因此,这些也可以视为实验版本。

    【讨论】:

    • 抱歉,我的线性代数有点生疏了。我希望您能详细说明智能解决方案,因为您一直在发布它,而我并没有完全理解。尤其是HelpAHelpBhelpA * helpB'。你为什么要ones(numA,1)?为什么在-2*im23(:,id) 中使用-2?为什么要按顺序创建helpAHelpB 的值? helpA * helpB'的目的是什么?
    • @kkuilla 看看编辑中的解释是否有意义?
    • @Divakar 对bsxfun 有很好的改进,而且答案非常彻底! +1
    • 哦,是的。极好的。谢谢你。 +50 :-)。我没有看到你使用我不记得名字的规则重写了(A(1,1)-B(1,1))^2 +((A(1,2)-B(1,2)^2)。很好的解释。
    • @kkuilla 有趣的是我忘记了它的名字,小学早已不复存在,但我认为它可能被命名为减法平方的扩展:)
    猜你喜欢
    • 2015-07-03
    • 2012-04-18
    • 1970-01-01
    • 2013-08-19
    • 1970-01-01
    • 2016-02-15
    • 1970-01-01
    • 2012-06-17
    • 1970-01-01
    相关资源
    最近更新 更多