在二进制 {0, 255} 图像中找到中位数时消除分支

Eliminate branching when find median in a binary {0, 255} image

本文关键字:中位数 分支 图像 二进制      更新时间:2023-10-16

我有一个二进制图像,二进制值是 0 或 255。 图像数据的类型是无符号字符。在这里,我需要对此图像进行中值过滤。

我认为使用直方图找到中位数应该很快。使用一些代码来解释:

unsigned int hist[2] = {0, 0};
for (int i = 0; i < kernel_h; ++i) {
     for (int j = 0; j < kernel_w; ++j) {
          if (image(i,j) == 0) {
              hist[0]++;
          }
          else {
              hist[1]++;
          }
     }
}

然后,我们可以非常快速地得到中值。但是由于这种情况,代码仍然可以改进:

int counter = 0;
for (int i = 0; i < kernel_h; ++i) {
     for (int j = 0; j < kernel_w; ++j) {
          if (image(i,j) == 0) {
              counter++
          }
          else {
              counter--;
          }
     }
}

但我想知道是否有其他方法可以消除 if-else 分支,例如使用位操作将 {0, 255} 映射到某些内容,以便我们可以更新标志而不进行分支。

有人有什么建议吗?

255 的所有位都是 1,因此您可以将"if"简化为:

hist[image(i,j) & 1]++;
如果要

使用计数器,可以执行以下操作:

counter += (image(i,j) & 2)-1;

这种计算可以更快、更具体地O(n)像素数,并且与内核大小无关。

这个想法是首先进行"扫描转换",计算一个求和面积表,其中每个(x,y(像素的值被替换为从(0,0(到(x,y(的所有像素的总和。

鉴于此,您可以在固定时间内知道在任何矩形中使用

st(x0, y0) + st(x1, y1) - st(x0, y1) - st(x1, y0)

假设每个像素都是 0 或 1,总和会给你 1 的计数。

总计算时间为O(n)构建总和表,O(n)进行中值过滤,无论进行计数的区域有多大。

在中位数过滤的情况下,您可以根据总和预先计算结果,内部循环中的公式可以是:

result[x] = res[p0[x] + p1[x+box] - p0[x+box] - p1[x]];

此外,总和表不需要完全计算(因此每个像素需要一个整数(,但它可以在做结果时"懒惰"地计算,你只需要与内核高度一样多的表行仍然保持计算时间O(image_width*image_height)但只需要kernel_height*image_width内存。