从 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 实现。

widthheight 变量用于 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 转换。

我建议保留您的图像或将您的图像转换为单独的红色、蓝色和绿色数组首先


如果半径很大,您可以在移动时保持红色、蓝色和绿色的总和,从顶部减去像素,并在向下爬取位图时从底部添加像素,但这会使代码有点复杂。是否添加代码以进一步优化取决于您的要求。


此外,您还有很多可以优化的小东西。我不确定编译器是否正在处理它们。例如,你可以做一些强度降低。您不需要计算neighborsdestOffsetrOffsetkOffset 的行中的任何乘法。如果你稍微重构一下代码,你只需要添加。

【讨论】:

我的名字很糟糕,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(中值过滤)的主要内容,如果未能解决你的问题,请参考以下文章

golang位移运算

Java 将 4 个字节转换为 int

Java - 确定要从波形文件中读取以获取时间戳的字节数

java int怎么位移取前几位数字

使用 Core Motion 从加速度计数据中获取位移

用C++写的二进制文件,用JAVA怎么读取?