使用ImageIO导出8位深度JPEG

问题描述 投票:0回答:1

使用Java ImageIO,是否可以导出位深度为8的jpeg图像?我该怎么做?即使导出TYPE_BYTE_BINARY的BufferedImage(它是灰度图像),结果还是JPEG的位深度为24。

这是我到目前为止所拥有的。

public void testJpegBitDepth() throws Exception{
    Path pIn = Paths.get("testing/jpg/box1.jpg"), pOut;
    BufferedImage bi;

    //*******************************************
    //Write 8 bit jpg

    //Init ImageWriter
    Iterator<ImageWriter> it = ImageIO.getImageWritersByFormatName("jpg");
    ImageWriter writer = null;
    while(it.hasNext()) {
        try {
            writer = it.next();

            //Read input
            bi = ImageIO.read(pIn.toFile());
            if(bi == null)
                throw new Exception("Failed to read input file: " + pIn);

            //Convert to gray
            bi = AWTImaging.convertToGray(bi);
            log.debug("Num bands from the image raster: " + bi.getRaster().getNumBands());

            pOut = test.outputDir.resolve("jpegBitDepth-8-"
                    + pIn.getFileName().toString() + ".jpg");

            //Init ImageTypeSpecifier
            ImageTypeSpecifier imageType = ImageTypeSpecifier.createGrayscale(
                    8,                      //8 bits per pixel 
                    DataBuffer.TYPE_BYTE,   //stored in a byte
                    false);                 //unsigned

            //Init WriteParam
            ImageWriteParam param = writer.getDefaultWriteParam();
            param.setDestinationType(imageType);
           //Not sure if this is required or not, but the same Exception occurs either way
           //param.setSourceBands(new int[] {0});

            //Init meta
            IIOMetadata meta = writer.getDefaultImageMetadata(imageType, param);

            String metadataFormat = "javax_imageio_jpeg_image_1.0";
            IIOMetadataNode root = new IIOMetadataNode(metadataFormat);
            IIOMetadataNode jpegVariety = new IIOMetadataNode("JPEGvariety");
            IIOMetadataNode markerSequence = new IIOMetadataNode("markerSequence");

            //I think we want app0JFIF metadata here, as it can specify a grayscale image https://docs.oracle.com/javase/10/docs/api/javax/imageio/metadata/doc-files/jpeg_metadata.html
            IIOMetadataNode app0JFIF = new IIOMetadataNode("app0JFIF");

            root.appendChild(jpegVariety);
            root.appendChild(markerSequence);
            jpegVariety.appendChild(app0JFIF);

            meta.mergeTree(metadataFormat, root);

            //Export jpg
            Files.deleteIfExists(pOut);
            ImageOutputStream ios = ImageIO.createImageOutputStream(pOut.toFile());
            writer.setOutput(ios);
            writer.write(meta, new IIOImage(bi, null, meta), param);
            log.debug("Succeded writing jpeg with writer: " + writer.getClass().toString());
            break;
        }catch(Exception e) {
            log.error("Failed writing jpeg with writer: " + (writer != null ? writer.getClass().toString():"null"));
            log.error("Ex: " + e);              
        }
    }

}

我从JpegImageWriter抛出异常,这是相关的堆栈跟踪:

    Ex: javax.imageio.IIOException: Metadata components != number of destination bands
File=null,Class=com.sun.imageio.plugins.jpeg.JPEGImageWriter,Method=checkSOFBands,Line=-1
File=null,Class=com.sun.imageio.plugins.jpeg.JPEGImageWriter,Method=writeOnThread,Line=-1
File=null,Class=com.sun.imageio.plugins.jpeg.JPEGImageWriter,Method=write,Line=-1

[我也知道缓冲图像是TYPE_BYTE_BINARY,并且光栅具有1波段(我在上面的调试消息中打印了该波段)。因此,异常消息使我认为我需要在app0JFIF元数据中定义要导出1个波段的数据。我不知道如何定义它,有人对此有任何经验吗?此元数据很难使用,或者仅仅是我?

提前感谢。

java image jpeg javax.imageio grayscale
1个回答
0
投票

您需要一支乐队是正确的。这是我的操作方式:

if (bi.getSampleModel().getNumBands() != 1) {
    ColorModel colorModel = new ComponentColorModel(
        ColorSpace.getInstance(ColorSpace.CS_GRAY), false, false,
        Transparency.OPAQUE, DataBuffer.TYPE_BYTE);

    BufferedImage oneBandedImage = new BufferedImage(colorModel,
        colorModel.createCompatibleWritableRaster(
            bi.getWidth(), bi.getHeight()),
        false, new Properties());

    Graphics g = oneBandedImage.createGraphics();
    g.drawImage(bi, 0, 0, null);
    g.dispose();

    bi = oneBandedImage;
}

之后,我不需要直接获取ImageWriter,也不需要设置任何元数据; ImageIO.write(bi, "JPEG", file)足够。

我在结果上运行了/usr/bin/file,并得到了这个:

JPEG image data, JFIF standard 1.02, aspect ratio, density 1x1, segment length 16, baseline, precision 8, 315x180, components 1

我假设components 1部分意味着它只有一个通道。

© www.soinside.com 2019 - 2024. All rights reserved.