如何更快地处理 BufferedImage

huangapple 未分类评论45阅读模式
英文:

How can I process BufferedImage faster

问题

我正在制作一个基本的图像编辑器,以提高我的图像处理技能。我目前有12个滤镜。
所有的滤镜都有一个可点击的带有图像的JLabel
当所有滤镜都应用时,我使用以下函数更新所有滤镜的图像:

public static void buttonImagesUpdater(){
    for(int i = 0; i < effects.size(); i++){
        effects.get(i).getButton().setImage(new ImageIcon(effects.get(i).process(image)));
    }
}

所有的滤镜都有类似这样的process函数:

public BufferedImage process(BufferedImage base) {
    BufferedImage product = new BufferedImage(base.getWidth(), base.getHeight(), base.getType());
    for(int indisY = 0; indisY < base.getHeight(); indisY++){
        for(int indisX = 0; indisX < base.getWidth(); indisX++){
            Color currentColor = new Color(base.getRGB(indisX, indisY));
            int greyTone = 0;
            greyTone = (int) (currentColor.getRed()*0.315) +
                    (int) (currentColor.getGreen()*0.215) 
                    + (int) (currentColor.getBlue()*0.111);
            product.setRGB(indisX, indisY, new Color(greyTone,greyTone,greyTone).getRGB());
                
        }
    }
    return product;
}

程序运行得非常慢。当我点击一个效果的按钮时,在使用5000x3000像素的图像时,它会在45秒后完成。我该如何解决这个性能问题?

英文:

I'm making a basic image editor to improve my image process skills. I have 12 filters(for now).
All filters have a clickable JLabel that has image
I update images of all of them when all filters apply with this function:

public static void buttonImagesUpdater(){
    for(int i = 0; i &lt; effects.size(); i++){
        effects.get(i).getButton().setImage(new ImageIcon(effects.get(i).process(image)));
    }
}

All filters have a process function like that:

public BufferedImage process(BufferedImage base) {
    BufferedImage product = new BufferedImage(base.getWidth(), base.getHeight(), base.getType());
    for(int indisY = 0; indisY &lt; base.getHeight(); indisY++){
        for(int indisX = 0; indisX &lt; base.getWidth(); indisX++){
            Color currentColor = new Color(base.getRGB(indisX, indisY));
            int greyTone = 0;
            greyTone = (int) (currentColor.getRed()*0.315) +
                    (int) (currentColor.getGreen()*0.215) 
                    + (int) (currentColor.getBlue()*0.111);
            product.setRGB(indisX, indisY, new Color(greyTone,greyTone,greyTone).getRGB());
            
        }
    }
    return product;
}

Program works so slowly. When I click an effect's button it done 45 second later when I use 5000x3000 image. How can I fix this performance problem?

答案1

得分: 0

你必须记住 3000 * 5000 等于 15,000,000,所以你正在创建 15,000,000 个Color对象,你调用setRGB 15,000,000次。如果我是你,我会考虑是否可以使用ForkJoinPool来处理这个。

英文:

You have got to remember that 3000 * 5000 is 15,000,000 so you're creating 15,000,000 Color objects, you're calling setRGB 15,000,000 times. If I were you, I would look into potentially using ForkJoinPool for this.

答案2

得分: 0

我同意 @Jason - 问题在于你正在创建(和销毁)1500万个 Color 对象。

然而,我认为仅仅使用多线程不太可能获得足够的性能提升,因为你仍然会对内存和垃圾回收器施加很大压力,因为你仍然会创建并销毁1500万个对象,只不过是并行地执行而已。

我认为你既可以完全避免创建 Color 对象,又可以减少循环次数,通过直接使用 BufferedImage 类的 getRGB() 方法的结果,而不是创建一个 Color 对象。此外,你可以使用返回整数数组的 getRGB() 重载方法,一次获取一行(或更多)像素,以减少循环内部必须进行的调用次数。类似地,你可以使用接受像素值数组的 setRGB() 版本。

关键在于能够在不分离 R、G 和 B 值的情况下将整数颜色值转换为灰度值(或任何其他你需要做的操作),或者找到一种比创建、使用和销毁 Color 对象更有效的方式来分离 R、G 和 B 值。

关于从 getRGB() 返回的整数中获取 R、G 和 B 值的线索,可以注意一下 Color.getRGB() 的文档中的说明,

