Domanda

Come dice il titolo, sto cercando di utilizzare le tabelle di quantizzazione personalizzate per comprimere un'immagine in formato JPEG.Il mio problema è che il file risultante non può essere aperto e l'errore è:

Quantization table 0x00 was not defined
.

Ecco come è il mio codice:

        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()));
.

La mia ipotesi è che ha qualcosa a che fare con i metadati, ma non ho avuto fortuna a trovare una soluzione.

Grazie, R.

Aggiornamento: utilizzando un visualizzatore esagonale Ho determinato che la tabella di quantizzazione (sezione DQT - 0xFF, 0xDB) non viene scritta nel file di output.Suppongo che devo costringerlo a essere scritto in qualche modo.

Aggiornamento 2: Quindi, dopo aver effettuato effettivamente l'esecuzione di debug, ciò che ho trovato è che se le tabelle sono impostate nell'oggetto Parametri, non viene generato i metadati non sono generati per né la quantizzazione non le tabelle Huffman.Se mancano i metadati, le tabelle non vengono scritte nel file.La cosa è che non vedo alcun strada per personalizzare il contenuto dei metadati.

È stato utile?

Soluzione

Domanda molto interessante, e sfortunatamente non banale ... Ecco cosa ho trovato:

Prima di tutto, usando JPEGImageWriteParam.setEncodeTables(...) non lo farà. Dal javadoc >:

.

Imposta le tabelle di quantizzazione e huffman da utilizzare nella codifica dei flussi abbreviati.

e oltre da Specifiche del formato di metadati JPEG e note di utilizzo :

.

Questo ordinamento implementa l'intenzione di progettazione che le tabelle debbano essere incluse in JPEGImageWriteParams solo come mezzo per specificare le tabelle quando non è disponibile alcuna origine, e ciò può verificarsi solo quando si scrive su un flusso abbreviato senza tabelle con tavoli non standard noti per tavoli compressione.

I.e., L'opzione Param può solo essere utilizzata per scrivere "flussi abbreviati" (jpegs doganale senza tabelle, supponendo che le tabelle verranno fornite durante la lettura). Conclusione: l'unico modo in cui possiamo specificare le tabelle da codificare con JPEG, è di passarle nei Meta Data.

Dallo stesso documento sopra menzionato, le tabelle dei metadati verranno ignorate e sostituite a meno che la modalità di compressione non sia generatoDicetagcode, quindi dobbiamo specificarlo.

Vedi il Image Metadata DTD per la documentazione sulla struttura dei metadati. Le parti importanti sono i generatori di MODE_COPY_FROM_METADATA e i nodi dqt con i sub-nodi e il loro "oggetto utente" s (non confuso con il normale DOM "UserData"). Dobbiamo aggiornare questi nodi, con le nuove tabelle che vogliamo usare.

Ecco il codice che ho trovato:

// 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()));
.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top