java - 过滤图像的最快性能

标签 java

上下文: 我正在尝试在 java 中创建动画。 动画只是拍摄一张图像,并使其从最暗的像素到最亮的像素出现。

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

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

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

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

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

我的目标是以 30fps 的速度处理/动画化 FullHD 图像......我将无法按照我所遵循的路径进行。大多数计算机中没有。

我哪里错了?我怎样才能让它更快?使用 getDataBuffer 或 getPixels 代替 getSample 可以改进吗?

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


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

处理时间从 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;
    }
}

最佳答案

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

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

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

关于java - 过滤图像的最快性能,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31420694/

相关文章:

java - Spring 安全 : ClassNotFoundException or IllegalArgumentException on deploy

java - 将 spring-cloud 与 netflix Eureka 一起使用时,如何让 Discovery Client 工作?

java - MassIndexing 时 Hibernate Search 中引用代理的 transient 方法

java - 将局部变量中的动态 int 值存储到静态?

java - 是否可以编写一个在有结果时提前退出的 Java 收集器?

java - Selenium 中的输入字段被标题隐藏

java - 如何在 TreeTable 的第二列中显示树线

java - 如何创建一个需要抛出或捕获特定异常的类。

java - 在 java 类中使用 string.xml 资源更改 textView

java - 拖动鼠标在JPanel上画线