Java ARGB转JPG

3

如何将类型为TYPE_INT_ARGBBufferedImage保存为jpg格式?

程序生成了以下这张图片:

enter image description here

这张图片看起来没问题,但是当我尝试以以下方式保存时:

    ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
    BufferedOutputStream bos = new BufferedOutputStream(byteStream);
    try {
        ImageIO.write(buffImg, "jpg", bos);
        // argb
        byteStream.flush();
        byte[] newImage = byteStream.toByteArray();

        OutputStream out = new BufferedOutputStream(new FileOutputStream("D:\\test.jpg"));
        out.write(newImage);
        out.close();
    } catch (IOException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }

结果如下:

问题出在alpha层上,但是不知道如何解决。png格式不适合我,需要jpg。

enter image description here

你尝试过用白色替换Alpha通道吗? - neuraminidase7
不相关的:当你可以直接写入 FileOutputStream 时,为什么要先将数据写入字节数组,再将字节写入文件?相关的:使用诸如“BufferedImage JPG 颜色错误”的关键字进行搜索会产生一些结果,似乎在某些情况下会发生这种情况。 - Marco13
我同意。也许不是最优的方式。但这并不重要。现在使用ImageWriter保存它以控制图像质量。 - Gwalk
2个回答

6

好的! 我已经解决了。 一切都相当容易。不知道这是一个好决定,以及它有多快。我没有找到其他的方法。 所以...我们需要做的就是定义新的BufferedImage。

BufferedImage buffImg = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_INT_ARGB);
Graphics2D g2 = buffImg.createGraphics();

// ... other code we need

BufferedImage img= new BufferedImage(buffImg.getWidth(), buffImg.getHeight(), BufferedImage.TYPE_INT_RGB);
Graphics2D g2d = img.createGraphics();
g2d.drawImage(buffImg, 0, 0, null);
g2d.dispose();

如果有改进此方法的想法,请尽管提出。

2
FYI:JPEG 没有 alpha 通道,因此在读取/写入时会处理不正确。正如您所演示的那样,您需要将图像转换为非 alpha 基础的结果。 - MadProgrammer
谢谢!但是这是否是将BufferedImage转换的更正常的方式? - Gwalk
在这种情况下,是的。实际上,这是ImageIO读取器/写入器处理alpha值的错误。 - MadProgrammer
@Gwalk恭喜你成功解决了问题。由于这似乎是一个相当好的解决方案,请自行接受您的答案以标记此问题为已解决。 - Sylvain Leroux
@MadProgrammer 实际上,JFIF(JPEG交换格式)仅指定样本为YCbCr或灰度(作为Y),即没有alpha通道。我认为Exif也是如此。然而,JPEG 压缩本身允许压缩任何数据通道,包括alpha通道。最常见的写法是YCbCrA或RGBA(非子采样)。不幸的是,这在软件中实现得不太好,这就是我们看到上面那张奇怪颜色的粉色背景图像的原因(也是通常最好不要存储带有alpha通道的JPEG的原因)。 :-) - Harald K
1
@haraldK 我只知道JPEG + alpha = 不好 :P - 虽然感谢你提供的信息 - MadProgrammer

0

具有4个颜色通道的图像不应写入jpeg文件。我们可以在不重复像素值的情况下在ARGB和RGB图像之间进行转换。这对于大型图像非常方便。以下是一个示例:

int a = 10_000;

BufferedImage im = new BufferedImage(a, a, BufferedImage.TYPE_INT_ARGB);
Graphics2D g = im.createGraphics();
g.setColor(Color.RED);
g.fillRect(a/10, a/10, a/5, a*8/10);
g.setColor(Color.GREEN);
g.fillRect(a*4/10, a/10, a/5, a*8/10);
g.setColor(Color.BLUE);
g.fillRect(a*7/10, a/10, a/5, a*8/10);

//preserve transparency in a png file
ImageIO.write(im, "png", new File("d:/rgba.png"));

//pitfall: in a jpeg file 4 channels will be interpreted as CMYK... this is no good
ImageIO.write(im, "jpg", new File("d:/nonsense.jpg"));

//we need a 3-channel BufferedImage to write an RGB-colored jpeg file
//we can make up a new image referencing part of the existing raster
WritableRaster ras = im.getRaster().createWritableChild(0, 0, a, a, 0, 0, new int[] {0, 1, 2}); //0=r, 1=g, 2=b, 3=alpha
ColorModel cm = new BufferedImage(1, 1, BufferedImage.TYPE_INT_RGB).getColorModel(); //we need a proper ColorModel
BufferedImage imRGB = new BufferedImage(cm, ras, cm.isAlphaPremultiplied(), null);

//this image we can encode to jpeg format
ImageIO.write(imRGB, "jpg", new File("d:/rgb1.jpg"));

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接