Экспорт 8-битного JPEG с ImageIO - PullRequest
       136

Экспорт 8-битного JPEG с ImageIO

0 голосов
/ 11 апреля 2020

Используя Java ImageIO, возможно ли экспортировать изображение в формате JPEG с битовой глубиной 8? Как бы я это сделал? Даже при экспорте изображения BufferedImage из TYPE_BYTE_BINARY, которое представляет собой изображение в градациях серого, в результате получается 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 полосу. Хотя я не знаю, как это определить, есть ли у кого-нибудь опыт с этим? С этими метаданными сложно работать, или это только у меня?

Заранее спасибо.

1 Ответ

0 голосов
/ 11 апреля 2020

Вы правы насчет необходимости одной группы. Вот как я это сделал:

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 часть означает, что он имеет только один канал.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...