上下文: 我正在尝试在 java 中创建动画。 动画只是拍摄一张图像,并使其从最暗的像素到最亮的像素出现。
问题: 定义像素转换的内部算法不是我的问题。 我是 Java 和一般计算的新手。我做了一些研究,知道有很多 API 可以帮助进行图像过滤/转换。 我的问题是性能,理解它。
为了实现,我创建了一个执行以下操作的方法:
- 接收 BufferedImage。
- 获取 BufferedImage 的 WritableRaster。
- 使用 setSample 和 getSample,逐个像素地处理和更改。
- 返回 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/