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

EOF exception with Tiff Images on rotation #315

Closed
Priyalks opened this issue Jan 16, 2017 · 17 comments
Closed

EOF exception with Tiff Images on rotation #315

Priyalks opened this issue Jan 16, 2017 · 17 comments
Assignees

Comments

@Priyalks
Copy link

Priyalks commented Jan 16, 2017

Hi,

If I read a Tiff image, rotate it and write and repeat this once again. Third time when I read the Tiff Image I get EOF Exception . I get this Exception with almost all compression types of Tiff.

Is rotation doing something wrong or #306 will fix this issue as [well?]

                    ImageInputStream inputStream = ImageIO.createImageInputStream(listOfFiles[j]);
                    Iterator<ImageReader> iterator = ImageIO.getImageReaders(inputStream);
                    if (iterator == null || !iterator.hasNext()) {
                        throw new IOException("Image file format not supported");
                    }
                    reader = (ImageReader) iterator.next();
                    reader.setInput(inputStream);
                    int nbPages = reader.getNumImages(true);
                    List<BufferedImage> imageList = new ArrayList<BufferedImage>();
                    List<IIOMetadata> metadataList = new ArrayList<IIOMetadata>();
                    List<IIOImage> iioImages = new ArrayList<IIOImage>();
                    int angle;
                    String compressionType = null;
                    for (int i = 0; i < nbPages; i++) {
                        IIOImage ioimg = reader.readAll(i, null);
                        BufferedImage bi = (BufferedImage) ioimg.getRenderedImage();
                        IIOMetadata metadata = ioimg.getMetadata();
                        if (compressionType == null) {
                            IIOMetadataNode tree = (IIOMetadataNode) metadata.getAsTree(IIOMetadataFormatImpl.standardMetadataFormatName);
                            IIOMetadataNode compression = (IIOMetadataNode) tree.getElementsByTagName("CompressionTypeName").item(0);
                            compressionType = compression.getAttribute("value");
                        }

                        System.out.println(listOfFiles[j].getName() + " " + compressionType);
                        if (pageRotationMap.get(i + 1) != null) {
                            //rotate
                            angle = pageRotationMap.get(i + 1);
                            AffineTransform at = new AffineTransform();
                            if (angle == 90 || angle == 270)
                                at.translate(-(bi.getWidth() - bi.getHeight()) / 2, (bi.getWidth() - bi.getHeight()) / 2);
                            at.rotate(Math.toRadians(angle), bi.getWidth() / 2, bi.getHeight() / 2);
                            AffineTransformOp opRotated = new AffineTransformOp(at,
                                    AffineTransformOp.TYPE_NEAREST_NEIGHBOR);
                            bi = opRotated.filter(bi, null);
                        }
                        imageList.add(bi);
                        metadataList.add(metadata);
                        iioImages.add(ioimg);
                    }
                    reader.dispose();
                    Iterator writers = ImageIO.getImageWritersByFormatName("tiff");
                    imageWriter = (ImageWriter) writers.next();
                    ImageOutputStream ios = ImageIO.createImageOutputStream(new File("C:\\soft\\Tiff2\\" + listOfFiles[j].getName()));
                    imageWriter.setOutput(ios);
                    imageWriter.prepareWriteSequence(null);
                    for (int i = 0; i < imageList.size(); i++) {
                        if (compressionType.equalsIgnoreCase("Old JPEG") && !isGrayJPEG(metadataList.get(i))) {
                            ImageWriteParam param = imageWriter.getDefaultWriteParam();
                            param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
                            param.setCompressionType("JPEG");
                            imageWriter.writeToSequence(new IIOImage(imageList.get(i), null, null), param);
                        } else if (compressionType.equalsIgnoreCase("Old JPEG") || (compressionType.equalsIgnoreCase("JPEG") && isGrayJPEG(metadataList.get(i))) || compressionType.contains("CCITT") || compressionType.equalsIgnoreCase("PackBits")) {
                            ImageWriteParam param = imageWriter.getDefaultWriteParam();
                            param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
                            param.setCompressionType("LZW");
                            imageWriter.writeToSequence(new IIOImage(imageList.get(i), null, null), param);
                        } else
                            imageWriter.writeToSequence(new IIOImage(imageList.get(i), null, metadataList.get(i)), null);
                    }
                    imageWriter.endWriteSequence();
                    imageWriter.dispose();
                    System.out.println(Instant.now().toString());
                    ios.flush();
                    ios.close();
