-
-
Notifications
You must be signed in to change notification settings - Fork 2.2k
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
Conversation
(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"), |
There was a problem hiding this comment.
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.
src/PIL/TiffImagePlugin.py
Outdated
(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"), |
There was a problem hiding this comment.
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).
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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! )
src/PIL/TiffImagePlugin.py
Outdated
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)), |
There was a problem hiding this comment.
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.
d3c8550
to
d11d8ca
Compare
any update on this? |
2380f0a
to
66459df
Compare
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. |
Tests pass and I think this is all ready to go now. Please review. |
This PR should presumably also change https://github.com/python-pillow/Pillow/blob/c2189235afc3fdc36cb781ee6/src/PIL/TiffTags.py#L441? |
@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. |
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'? |
@radarhere fixed. |
@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 |
I back-merged master into this PR and dropped |
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: It is better to drop all extra RGB → RGB modes for PhotoInterpretation=6 (former YCbCrX, YCbCrXX and YCbCrXXX). |
Could you also drop them from unpack.c? |
@homm @radarhere all done here. Please take a look. |
@radarhere I did rebase. Let me know, if you want commits squashed. |
Tests/test_lib_pack.py
Outdated
@@ -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( |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
added them back.
src/PIL/TiffImagePlugin.py
Outdated
if fillorder == 2: | ||
key = ( | ||
self.tag_v2.prefix, photo, sampleFormat, 1, | ||
self.tag_v2.get(BITSPERSAMPLE, (1,)), |
There was a problem hiding this comment.
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)
src/PIL/TiffImagePlugin.py
Outdated
(x, y, x+w, y+h), | ||
o, a)) | ||
(x, y, min(x+w, xsize), min(y+h, ysize)), | ||
offset, a)) |
There was a problem hiding this comment.
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.
src/libImaging/TiffDecode.c
Outdated
// 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; |
There was a problem hiding this comment.
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).
There was a problem hiding this comment.
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.
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.
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.
Fixes #2926 and #3044 probably #3195 and #3206.
Changes proposed in this pull request:
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