Pregunta

Como dice el título, estoy tratando de utilizar tablas de cuantización personalizado para comprimir una imagen en formato JPEG.Mi problema es que el archivo resultante no se puede abrir y el error es:

Quantization table 0x00 was not defined

Esta es la forma en que mi código es:

        JPEGImageWriteParam params = new JPEGImageWriteParam(null);
        if (mQMatrix != null) {
            JPEGHuffmanTable[] huffmanDcTables = {JPEGHuffmanTable.StdDCLuminance, JPEGHuffmanTable.StdDCChrominance};
            JPEGHuffmanTable[] huffmanAcTables = {JPEGHuffmanTable.StdACLuminance, JPEGHuffmanTable.StdACChrominance};
            dumpMatrices(mQMatrix);
            params.setEncodeTables(mQMatrix, huffmanDcTables, huffmanAcTables);
        }

        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();

        Iterator writers = ImageIO.getImageWritersByFormatName("JPEG");
        ImageWriter imageWriter = (ImageWriter) writers.next();

        ImageOutputStream imageOutputStream = ImageIO.createImageOutputStream(outputStream);
        imageWriter.setOutput(imageOutputStream);
        imageWriter.write(null, new IIOImage(mSourceImage, null, null), params);

        mCompressedImageSize = outputStream.size();

        try (FileOutputStream fileOutputStream = new FileOutputStream(mOutFileName)) {
            fileOutputStream.write(outputStream.toByteArray());

        }
        mCompressedImage = ImageIO.read(new ByteArrayInputStream(outputStream.toByteArray()));

Mi conjetura es que tiene algo que ver con los metadatos, pero no he tenido suerte de encontrar una solución.

Gracias, R.

ACTUALIZACIÓN:El uso de un hex viewer he determinado que la cuantización de la tabla (DQT - 0xFF, 0xDB sección) no está escrito en el archivo de salida.Estoy suponiendo que tengo a la fuerza para ser escrita de alguna manera.

ACTUALIZACIÓN 2:Así que después de que realmente la depuración de ejecución, lo que he encontrado es que si las mesas están colocadas en los parámetros objeto, a continuación, los metadatos no se genera ni la cuantización no las tablas de Huffman.Si falta los metadatos, a continuación, las tablas no están escritos en el archivo.La cosa es que veo que no hay manera de personalizar el contenido de los metadatos.

¿Fue útil?

Solución

Pregunta muy interesante, y por desgracia no trivial...He aquí lo que he encontrado:

Primero de todos, el uso de JPEGImageWriteParam.setEncodeTables(...) no hacer.A partir de la JavaDoc:

Establece la cuantización y Huffman tablas a utilizar en la codificación de los abreviado de flujos.

Y más de JPEG Formato de Metadatos de Especificación y Notas de Uso:

El orden en que se implementa el diseño intención de que las tablas deben ser incluidos en JPEGImageWriteParams sólo como un medio para especificar las tablas cuando no hay otra fuente está disponible, y esto sólo puede ocurrir cuando la escritura a una abreviado flujo sin tablas utilizando los conocidos no-tablas estándar para la compresión.

I. e., el parámetro de la opción puede sólo ser utilizado para escribir "abreviado de flujos" (costumbres imágenes Jpeg sin tablas, asumiendo las tablas serán proporcionados cuando la lectura de la espalda).Conclusión:La única manera en que podemos especificar las tablas para ser codificados con el formato JPEG, es para que se apruebe en la meta de datos.

Desde el mismo documento se mencionó anteriormente, las tablas de los metadatos serán ignorados y reemplazado a menos que modo de compresión de MODE_COPY_FROM_METADATA, por lo que debemos especificar que.

Ver el Los Metadatos de la imagen DTD para la documentación de la estructura de los metadatos.Las partes importantes son la dqt y dht los nodos con los sub-nodos, y su "objeto de Usuario"s (que no debe confundirse con el normal DOM "userData").Necesitamos actualizar estos nodos, con las nuevas tablas que queremos usar.

Aquí está el código que se me ocurrió:

// Obtain qtables
mQMatrix = ...;

// Read source image
ImageInputStream imageInputStream = ImageIO.createImageInputStream(...);
ImageReader reader = ImageIO.getImageReaders(imageInputStream).next();
reader.setInput(imageInputStream);

mSourceImage = reader.read(0);
IIOMetadata metadata = null;

// We need the imageWriter to create the default JPEG metadata
ImageWriter imageWriter = ImageIO.getImageWritersByFormatName("JPEG").next();

if (mQMatrix != null) {
    dumpMatrices(mQMatrix);

    // Obtain default image metadata data, in native JPEG format
    metadata = imageWriter.getDefaultImageMetadata(ImageTypeSpecifier.createFromRenderedImage(m‌​SourceImage), null);
    IIOMetadataNode nativeMeta = (IIOMetadataNode) metadata.getAsTree("javax_imageio_jpeg_image_1.0");

    // Update dqt to values from mQMatrix
    NodeList dqtables = nativeMeta.getElementsByTagName("dqtable");
    for (int i = 0; i < dqtables.getLength(); i++) {
        IIOMetadataNode dqt = (IIOMetadataNode) dqtables.item(i);
        int dqtId = Integer.parseInt(dqt.getAttribute("qtableId"));
        dqt.setUserObject(mQMatrix[dqtId]);
    }

    // For some reason, we need dht explicitly defined, when using MODE_COPY_FROM_METADATA...
    NodeList dhtables = nativeMeta.getElementsByTagName("dhtable");

    // Just use defaults for dht
    JPEGHuffmanTable[] huffmanDcTables = {JPEGHuffmanTable.StdDCLuminance, JPEGHuffmanTable.StdDCChrominance};
    JPEGHuffmanTable[] huffmanAcTables = {JPEGHuffmanTable.StdACLuminance, JPEGHuffmanTable.StdACChrominance};

    // Update dht
    for (int i = 0; i < dhtables.getLength(); i++) {
        IIOMetadataNode dht = (IIOMetadataNode) dhtables.item(i);
        int dhtClass = Integer.parseInt(dht.getAttribute("class")); // 0: DC, 1: AC
        int dhtId = Integer.parseInt(dht.getAttribute("htableId"));

        dht.setUserObject(dhtClass == 0 ? huffmanDcTables[dhtId] : huffmanAcTables[dhtId]);
    }

    // Merge updated tree back (important!)
    metadata.mergeTree("javax_imageio_jpeg_image_1.0", nativeMeta);
}

ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
ImageOutputStream imageOutputStream = ImageIO.createImageOutputStream(outputStream);
imageWriter.setOutput(imageOutputStream);

// See http://docs.oracle.com/javase/6/docs/api/javax/imageio/metadata/doc-files/jpeg_metadata.html#tables
JPEGImageWriteParam params = new JPEGImageWriteParam(null);
params.setCompressionMode(metadata == null ? MODE_DEFAULT : MODE_COPY_FROM_METADATA); // Unless MODE_COPY_FROM_METADATA, tables will be created!

imageWriter.write(null, new IIOImage(mSourceImage, null, metadata), params);
imageOutputStream.close();

mCompressedImageSize = outputStream.size();

try (FileOutputStream fileOutputStream = new FileOutputStream(mOutFileName)) {
    fileOutputStream.write(outputStream.toByteArray());
}

mCompressedImage = ImageIO.read(new ByteArrayInputStream(outputStream.toByteArray()));
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top