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

Adding support to reading tiled and YcbCr jpeg tiffs through libtiff #3227

Merged
merged 13 commits into from
Sep 29, 2018

Conversation

kkopachev
Copy link
Contributor

@kkopachev kkopachev commented Jul 3, 2018

Fixes #2926 and #3044 probably #3195 and #3206.

Changes proposed in this pull request:

  • Read tiled images through LibTIFF
  • Read YCbCr JPEG-encoded TIFFs as RGB
  • for non-LibTiff case limit tile dimensions to not exceed image dimensions

Opening this mostly to gather feedback. Code is working, but missing tests. Would like to get some thoughts before I went too far with this.

ignoring whitespace changes might help with code review: https://github.com/python-pillow/Pillow/pull/3227/files?w=1

5.0 release notes had statement that all compressed tiffs require libtiff to be installed. That was introduced in #2899

(MM, 6, (1,), 1, (8, 8, 8, 8, 8), (0, 0)): ("YCbCr", "YCbCrXXX"),
(II, 6, (1,), 1, (8, 8, 8, 8, 8, 8), (0, 0, 0)): ("YCbCr", "YCbCrXXX"),
(MM, 6, (1,), 1, (8, 8, 8, 8, 8, 8), (0, 0, 0)): ("YCbCr", "YCbCrXXX"),
(II, 6, (1,), 1, (8, 8, 8), ()): ("RGB", "RGB"),
Copy link
Contributor Author

@kkopachev kkopachev Jul 3, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LibTiff is able to read YCbCr from jpeg-encoded images, but it does not do upsampling. So it either requires somebody to write upsampling code, or let LibTiff ask libjpeg to output in RGB. I thought this was acceptable, since JpegDecode already outputs RGB by default and making so for tiffs could be acceptable too.

(MM, 6, (1,), 1, (8, 8, 8), ()): ("YCbCr", "YCbCr"),
(II, 6, (1,), 1, (8, 8, 8, 8), (0,)): ("YCbCr", "YCbCrX"),
(MM, 6, (1,), 1, (8, 8, 8, 8), (0,)): ("YCbCr", "YCbCrX"),
(II, 6, (1,), 1, (8, 8, 8, 8, 8), (0, 0)): ("YCbCr", "YCbCrXXX"),
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think there was a typo and rawmode should have being YCbCrXX (extra X removed).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This has now been taken care of with the merge of #3335

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I knew that seen this somewhere! )

self.tile.append(
(self._compression,
(x, y, x+w, y+h),
o, a))
(min(x, xsize), min(y, ysize), min(x+w, xsize), min(y+h, ysize)),
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this to resolve #3044 limiting tile size.

@kkopachev kkopachev force-pushed the master branch 2 times, most recently from d3c8550 to d11d8ca Compare July 4, 2018 06:21
@kkopachev kkopachev changed the title [WIP] Adding support to reading tiled and YcbCr jpegs tiffs through libtiff Adding support to reading tiled and YcbCr jpegs tiffs through libtiff Jul 17, 2018
@doko42
Copy link

doko42 commented Jul 17, 2018

any update on this?

@kkopachev kkopachev force-pushed the master branch 2 times, most recently from 2380f0a to 66459df Compare July 17, 2018 17:45
@kkopachev
Copy link
Contributor Author

I think I'm done with changes here. Should be ready to review.

I've noticed, that winbuilds are all without libtiff, because it could not find zlib when compiling libtiff. I'll try to fix that as well, but in another commit.

@kkopachev
Copy link
Contributor Author

Tests pass and I think this is all ready to go now. Please review.

@radarhere
Copy link
Member

@kkopachev
Copy link
Contributor Author

@radarhere From my understanding, the line you referenced is only used for writing through libtiff. This PR adds only reading of tiled images and YCbCr's through libtiff.

@radarhere
Copy link
Member

You're right that the line is only using for writing - I was thinking of the comment. This PR means that 'We don't have support for tiled images in libtiff' is no longer true.

'We don't have support for writing tiled images with libtiff'?

@kkopachev
Copy link
Contributor Author

@radarhere fixed.

@radarhere radarhere changed the title Adding support to reading tiled and YcbCr jpegs tiffs through libtiff Adding support to reading tiled and YcbCr jpeg tiffs through libtiff Sep 10, 2018
@kkopachev
Copy link
Contributor Author

@homm do you happen to have YCbCr images with extra samples? I think libtiff in case of jpeg compressed tiff will always expect 3 samples and will ignore all extra samples anyway, but I am not sure if it's even possible to make tiff with YCbCrX mode.

@kkopachev
Copy link
Contributor Author

kkopachev commented Sep 10, 2018

