문제

제목에서 알 수 있듯이 사용자 정의 양자화 테이블을 사용하여 JPEG 형식의 이미지를 압축하려고 합니다.내 문제는 결과 파일을 열 수 없으며 오류는 다음과 같습니다.

Quantization table 0x00 was not defined

내 코드는 다음과 같습니다.

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

내 생각에는 메타데이터와 관련이 있는 것 같지만 해결책을 찾지 못했습니다.

감사합니다, R.

업데이트:16진수 뷰어를 사용하여 양자화 테이블(DQT - 0xFF, 0xDB 섹션)이 출력 파일에 기록되지 않는 것을 확인했습니다.어떻게든 강제로 써야 할 것 같아요.

업데이트 2:따라서 실제로 실행을 디버깅한 후 내가 발견한 것은 테이블이 매개변수 개체에 설정되어 있으면 양자화 및 허프만 테이블에 대해 메타데이터가 생성되지 않는다는 것입니다.메타데이터가 누락되면 테이블이 파일에 기록되지 않는 것입니다.문제는 메타데이터의 내용을 사용자 정의할 방법이 없다는 것입니다.

도움이 되었습니까?

해결책

매우 흥미로운 질문이고 불행히도 사소하지 않습니다 ...내가 찾은 내용은 다음과 같습니다.

우선, JPEGImageWriteParam.setEncodeTables(...) 하지 않습니다.로부터 JavaDoc:

축약된 스트림을 인코딩하는 데 사용할 양자화 및 허프만 테이블을 설정합니다.

그리고 더 멀리서 JPEG 메타데이터 형식 사양 및 사용 참고사항:

이 순서는 테이블이 포함되어야 한다는 디자인 의도를 구현합니다. JPEGImageWriteParams 다른 소스를 사용할 수 없을 때 테이블을 지정하는 수단으로만 사용되며 이는 압축을 위해 알려진 비표준 테이블을 사용하여 테이블 없이 축약된 스트림에 쓸 때만 발생할 수 있습니다.

즉, param 옵션은 다음과 같습니다. 오직 "축약된 스트림"(다시 읽을 때 테이블이 제공된다고 가정하고 테이블이 없는 사용자 정의 JPEG)을 작성하는 데 사용됩니다.결론:JPEG로 인코딩할 테이블을 지정할 수 있는 유일한 방법은 메타 데이터에 테이블을 전달하는 것입니다.

위에서 언급한 동일한 문서에서 압축 모드를 선택하지 않으면 메타데이터의 테이블은 무시되고 교체됩니다. MODE_COPY_FROM_METADATA, 그래서 우리는 그것을 지정해야 합니다.

참조 이미지 메타데이터 DTD 메타데이터 구조에 대한 문서화입니다.중요한 부분은 dqt 그리고 dht 하위 노드가 있는 노드 및 해당 "사용자 개체"(일반 DOM "userData"와 혼동하지 마십시오).사용하려는 새 테이블로 이러한 노드를 업데이트해야 합니다.

내가 생각해낸 코드는 다음과 같습니다.

// 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()));
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top