在 BufferedImage 中乘以像素值会导致奇怪的行为

Multiplying Pixel Values in BufferedImage results in strange Behaviour

我目前正在开发一个程序来帮助摄影师创建延时摄影。 它计算一系列图像的亮度下降或上升。因此,例如曝光和 iso 的变化不会影响亮度的整体下降。

为此,我使用一个简单的基于 Swing 的界面来显示第一张和最后一张图像。它们下方是用于调整图像亮度的滑块。

这是通过直接操作底层 DataBuffer 的 BufferedImages 来应用的。

大多数情况下这是有效的,但我遇到了一些似乎有问题的图像。

你知道为什么会这样吗?

public BufferedImage getImage(float mult){
    BufferedImage retim;
    retim =  new BufferedImage(img.getWidth(), img.getHeight(), img.getType());
    Graphics g = retim.getGraphics();
    g.drawImage(img, 0, 0, null);
    g.dispose();

    DataBufferByte db = (DataBufferByte) retim.getRaster().getDataBuffer();
    byte[] bts = db.getData();
    for(int i=0;i<bts.length;i++){
        float n = bts[i]*mult;
        if(n > 255){
            bts[i]= (byte) 255;
        }else{
            bts[i] = (byte) n;
        }
    }
    return retim;
}

这是一种采用浮点数并将图像中的每个像素与其相乘的方法。 (还有一些防止字节值溢出的代码)。

This is the unwanted behaviour (on the left) and the expected on the right.

这是因为图像中某些字节的值被限制了。

例如(假设RGB单色space):

像素从(125,255,0)开始,如果乘以因子2.0,结果是(255,255,0)。这与原来的色调不同。

这也是奇怪的结果只出现在已经具有高亮度的像素上的原因。

这个link可能有助于改进亮度调节算法。

您也可以参考this related question

你的问题是这一行,它的发生是由于 Java bytes 被签名(在 [-128...127] 范围内):

float n = bts[i] * mult;

乘法后,您的n变量可能为负,从而导致溢出。

要修复它,请使用位掩码将值作为无符号整数(在 [0...255] 范围内),然后再与常量相乘:

float n = (bts[i] & 0xff) * mult;

更好的解决方法可能是使用 RescaleOp,它是为 BufferedImage 进行亮度调整而构建的。

类似于:

public BufferedImage getImage(float mult) {
    return new RescaleOp(mult, 0, null).filter(img, null);
}