I back-merged master into this PR and dropped X for all JPEG-compressed modes.
It says here at table 5 that YCbCr tiffs must have 3 samples.

@homm
Copy link
Member

homm commented Sep 11, 2018

do you happen to have YCbCr images with extra samples?

I'm not sure. I just added XX to all modes where this doesn't break anything )

You are right, according to specification, there is could be only 3 channels for YCbCr images:

screen shot 2018-09-11 at 14 09 29

It is better to drop all extra RGB → RGB modes for PhotoInterpretation=6 (former YCbCrX, YCbCrXX and YCbCrXXX).

@homm
Copy link
Member

homm commented Sep 11, 2018

Could you also drop them from unpack.c?

@kkopachev
Copy link
Contributor Author

@homm @radarhere all done here. Please take a look.

@kkopachev
Copy link
Contributor Author

@radarhere I did rebase. Let me know, if you want commits squashed.

@@ -151,10 +151,6 @@ def test_YCbCr(self):
self.assert_pack("YCbCr", "YCbCr", 3, (1, 2, 3), (4, 5, 6), (7, 8, 9))
self.assert_pack("YCbCr", "YCbCr;L", 3,
(1, 4, 7), (2, 5, 8), (3, 6, 9))
self.assert_pack(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I change my mind. This is actually breaking change, because 'YCbCrX' is allowed for using in user-space:

data = im.tobytes("raw", "YCbCrX")
Image.frombytes(im.mode, im.size, data, "raw", "YCbCrX", 0, 1)

I think it's better to leave YCbCrX mode and drop modes with more Xs

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This applies only to pack and unpack. No support of YCbCrX for tiff is required.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added them back.

@homm homm added the TIFF label Sep 25, 2018
if fillorder == 2:
key = (
self.tag_v2.prefix, photo, sampleFormat, 1,
self.tag_v2.get(BITSPERSAMPLE, (1,)),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are all other values except fillorder always should be the same as in the previous key? If so, it's better to modify existing key.

Option 1:

key = key[:3] + (1,) + key[4:]

Option 2:

key = list(key)
key[3] = 1
key = tuple(key)

(x, y, x+w, y+h),
o, a))
(x, y, min(x+w, xsize), min(y+h, ysize)),
offset, a))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indent should be the same as in the line above.

// with shuffle. (or, just alloc a buffer myself, then figure out how to get it
// back in. Can't use read encoded stripe.
if (TIFFIsTiled(tiff)) {
uint32 x, y, tile_y;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Historical most C files have tabs and spaces mix. Please, use only four spaces as indentation in a new code (from line 239: if (TIFFIsTiled(tiff)) to line 294: } else { here).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you want, you can create another PR with tabs to spaces conversion in TiffDecode.c or in the whole codebase.

@hugovk hugovk merged commit d36365f into python-pillow:master Sep 29, 2018
radarhere added a commit to radarhere/Pillow that referenced this pull request Sep 30, 2018
hugovk added a commit that referenced this pull request Sep 30, 2018
kkopachev added a commit to kkopachev/Pillow that referenced this pull request Dec 9, 2018
Old-style JPEG compression in TIFFs are able to be read using Strip/Tile APIs. Although, it should be possible to read them using Scanline API, it does not work for some reason.  Anyway, reading subsampled YCbCr formats through Strip/Tile/Scanline libtiff API does not de-subsample the data, so caller should unpack data to whatever format is appropriate.  New-style JPEG compressed images were already read through libtiff as RGB images (python-pillow#3227). Unfortunately, there is no flag to ask libtiff to de-subsample old jpeg, but it provides a way to read any image as 32bit RGBA.  This commit adds ability to read old-style JPEG TIFFs through reading *all* YCbCr images as RGBX using Tile and Strip reading API. This supersedes previous work (PR python-pillow#3227) to read new-style JPEG-TIFFs.
kkopachev added a commit to kkopachev/Pillow that referenced this pull request Dec 10, 2018
Old-style JPEG compression in TIFFs are able to be read using Strip/Tile APIs. Although, it should be possible to read them using Scanline API, it does not work for some reason.  Anyway, reading subsampled YCbCr formats through Strip/Tile/Scanline libtiff API does not de-subsample the data, so caller should unpack data to whatever format is appropriate.  New-style JPEG compressed images were already read through libtiff as RGB images (python-pillow#3227). Unfortunately, there is no flag to ask libtiff to de-subsample old jpeg, but it provides a way to read any image as 32bit RGBA.  This commit adds ability to read old-style JPEG TIFFs through reading *all* YCbCr images as RGBX using Tile and Strip reading API. This supersedes previous work (PR python-pillow#3227) to read new-style JPEG-TIFFs.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants