过滤1分贝图像
我正在寻找使用3x3滤波器过滤每像素1位的图像:对于每个输入像素,如果其周围像素的加权和(由过滤器确定的权重)超过某个阈值,则对应的输出像素将设置为1 。
我希望这会比转换成8 bpp更高效,然后过滤掉,但我想不出一个好办法。 一个简单的方法是跟踪九个字节指针(三个连续的行,并且还指向每行当前字节的任一侧,用于计算这些字节中第一个和最后一个位的输出),并为每个输入像素计算
sum = filter[0] * (lastRowPtr & aMask > 0) + filter[1] * (lastRowPtr & bMask > 0) + ... + filter[8] * (nextRowPtr & hMask > 0)
,
对于字节边缘的位来说额外的faff。 但是,这很慢,看起来真的很难看。 你没有获得任何并行性,因为你在每个字节中有八个像素,而不得不做大量额外的工作来掩盖事情。
有没有什么好的资源来说明如何做到这一点? 这个特定问题的解决方案将会非常棒,但我很乐意指出在C / C ++中使用1bpp图像进行高效图像处理的任何示例。 我想在未来使用1 bpp算法替换更多8 bpp的东西,以避免图像转换和复制,所以对此的任何常规资源将不胜感激。
几年前,我发现将位拆分为字节,做过滤器,然后将字节打包回位比直接使用位要快。 这似乎与直觉相反,因为它是3个循环而不是1个,但每个循环的简单性超过了它。
我不能保证它仍然是最快的; 编译器和特别是处理器很容易发生变化。 不过,简化每个循环不仅使优化更容易,而且更易于阅读。 这是值得的。
解开一个单独的缓冲区的另一个好处是它可以让你灵活地处理你在边缘做的事情。 通过使缓冲区大于输入的2个字节,可以从字节1开始解压缩,然后将字节0和n
设置为任何你喜欢的,并且滤波循环根本不必担心边界条件。
看看可分离的过滤器。 除此之外,它们在它们工作的情况下允许大规模并行。
例如,在您的3x3样本权重和过滤器案例中:
在数学上,这种方法的优点是它将样本操作的次数从n^2
到2n
,尽管它需要一个与源相同大小的缓冲区(如果您已经执行了一个副本,可以用作缓冲区;您不能修改步骤2)的原始源。 为了保持2n
内存使用,你可以一起执行第2步和第3步(这有点棘手,并不完全令人愉快); 如果内存不是问题,则可以在两个缓冲区(source,hblur,vblur)上花费3n
。
由于每个操作都与不可变源完全隔离,因此如果有足够的内核,则可以同时在每个像素上执行过滤器。 或者,在更现实的情况下,您可以利用分页和缓存来加载和处理单个列或行。 这在处理奇数步时很方便,在一行的尾部填充等等。第二轮样本(垂直)可能与你的缓存有关,但在最坏的情况下,一轮会缓存,并且你已经从处理指数切换到线性处理。
现在,我还没有涉及具体存储数据的情况。 这确实使事情稍微复杂一点,但并不是如此。 假设你可以使用滚动窗口,例如:
d = s[x-1] + s[x] + s[x+1]
作品。 有趣的是,如果在步骤1的输出期间将图像旋转90度(y,x)
在读取时平凡,来自(y,x)
样本),则可以为任何样本加载至多两个水平相邻字节,并且只有单字节大约有75%的时间。 这在读取过程中对缓存起到一点友善的作用,但是大大简化了算法(足以使其恢复丢失)。
伪代码:
buffer source, dest, vbuf, hbuf;
for_each (y, x) // Loop over each row, then each column. Generally works better wrt paging
{
hbuf(x, y) = (source(y, x-1) + source(y, x) + source(y, x+1)) / 3 // swap x and y to spin 90 degrees
}
for_each (y, x)
{
vbuf(x, 1-y) = (hbuf(y, x-1) + hbuf(y, x) + hbuf(y, x+1)) / 3 // 1-y to reverse the 90 degree spin
}
for_each (y, x)
{
dest(x, y) = threshold(hbuf(x, y))
}
访问字节内的位( source(x, y)
表示访问/样本)相对来说比较简单,但在这里写出来很痛苦,所以留给读者。 特别是以这种方式实现(90度旋转)的原理只需要每次传送n
采样2次,并且总是从紧邻的位/字节采样(从不要求计算下一行中位的位置) 。 总而言之,它比任何其他方法都快得多且简单。
您可以简单地扩展当前窗口 - 读取第一行的第一个字节,移位和屏蔽,然后读出您的三位数字,而不是将整个图像扩展为1位/字节(或8bpp,本质上,如您所注意的)需要; 对另外两行做相同的操作。 然后,对于下一个窗口,您只需丢弃左列并从每行中取多一位。 这样做的逻辑和代码并不像简单地扩展整个图像那么简单,但它会占用更少的内存。
作为一个中间立场,你可以扩展你目前正在处理的三行。 这样编码可能更容易。
链接地址: http://www.djcxy.com/p/25389.html