> “返回表示默认 sRGB ColorModel 中颜色的 RGB 值。(位 24-31 是 alpha 通道,位 16-23 是红色,位 8-15 是绿色,位 0-7 是蓝色)。”

在你搞清楚这一点之后,你可以考虑进行并行化处理。

英文:

I agree with @Jason - the problem is that you're creating (and destroying) 15 million Color objects.

However, I don't think that just using multiple threads is going to get you enough of a performance increase, because you are still going to be putting a lot of pressure on memory and the garbage collector, since you'll still be creating and destroying 15 million objects, you'll just be doing several in parallel.

I think that you can both stay away from creating Color objects entirely, and make fewer loops, by using the result of the BufferedImage class' getRGB() method directly, instead of creating a Color object. Further, you can use the overload of getRGB() that returns an array of ints, to get, say, a row of pixels (or more) at a time, to reduce the number of calls that you have to make inside the loop. You can similarly use the version of setRGB() that takes an array of pixel values.

The trick is to be able to convert the int color value to a gray value (or whatever else you need to do) without separating the R, G, and B values, or finding an efficient way to separate R, G, and B - more efficient than creating, using, and destroying a Color object.

For a lead on getting R, G, and B values from the int returned by getRGB(), note that the documentation for Color.getRGB() says,

> "Returns the RGB value representing the color in the default sRGB
> ColorModel. (Bits 24-31 are alpha, 16-23 are red, 8-15 are green, 0-7
> are blue)."

Once you have that working, you can think about parallelizing it.

答案3

得分: 0

你可以尝试这个方法,看看是否能稍微加快速度。

- 这里使用了来自图像光栅的 DataBuffer。
- 并使用映射来保留先前转换的颜色。这可能会随着时间的推移而有所帮助,具体取决于图像的类型。
- 并且使用了双精度,因为数据缓冲区支持各种类型。

我还将您的值乘以 2 的幂,以便将它们移动到正确的位置。Color 的 get* 方法返回介于 0 到 255(包括)之间的值。RGB 在 int 上占用了较低的 24 位(Alpha 在最左边的字节)。

我只看到了一张暗图,但我使用其他参数进行了测试,我知道它有效。帐篷中的主要问题似乎是读取和写入图像。我正在使用 `6637 3787` 图像,可以在 12 秒内读取、修改和写入它。对于更高级的处理,您可能想要查看 `AffineTransformOp`。

```java
	static Map<Color, Double> colorMap = new HashMap<>();
	
	public BufferedImage process(BufferedImage base) {
		DataBuffer db = base.getRaster().getDataBuffer();
		for (int i = 0; i < db.getSize(); i++) {
			Color currentColor = new Color(db.getElem(i));
			double greyTone = colorMap.computeIfAbsent(currentColor, v ->
		    currentColor.getRed() * .315 *256*256
						+ currentColor.getGreen() *.215 * 256
							+ currentColor.getBlue()*.115);
          	
			db.setElemDouble(i, greyTone);
			
		}
		return base;	
	}

希望对您有所帮助。

英文:

You might try this to see if things speed up a little.

  • This uses a DataBuffer from an Image Raster.
  • And uses a map to retain previous converted colors. It may help over a period of time depending on the type of image.
  • And works with doubles since the data buffer supports various types.

I also multiplied your values by powers of 2 to shift them in the proper position. The get* methods of Color return values between 0 and 255 inclusive. The RGB occupy lower 24 bits on an int (Alpha is in the left most byte).

All I see is a dark image but I tested this with other parameters and I know it works. The long pole in the tent seems to be reading and writing the images. I was using a 6637 3787 image and could read, alter, and write it in 12 seconds. For more advanced processing you may want to check on AffineTransformOp.

	static Map&lt;Color, Double&gt; colorMap = new HashMap&lt;&gt;();
	
	public BufferedImage process(BufferedImage base) {
		DataBuffer db = base.getRaster().getDataBuffer();
		for (int i = 0; i &lt; db.getSize(); i++) {
			Color currentColor = new Color(db.getElem(i));
			double greyTone = colorMap.computeIfAbsent(currentColor, v-&gt; 
		    currentColor.getRed() * .315 *256*256
						+ currentColor.getGreen() *.215 * 256
							+ currentColor.getBlue()*.115);
          	
			db.setElemDouble(i, greyTone);
			
		}
		return base;	
	}

huangapple
  • 本文由 发表于 2020年4月10日 05:11:18
  • 转载请务必保留本文链接:https://java.coder-hub.com/61130264.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定