Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

"javax.imageio.IIOException: No SOF segment in stream" when using JPEG compression in a TIFF #433

Closed
THausherr opened this issue Aug 4, 2018 · 4 comments

Comments

@THausherr
Copy link

THausherr commented Aug 4, 2018

test code:

@Test
    public void TestWriteRead() throws IOException
    {
        BufferedImage image = new BufferedImage(100, 100, BufferedImage.TYPE_INT_RGB);

        ImageWriter writer = ImageIO.getImageWritersByFormatName("tif").next();
        ImageWriteParam param = writer.getDefaultWriteParam();
        IIOMetadata metadata = writer.getDefaultImageMetadata(new ImageTypeSpecifier(image), param);

        System.out.println(writer);
        System.out.println(param);
        System.out.println(metadata);

        param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
        param.setCompressionType("JPEG"); // https://download.java.net/media/jai-imageio/javadoc/1.1/com/sun/media/imageio/plugins/tiff/TIFFImageWriteParam.html
        try (ImageOutputStream imageOutputStream = ImageIO.createImageOutputStream(new File("test.tif")))
        {
            System.out.println(imageOutputStream);
            writer.setOutput(imageOutputStream);
            writer.write(null, new IIOImage(image, null, metadata), param);
        }

        ImageReader reader = ImageIO.getImageReadersByFormatName("tif").next();
        System.out.println(reader);
        try (ImageInputStream imageInputStream = ImageIO.createImageInputStream(new File("test.tif")))
        {
            reader.setInput(imageInputStream);
            reader.read(0);
        }
    }

output:

com.sun.media.imageioimpl.plugins.tiff.TIFFImageWriter@2471cca7
com.sun.media.imageio.plugins.tiff.TIFFImageWriteParam@5fe5c6f
com.sun.media.imageioimpl.plugins.tiff.TIFFImageMetadata@6979e8cb
javax.imageio.stream.FileImageOutputStream@2d6a9952
com.sun.media.imageioimpl.plugins.tiff.TIFFImageReader@7225790e
Exception in thread "main" javax.imageio.IIOException: No SOF segment in stream
	at com.twelvemonkeys.imageio.plugins.jpeg.JPEGImageReader.getSOF(JPEGImageReader.java:762)
	at com.twelvemonkeys.imageio.plugins.jpeg.JPEGImageReader.read(JPEGImageReader.java:334)
	at com.sun.media.imageioimpl.plugins.tiff.TIFFJPEGDecompressor.decodeRaw(TIFFJPEGDecompressor.java:224)
	at com.sun.media.imageio.plugins.tiff.TIFFDecompressor.decode(TIFFDecompressor.java:2527)
	at com.sun.media.imageioimpl.plugins.tiff.TIFFImageReader.decodeTile(TIFFImageReader.java:1137)
	at com.sun.media.imageioimpl.plugins.tiff.TIFFImageReader.read(TIFFImageReader.java:1417)
	at javax.imageio.ImageReader.read(ImageReader.java:939)
	at jdk9test.JDK9Test.main(JDK9Test.java:44)

This happens only if I use the JPEG decoder from here and the jai_imageio.jar from oracle (or the community fork on github). If I use all from oracle or all from twelvemonkeys it works.

test.zip

@haraldk
Copy link
Owner

haraldk commented Aug 6, 2018

Hmmmm...

Interesting... We never made any promises about partial interop with JAI ImageIO. ;-) But in theory, this should just work. And now that the TIFF plugin is part of the JRE in Java 9+, I think we have to look into it and make sure it does.

Which version of the library did you use? The latest release, or the latest master build?

Also, did you use the Twelvemonkeys JPEGImageWriter for writing the JPEG data for the TIFF, or the standard Sun/Oracle one? Or does that not matter in this case?

I see that the TIFF uses strips, with RowsPerStrip set to 32 (resulting in 4 strips), but does not set the JPEGTables field (optional). The file seems to contain 4 full, valid JPEG streams, so this should be all fine though.

I need some time to look into this in detail.

--
Harald K

@THausherr
Copy link
Author

I used release versions:

common-image-3.3.2.jar
common-io-3.3.2.jar
common-lang-3.3.2.jar
imageio-core-3.3.2.jar
imageio-jpeg-3.3.2.jar
imageio-metadata-3.3.2.jar

jai-imageio-core-1.4.0.jar (jai community fork)

I set a class breakpoint and com.twelvemonkeys.imageio.plugins.jpeg.JPEGImageWriterSpi.createWriterInstance() is called and a com.twelvemonkeys.imageio.plugins.jpeg.JPEGImageWriter object is created. This was indirectly called by com.github.jaiimageio.impl.plugins.tiff.TIFFBaseJPEGCompressor.initJPEGWriter(). Later, com.twelvemonkeys.imageio.plugins.jpeg.JPEGImageWriter.write(IIOMetadata streamMetadata, IIOImage image, ImageWriteParam param) is called 4 times.

So that would be a yes on the "did you use the Twelvemonkeys JPEGImageWriter for writing the JPEG data for the TIFF" question.

Using or not using the standard jpeg library for writing makes no difference, the file created is the same. This would suggest it is only a reading problem.

I extracted the jpeg file from the tiff file (offset 300, len 667) and no problem reading it with twelvemonkeys.

Possible idea:

Twelvemonkeys JPEGImageReader.readSegments() has "imageInput.seek(0);". Could it be that this is wrong when the jpeg is within the other file?

The original JPEGImageReader has checkTablesOnly() which stores the beginning position in imagePositions, which is used later.

@haraldk
Copy link
Owner

haraldk commented Aug 6, 2018

Thanks Tilman!

Twelvemonkeys JPEGImageReader.readSegments() has "imageInput.seek(0);". Could it be that this is wrong when the jpeg is within the other file?

Yes, this is indeed the bug.

For the TwelveMonkeys TIFFImageReader, I always pass the offset JPEG streams as a SubImageInputStream with local offset 0 and (if known) a limited length. This allows the code above to work fine. However, the JAI version simply passes the full TIFF stream (located at the correct offset), and thus seeking to the beginning doesn't work.

If I wrap the stream in a SubImageInputStream (inside the JPEGImageReader.setInput method) if the offset is != 0, the test passes. This seems to be ok as a quick fix, all other tests passes. But the seek should probably not be there in the first place... I'll see if I can find a better fix.

Anyway, thanks for reporting and looking further into the issue!

--
Harald K

@haraldk
Copy link
Owner

haraldk commented Aug 18, 2018

Fixed in latest master.

@haraldk haraldk closed this as completed Aug 18, 2018
haraldk added a commit that referenced this issue Aug 18, 2018
Repository owner deleted a comment from rigeborod Mar 30, 2019
Repository owner deleted a comment from rigeborod Mar 30, 2019
Repository owner deleted a comment from rigeborod Mar 30, 2019
Repository owner locked as resolved and limited conversation to collaborators Mar 30, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

2 participants