@Priyalks
Copy link
Author

Priyalks commented Jan 18, 2017

Any updates ?

This is happening mainly with LZW compressed Tiff images(also with CCITT and Packbits as they are written in LZW bcoz of the existing bugs #312 #314 )

Also when the image is rotated 90/270 degree multiple times. Doesn't happen with 180

@haraldk
Copy link
Owner

haraldk commented Jan 18, 2017

As already mentioned, I believe this is the same issue as #306 (bug in the line length computation in the LZW compression). I don't see what is different in this issue than what we already know from #310 (and #306, #312 and #314).

I would guess (without testing) that it would work fine if you changed from LZW to Deflate compression for now.

As already mentioned, I will work on fixing these bugs as soon as possible (but as it's mostly me doing development on my spare time, that may not be as quickly as any of us would like).

Best regards,

--
Harald K

@haraldk haraldk self-assigned this Jan 18, 2017
@Priyalks
Copy link
Author

Ok Thanks Harald..We will wait for the fix.
Only difference from #310 here is that this throws EOF with image rotation (90/270) .
Changing from LZW to Deflate doesn't work.

@haraldk
Copy link
Owner

haraldk commented Jan 18, 2017

After the latest changes, I ran (a slightly edited* version of) your code above and it worked fine with the input data from #310 + #307 (except the JBIG data, which I still can't read).

I could then re-run the program multiple times (I stopped after 4 times), without problems. For each step, I also manually inspected the files in macOS Preview and the TIFFImageReader, and the looked good each time.

Please verify, or update the issue with further details to reproduce.

*) I don't know what's in your pageRotationMap, so I just hardcoded angle = 90, which should have provoked the exception, AFAIK. I also just read all input files from the command line in a loop, instead of hardcoding paths. Finally, I added support for re-writing the PIXTIFF ZIP compression to Deflate, like this:

ImageWriteParam param = imageWriter.getDefaultWriteParam();

if ("Old JPEG".equals(compressionType)) {
    param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
    param.setCompressionType("JPEG");
}
else if (compressionType.equals("Unknown 50013")) {
    // Currently a limitation in the metadata class, doesn't recognise the PIXTIFF ZIP compression
    param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
    param.setCompressionType("Deflate");
}

imageWriter.writeToSequence(new IIOImage(imageList.get(i), null, metadataList.get(i)), param);

--
Harald K

@Priyalks
Copy link
Author

Thanks a lot Harald.
Thanks for fixing the issues so soon.

I did some test and found some issues:

  1. multiplage LZW compressed Tiff : Throws EOF Exception on re-reading
    (132Pages.tiff- have already attached the doc in Tiff Image Rotation increases the size 6x times #310 )

2)CCITT RLE - image blackens when we just read and write
60 plexustiff binary modified g3
18 tiff binary modified g3

3)4 bit Gray LZW - EOF Exception - reading the image 3rd time(read, write and read the same image)
32 tiff 4 bit gray lzw

4)Binary LZW - EOF Exception - when I read , rotate , write and read the same image
21 tiff binary lzw

@haraldk
Copy link
Owner

haraldk commented Jan 19, 2017

Hi,

  1. Seems there is still issues with writing LZW. I can reproduce the EOFException on read-write-read. I could read-write-read multiple times, if I change the LZW compression to ZLib/Deflate, however. Also, I get EOFExceptions on different pages when rotating/not rotating, so I think there's some relation to dimensions/line padding (odd widths perhaps?).

  2. I can't reproduce (or don't understand) this issue. The image does not become black after several iterations, with or without rotation.

  3. and 4. See 1. I think this is the same issue.

PS: Can we close #310 for now, and continue trouble-shooting in this issue? I'll file separate bugs for each bug I'm able to reproduce in addition, to keep things somewhat tidy.

