从 int 中获取字节以避免位移乐趣 - Java(中值过滤)
Posted
技术标签:
【中文标题】从 int 中获取字节以避免位移乐趣 - Java(中值过滤)【英文标题】:Get Bytes from an int to avoid bit shifting fun - Java (Median Filtering) 【发布时间】:2009-06-21 20:40:04 【问题描述】:我正在尝试在 Java 中对图像执行中值过滤,但速度非常慢。首先,如果你们中有人知道我可以使用的独立实现,如果您能告诉我,那就太好了。我正在 android 上实现,试图复制 JAI 的一小部分。
在我的方法中,我获取每个像素,使用
提取 R、G 和 B 值r = pixel >> 16 & 0xFF
或类似的,找到内核的中值并以
结束pixel = a | r <<16 | g << 8 | b
有什么方法可以更快地从 int 中获取字节?
亲切的问候,
加文
编辑:根据要求帮助诊断我的低性能的完整代码
对于实际的源文件,请转到here,在那里可以找到我的 medianFilter 实现。
width 和 height 变量用于 dest 的大小,可用作类成员变量。像素被线性化成一维数组。
private void medianFilterSquare(int[] source, int[] dest, int rWidth,
int rHeight, int radius)
// Source has been reflected into a border of size radius
// This makes it radius * 2 pixels wider and taller than the dest
int r,g,b;
int destOffset, rOffset, kOffset;
// The first offset into the source to calculate a median for
// This corresponds to the first pixel in dest
int rFirst = radius + (rWidth*radius);
// We use a square kernel with the radius passed
int neighbours = (radius+radius+1)*(radius+radius+1);
int index;
// Arrays to accumulate the values for median calculation
int[] rs = new int[neighbours];
int[] gs = new int[neighbours];
int[] bs = new int[neighbours];
// Declaring outside the loop helps speed? I'm sure this is done for me
// by the compiler
int pixel;
// Iterate over the destination pixels
for(int x = 0; x < height; x++)
for(int y = 0; y < width; y++)
// Offset into destination
destOffset = x + (y * width);
// Offset into source with border size radius
rOffset = destOffset + rFirst + (y * (radius *2));
index = 0;
// Iterate over kernel
for(int xk = -radius; xk < radius ; xk ++)
for(int yk = -radius; yk < radius ; yk ++)
kOffset = rOffset + (xk + (rWidth*yk));
pixel = source[kOffset];
// Color.red is equivalent to (pixel>>16) & 0xFF
rs[index] = Color.red(pixel);
gs[index] = Color.green(pixel);
bs[index] = Color.blue(pixel);
index++;
r = medianFilter(rs);
g = medianFilter(gs);
b = medianFilter(bs);
dest[destOffset] = Color.rgb(r, g, b);
【问题讨论】:
位移和逻辑运算应该比较快。你确定不是这两行之间的代码是瓶颈吗? 你如何“找到内核的中值”。我们可以看代码吗? 事实上,你甚至没有向我们展示你是如何得到 g、b 或 a 的。为什么不发布所有代码? 当然,我试图让您免于头痛,非常感谢您的帮助,我在试图让它在合理的时间内运行时有点困难!要遵循的代码 感谢您的代码。您真正的问题是您一遍又一遍地对相同的像素执行相同的操作。请参阅下面的答案。 【参考方案1】:正如其他人所说,这可能是导致问题的原因。我要说的一件事(这可能很明显,但无论如何) - 不要只在桌面 VM 上分析应用程序并假设瓶颈将在同一个地方。如果在 Dalvik 中发现完全不同的瓶颈,我一点也不感到惊讶。
您是否可以使用仍然发生变化的值?例如,如果您要只是为不同的颜色遮罩:
int r = pixel & 0xff0000;
int g = pixel & 0xff00;
int b = pixel & 0xff;
你能相应地调整你的处理算法吗?
最后一个想法:我总是发现移位运算符的优先级令人困惑。我强烈建议从可读性的角度来看,您将它们括起来:
r = (pixel >> 16) & 0xFF;
pixel = a | (r <<16) | (g << 8) | b;
与性能无关,但如果我是维护者,我当然会很感激 :)
【讨论】:
【参考方案2】:获取 r,g,b 值的最快方法应该是
new byte[]
(byte)(value >>> 24),
(byte)(value >>> 16),
(byte)(value >>> 8),
(byte)value
;
【讨论】:
这是否最快取决于各种因素,包括处理器是否具有桶形移位器。这是什么处理器? x86 或 ARM 还是其他?【参考方案3】:专注于如何进行位操作会分散注意力。只关心您如何执行这些操作,因为您需要一遍又一遍地处理同一个像素。
您为每个像素调用了 3 次中值滤波器,并且您在像素周围获得了多个像素每个像素。这意味着您要多次为同一个像素做所有这些工作。你有 for
循环嵌套四层!
如果您的半径为 5,则您正在处理 121 个像素。然后你向下移动 1 并再次处理 121 个像素,你已经转换了除了 11 个之外的所有像素!你对向下的每个像素做同样的事情,然后向右移动一个像素。对于 5 个半径,您需要进行两个数量级的 rgb 转换。
我建议保留您的图像或将您的图像转换为单独的红色、蓝色和绿色数组首先。
如果半径很大,您可以在移动时保持红色、蓝色和绿色的总和,从顶部减去像素,并在向下爬取位图时从底部添加像素,但这会使代码有点复杂。是否添加代码以进一步优化取决于您的要求。
此外,您还有很多可以优化的小东西。我不确定编译器是否正在处理它们。例如,你可以做一些强度降低。您不需要计算neighbors
、destOffset
、rOffset
或kOffset
的行中的任何乘法。如果你稍微重构一下代码,你只需要添加。
【讨论】:
我的名字很糟糕,medianFilter 应该只被称为中值,它找到整数数组的中值。 没关系。事实上,您一遍又一遍地对相同的像素进行相同的处理。尽管还有很多改进的空间,但将整个图像分成红色、绿色和蓝色部分是最大的问题。 我确定你是对的,但我需要找到图像中红色、绿色和蓝色的中值,以获得每个像素周围的像素内核。我看不出我怎么能少调用中位数,每个像素需要调用 3 次。除非我遗漏了一些我真诚希望的明显的东西。 你仍然需要为红色、绿色和蓝色调用它。但是您不需要为每个像素的每个邻居调用这些。想象一下,你在你家,你正在数最近的 5 个邻居家的屋顶瓦片。然后你走到隔壁邻居的房子,数他最近的 5 个邻居的所有屋顶瓦片。看看你重复了多少工作?所以首先将图像分成红色、绿色和蓝色的部分。然后对这些部分进行算法。您仍然需要对邻域进行平均,但您不需要对每个像素进行所有额外的处理。【参考方案4】:您偶尔可以在单个 int 中同时对红色和蓝色分量进行算术运算:
int average(int rgb1, int rgb2)
int rb = (((rgb1 & 0xFF00FF) + (rgb2 & 0xFF00FF)) >> 1) & 0xFF00FF;
int g = (((rgb1 & 0xFF00) + (rgb2 & 0xFF00)) >> 1) & 0xFF00;
return (rb | g);
因为红色和蓝色分量被 8 位分开,所以它们不会相互干扰。
不过,我从未见过显着的加速(超过 5-10%)。
【讨论】:
即使有 8 位分隔,如果他的半径超过 7,他也可能会遇到麻烦。半径为 8 时,他平均有 289 个邻居。以上是关于从 int 中获取字节以避免位移乐趣 - Java(中值过滤)的主要内容,如果未能解决你的问题,请参考以下文章