【问题标题】:Fastest Performance Filtering an Image过滤图像的最快性能
【发布时间】:2015-10-03 22:05:47
【问题描述】:

背景: 我正在尝试在 java 中创建动画。 动画只是简单地拍摄一张图像,让它从最暗的像素到最亮的像素出现。

问题: 定义像素转换的内部算法不是我的问题。 我是 Java 和一般计算的新手。我做了一些研究,并且知道有很多 API 可以帮助图像过滤器/转换。 我的问题是性能,理解它。

对于实现,我创建了一个执行以下操作的方法:

  1. 接收 BufferedImage。
  2. 获取 BufferedImage 的 WritableRaster。
  3. 使用 setSample 和 getSample,逐个像素地处理和更改。
  4. 返回 BufferedImage。

之后,我使用 Timer 来调用该方法。 每次调用后,返回的 BufferedImage 通过 setIcon 附加到 JButton。

使用 500x500 的图像,我的机器处理每个调用大约需要 3 毫秒。 对于标准 1080p 图像,大约需要 30 毫秒,即每秒大约 33 帧。

我的目标是以 30fps 的速度处理/动画全高清图像...而且我将无法使用我所遵循的路径。大多数计算机中没有。

我做错了什么?我怎样才能让它更快?使用 getDataBuffer 或 getPixels 代替 getSample 可以改善吗?

提前致谢!对不起我的英语。


部分结论: 感谢这里的一些帮助。我已经改变了概念。我没有使用 getSample 和 setSample,而是将 BufferedImage 的像素 ARGB 信息存储到一个数组中。所以我处理了这个数组,并一次将它全部复制到另一个 BufferedImage 的 Raster 中。

处理时间从 30 毫秒(获取/设置样本)减少到 1 毫秒。 (测量不佳,但在同一台机器、环境和代码中)。

下面是我编写的一个小类来实现它。该类只能过滤低于亮度级别的像素,其他像素变为透明(alpha = 0)。

希望对以后寻找相同解决方案的人有所帮助。请注意,我的 Java 水平低于菜鸟水平,因此代码可能组织/优化不佳。

import java.awt.Graphics2D;
import java.awt.image.*;

/**
 * @author Psyny
 */
public class ImageAppearFX {
    //Essencial Data
    BufferedImage imgProcessed;
    int[] RAWoriginal;
    int[] RAWprocessed;
    WritableRaster rbgRasterProcessedW;

    //Information about the image
    int x,y;
    int[] mapBrightness;

    public ImageAppearFX(BufferedImage inputIMG) {
        //Store Dimensions
        x = inputIMG.getWidth();
        y = inputIMG.getHeight();

        //Convert the input image to INT_ARGB and store it. 
        this.imgProcessed = new BufferedImage(x, y, BufferedImage.TYPE_INT_ARGB); 
        Graphics2D canvas = this.imgProcessed.createGraphics();
        canvas.drawImage(inputIMG, 0, 0, x, y, null); 
        canvas.dispose();

        //Create an int Array of the pixels informations.
        //p.s.: Notice that the image was converted to INT_ARGB
        this.RAWoriginal = ((DataBufferInt) this.imgProcessed.getRaster().getDataBuffer()).getData();
        //Dupplication of original pixel array. So we can make changes based on original image
        this.RAWprocessed = this.RAWoriginal.clone();

        //Get Raster. We will need the raster to write pixels on
        rbgRasterProcessedW = imgProcessed.getRaster();

        //Effect Information: Store brightness information
        mapBrightness = new int[x*y];
        int r,g,b,a,greaterColor;
        // PRocess all pixels
        for(int i=0 ; i < this.RAWoriginal.length ; i++) {   
            a = (this.RAWoriginal[i] >> 24) & 0xFF;
            r = (this.RAWoriginal[i] >> 16) & 0xFF;
            g = (this.RAWoriginal[i] >>  8) & 0xFF;
            b = (this.RAWoriginal[i]      ) & 0xFF;

            //Search for Stronger Color
            greaterColor = r;
            if( b > r ) {
                    if( g > b ) greaterColor = g;
                    else greaterColor = b;        
            } else if ( g > r ) {
                greaterColor = g;
            }

            this.mapBrightness[i] = greaterColor;
        }
    }

    //Effect: Show only in a certain percent of brightness
    public BufferedImage BrightnessLimit(float percent) {
        // Adjust input values
        percent = percent / 100;

        // Pixel Variables
        int hardCap = (int)(255 * percent);
        int r,g,b,a,bright;

        // Process all pixels
        for(int i=0 ; i < this.RAWoriginal.length ; i++) {   
                //Get information of a pixel of the ORIGINAL image 
                a = (this.RAWoriginal[i] >> 24) & 0xFF;
                r = (this.RAWoriginal[i] >> 16) & 0xFF;
                g = (this.RAWoriginal[i] >>  8) & 0xFF;
                b = (this.RAWoriginal[i]      ) & 0xFF;

                //Brightness information of that same pixel
                bright = this.mapBrightness[i];

                //
                if( bright > hardCap  ) {     
                    a = 0;                 
                }
                this.RAWprocessed[i] = ((a << 24) + (r << 16) + (g << 8) + ( b )); //Write ARGB in byte format
        }

        //Copy the processed array into the raster of processed image
        rbgRasterProcessedW.setDataElements(0, 0, x, y, RAWprocessed);

        return imgProcessed;
    }

    //Return reference to the processed image
    public BufferedImage getImage() {
        return imgProcessed;
    }
}

【问题讨论】:

  • 你不能把 1-4 放在一个 while 循环中,而不是让所有的 Timer 开销吗?
  • 是的,我想我可以。但是,它不会提高每次传递的性能,这是我的瓶颈。我在想 BufferedImage 会在光栅通道中的每次更改后尝试在内部更新。也许我需要在将任何内容绘制到光栅之前完全组合新的图像数据?我不知道怎么...
  • 可能有一些选项。从直接获取像素作为数组(这很可能是最快的方式,但可能会降低 painting 图像的性能 - 并且需要您知道输入的 type图像),到高级方法,如简单的并行化。
  • 您的意思是先绘制最暗的像素,然后绘制第二个最暗的像素,等等?如果是这样,同样暗的像素会出现在单独的更新中还是在同一个更新中?
  • 我怀疑瓶颈源于每次要绘制时都必须搜索整个图像。一种快速检查方法是增加像素(即 、、、...),而不是搜索并查看性能是否发生变化。如果是这样,我可能会有答案;如果不是,我们可能需要查看一些代码...

标签: java


【解决方案1】:

虽然变化导致的时间差并不能证明重复搜索是瓶颈,但它确实暗示了它。

如果您愿意/能够用内存换取时间,我会首先按亮度对所有像素位置的列表进行排序。接下来,我将在动画期间使用排序列表来查找要复制的下一个像素。

一条额外的建议:使用 Java 的一种内置排序方法。自己制作是很有教育意义的,但学习如何排序似乎不是你的目标。此外,如果我对瓶颈的猜测是错误的,那么您将希望尽量减少寻求此答案的时间。

【讨论】:

  • 哼..按亮度映射和排序像素确实可以避免遍历所有像素的需要。我会尽快实现它。
猜你喜欢
  • 2018-07-25
  • 2010-12-07
  • 2011-08-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-05-23
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多