--
Harald K

@haraldk
Copy link
Owner

haraldk commented Jan 19, 2017

Okay, so I think I found the bug in the LZW encoding.

Currently, the length of the LZW buffer is calculated based like roundUp(w * h * bits). While it should beroundUp(w * bits) * h, as each scan line needs padding, not just the entire tile. So the LZW stream is truncated for odd widths.

Should be fixable. 😀

PS: There's a (completely unrelated to the bug) rounding error in your code, so that for each step of 90 degrees rotation, a little of the image is chopped off. This is caused by the integer division in the at.translate(..) and at.rotate(..) arguments (/ 2 instead of / 2.0).

--
Harald K

@haraldk
Copy link
Owner

haraldk commented Jan 19, 2017

Committed a fix now, please pull and build from latest master, to see if that solves your issues.

With the fix I can now run the 90 degree rotation of all pages in the 132 page doc, 4 times in a row, and end up with pretty much the same as the starting point (meta data and some file layout is changed, but pixel data should be the same).

--
Harald K

@Priyalks
Copy link
Author

Thanks Harald,

Now almost all the issues are resolved. But CCITT RLE image issue is still reproducible. Just read and write and can see difference in the image.
Attaching both original and modified image
18 tiff binary modified g3
18 tiff binary modified g3

@haraldk
Copy link
Owner

haraldk commented Jan 20, 2017

I see.

I need to look into this last issue a little bit more, to determine what the problem is. The TIFFImageReader does read both the images above, and displays them just the same. However, I see that other software displays the colors inverted...

So... Either there's bug in both the reader and writer, or the combination of CCITT compression and BlackIsZero photometric interpretation is just very exotic and not widely supported....

According to the TIFF 6.0 Specification, Section 11: CCITT Bilevel Encodings:

PhotometricInterpretation. An encoded CCITT string is self-photometric, defined
in terms of white and black runs. Yet TIFF defines a tag called
PhotometricInterpretation that also purports to define what is white and what is
black. Somewhat arbitrarily, we adopt the following convention:

The “normal” PhotometricInterpretation for bilevel CCITT compressed data is
WhiteIsZero. In this case, the CCITT “white” runs are to be interpretated as white,
and the CCITT “black” runs are to be interpreted as black. However, if the
PhotometricInterpretation is BlackIsZero, the TIFF reader must reverse the meaning
of white and black when displaying and printing the image.

The samples use the modified Huffman compression (Compression: 2), but essentially the same text is found in Section 10: Modified Huffman Compression.

Because the most "natural" mode for Java is BlackIsZero, I convert the values when reading, and write the values as-is. So a read-write cycle will change from WhiteIsZero to BlackIsZero. But still, I believe everything is implemented correctly, according to the spec. It's of course unfortunate if this differs from what other software thinks, so we might want to change that behavior...

PS: Incidentally, if I view the images in gmail, they look the same too.

--
Harald K

@Priyalks
Copy link
Author

ok ..

Any approximate timeline for 3.3.2 release?

@haraldk
Copy link
Owner

haraldk commented Jan 24, 2017

Hopefully this week.

--
Harald K

@Priyalks
Copy link
Author

Hi Harald,

Will the release happen anytime this week? looks like it didn't happen last week.

@haraldk
Copy link
Owner

haraldk commented Jan 30, 2017

Hi again,

Sorry, but I can't promise any release date. Until someone pays for the hours I put into the project, I only have my spare time to spend, and with a family, this time is limited.

Unfortunately, the job of merging the bug fixes down to the 3.3 branch was more time consuming than I first thought. I'll see what I can do this week though.

--
Harald K

@Priyalks
Copy link
Author

Priyalks commented Jan 31, 2017 via email

@haraldk
Copy link
Owner

haraldk commented Feb 2, 2017

Hi,

I just released 3.3.2. Binaries should be available through Maven Central within a few hours.

Best regards,

--
Harald K

@haraldk haraldk closed this as completed Feb 2, 2017
@Priyalks
Copy link
Author

Priyalks commented Feb 3, 2017 via email

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants