【问题标题】:Optimizing code that loops through pixels优化循环像素的代码
【发布时间】:2018-05-13 17:22:33
【问题描述】:

我得到了一个 200x200 的图像,作为像素的字节数组(每个像素 3 个字节,代表 RGB 值)。我想选择所有边界点,定义为一个非白色的点,并且位于图像的边界上或具有不同颜色的相邻像素。

为此编写了简单的 C 代码:

int i = 0, row = 0, column = 0, width3 = width*3;
char r,g,b;
while (i < length) {

    r = pixels[i], g = pixels[i+1], b = pixels[i+2];

    if (r != -1 || g != -1 || b != -1) { // Not white
        // Check for border point
        if (column == 0 || column == width-1 || row == 0 || row == height-1
            || r != pixels[i-3] || r != pixels[i+3] || r != pixels[i-width3] || r != pixels[i+width3]
            || g != pixels[i-2] || g != pixels[i+4] || g != pixels[i-width3+1] || g != pixels[i+width3+1]
            || b != pixels[i-1] || b != pixels[i+5] || b != pixels[i-width3+2] || b != pixels[i+width3+2]) {
            // Border point
        }
    }

    i += 3;

    if (++column == width) {
        column = 0;
        row++;
        // printf("new row");
    }

}

现在我想知道如何尽可能加快速度。 我可以使用 GPU,但从 GPU 到 GPU 的内存传输非常昂贵。

由于我对任何类型的优化技术(例如 openCV 中使用的技术)都是全新的,我想知道是否有任何方法可以让我的 sn-p 更快。

(更多上下文;我想将图像上每个非白色对象的边界点解释为可见对象的“轮廓”,然后使用 Douglas-Peucker 将轮廓近似为多边形)

【问题讨论】:

  • 注意pixels[i-3]pixels[i+5] 不要破坏数组边界。你没有显示i 是如何开始的,但你确实显示了i &lt; length
  • 他们不能,因为我第一次检查边界点。 irowcolumn 从 0 开始并遍历所有字节(一次 3 个字节,对于 RGB 值)。 width3width*3,在 200x200 图像的情况下为 600(我猜应该将所有这些添加到 sn-p 中)。
  • “我可以使用 GPU,但是从 GPU 到 GPU 的内存传输非常昂贵。”肯定不会像在 CPU 中执行整个操作那样昂贵! IIRC 复制到 VRAM 只比普通的 memcpy 慢一点。
  • 你确定吗? GPU 的问题是我想为每种颜色的单独队列添加边界点,但我不确定如何使用 GPU 来做到这一点(这对所有这些来说都是新事物)。
  • @0x5453 这是否非常适合 GPU 输出边界点列表?我假设输出本质上必须是串行的,就像一个可变大小的 x/y 对列表,否则 CPU 可能不得不做一些昂贵的事情来读回 GPU 输出的任何内容。我是 GPGPU 的新手,但考虑到输出的串行性质,这似乎很尴尬。

标签: c image-processing optimization


【解决方案1】:

一些微优化:

  • 重新组织行上的循环以仅访问整个图像内的像素对,这样您就无需测试列和行索引;

  • 不要对左右进行测试:如果两个像素不同,则对两者进行一次比较就足够了;

  • 仅在检测到边界点时测试白色(它们只是图像区域的一小部分);

您的 12 个比较测试(将减少到 6 个)可能很有效,因为它使用快捷逻辑(因此所有测试仅在统一区域执行)。您可以尝试将其换成无分支表达式,该表达式将始终完整执行,但避免代价高昂的条件分支:使用 r0 - r1 | g0 - g1 | b0 - b1,对于相同的颜色仅为零。

或者更好的是,一次加载整个像素作为整数值,计算适当的偏移量,对它们进行异或并屏蔽额外的字节:(*(unsigned int*)pixels ^ *(unsigned int*)(pixels + 3)) &gt;&gt; 8

如果这还不够,你可以考虑使用向量指令集(SSE/AVX),但这又是另外一回事了。

【讨论】:

  • 我测试白色是因为它通常占图像区域的很大一部分,但否则它会有意义。我不明白第一项;我测试列和行索引,因为图像边框上的每个非白色像素都是一个边界点。你认为我应该分开检查图像边框吗?顺便说一句,我肯定想了解更多关于 SSE/AVX 的信息,我浏览了 wiki,但不知道如何在我的情况下使用它。
  • @HaraKikiri:在 column=0 到 column=width-2 上循环,在行上的循环内。考虑一下我关于整数的建议,它一次处理三个组件。 SSE 是一个太大的话题,无法在这里讨论。
  • 整数加载似乎有很大帮助。非常感谢。如果您有任何关于 SSE 的链接要分享,我很乐意阅读。
  • 为什么它现在考虑最后一个非白色像素,以及任何最左边的边界点?由于某种原因,它似乎向右移动了 16 次。
  • @HaraKikiri:你得到的加速是多少?
猜你喜欢
  • 2011-03-12
  • 1970-01-01
  • 2011-10-12
  • 1970-01-01
  • 1970-01-01
  • 2016-10-09
  • 2012-02-06
  • 2014-03-05
  • 2018-10-15
相关资源
最近更新 更多