【问题标题】:Vectorize a triangle scan line in MATLAB/Octave在 MATLAB/Octave 中矢量化三角形扫描线
【发布时间】:2023-04-06 15:28:01
【问题描述】:

我是 Octave/MATLAB 的新手。我想为一个三角形矢量化一个简单的扫描线填充。这是我要消除的 while 循环:

 #Iterate over all the scan lines from bottom to top
 while(yCurr <= 200)

    #VARIABLES: 
    #  img - Zero matrix used as an image. Zero is black. Assume 320x200 pixels.
    #  leftIdx - Vector that contains the left-most pixel to fill in each
    #      scanline. Dim is 1x200. Each element represents x-coord of a pixel.
    #  rightIdx - Vector that contains the right-most pixel to fill in each
    #      scanline. Dim is also 1x200. Each element represents x-coord of a pixel.
    #  yCurr - The current row being scanned.    

    #Fill all the pixels in one scan line 
    img(leftIdx(yCurr) : rightIdx(yCurr), yCurr) = color;

    #Increment the row
    yCurr++;

 endwhile

【问题讨论】:

    标签: matlab graphics octave vectorization rasterizing


    【解决方案1】:

    简单的解决方案 - 您可以将循环直接更改为一个 arrayfun 调用。 arrayfun 是一种编写循环的奇特方式——它为所有提供的参数调用用户定义的函数。需要注意的是,通常与 arrayfun 一起使用的匿名函数不能分配变量。但是,您可以编写一个普通函数,将颜色分配给 img 并将其作为参数传递给 arrayfun:

     function doAllWork(img, rightIdx, leftIdx)
    
     arrayfun(@fill, 1:size(img, 1));
    
        function fill(i)
          img(leftIdx(i):rightIdx(i), i) = color;
        end
     end
    

    我已经在 doAllWork 中将 fill 函数定义为一个局部函数,这样它就可以访问 img、leftIdx、rightIdx。

    复杂的解决方案 通常在这种情况下,要一次性完成分配,您希望使用 sub2ind 来获得矩阵的线性索引(简而言之,在 C 中您会写 sth.like j *nx+i)。然后你写 img(ind) 而不是 img(indrow, indcol)。问题是每一行在不同的地方都有不同数量的非零。

    (复杂的)想法是创建一个显式的列索引数组 Ii{row} = [leftIdx(row):rightIdx(row)] 和相应的行索引数组 Ij{row} = [row*ones(1, lenght (Ii{row}))] 用于所有行。没有可以使用 arrayfun 完成的循环。一旦你有了这个,你可以在对应的 Ii/Ij 条目上使用 sub2ind 构造一个线性索引到你的 img 中,也称为使用 arrayfun。代码是这样的

    nrows=1:size(img, 1);
    Ii=arrayfun(@(i)(leftIdx(i):rightIdx(i)), nrows, 'UniformOutput', false);
    Ij=arrayfun(@(i)(i*ones(1,length(Ii{i}))), nrows, 'UniformOutput', false);
    lin=arrayfun(@(i)(sub2ind(size(A), Ij{i}, Ii{i})), nrows, 'UniformOutput', false);
    img([lin{:}])=color;
    

    这种方法在您的情况下没有多大用处 - 它太复杂了。但它对 arrayfun 可以做什么很有指导意义,而且 sub2ind 的技巧通常非常有用。

    【讨论】:

    • 感谢您的回答。我对简单的解决方案很感兴趣,并考虑过使用 arrayfun。我承认我还不了解复杂的解决方案,但我会研究 sub2ind,因为它经常出现在“提示和技巧”部分。该解决方案允许我避免循环,但我有一个并行化代码的隐藏议程。我想知道 Octave 是否足够聪明,可以将 arrayfun 工作分配到多个内核上?答案投票赞成。
    【解决方案2】:

    虽然不是 OP 的主要问题(即如何矢量化光栅扫描),但 MATLAB 中用于填充三角形值的简洁解决方案可以来自使用 poly2mask 指定区域在三角形内部,使用三角形顶点,并使用逻辑索引来分配值。

    % Example values
    yCurr = 1:200;
    img = zeros(320, 200);
    leftIdx = ones(1,200);
    rightIdx = 1:200;
    color = 1;
    
    % Define polygon vertices and get mask
    x = [leftIdx(1) leftIdx(200) rightIdx(1) rightIdx(200)];
    y = [1 1 200 200];
    bw = poly2mask(x,y,320,200);
    % Assign color values
    img(bw) = color; 
    

    由于poly2mask 很可能使用了一些底层for,,它不是光栅化扫描的矢量化版本。但是,它提供了处理更复杂的多边形区域的好处。

    【讨论】:

    • 很棒的电话。我不知道 poly2mask。我正在选择与矢量化相关的答案,但我决定转储我自己的扫描线函数并使用 poly2mask。我赞成你的回答。谢谢。
    【解决方案3】:

    我认为您最初的基于循环的解决方案是最好的解决方案。 为什么?

    1. 因为在您没有完成分析之前,您无法知道矢量化解决方案是否真的更快。
    2. 因为您使用 for 循环的代码对读者来说一目了然!它只包含 4 行代码,一看就知道它的用途。

    我唯一要改变的是while->for

    for yCurr = 1:200
        img(leftIdx(yCurr) : rightIdx(yCurr), yCurr) = color;
     end
    

    【讨论】:

    • 说得好。我不知道循环是否是性能瓶颈。性能不是我首先关心的问题。我想学习 MATLAB 编程范式,我感觉循环通常可以变成矩阵运算。但不总是。 :-)。我赞成这个答案,因为它开明的软件工程实践。
    猜你喜欢
    • 1970-01-01
    • 2011-07-08
    • 1970-01-01
    • 1970-01-01
    • 2012-05-19
    • 1970-01-01
    • 1970-01-01
    • 2016-07-17
    • 1970-01-01
    相关资源
    最近更新 更多