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

Fix thumbnail geometry when DCT scaling is used #4231

Merged
merged 16 commits into from Dec 25, 2019

Conversation

homm
Copy link
Member

@homm homm commented Nov 24, 2019

Image.thumbnail() method uses .draft(None, size) which could scale down an image with an integer ratio. The problem is if the original image size is not dividable by this ratio, the last line or row of the resulting image gets higher weight.

This could be fixed by using the box argument for resizing. For example, if the original image was 257×257 pixels and we want 30×30. The image will be scaled down 8 times to 33×33 and then resized to 30×30. But 257 / 8 = 32.125, not 33. Now using box we can extract an interesting region (0, 0, 32.125, 32.125).

im = Image.new('RGB', (257, 257), 'red')
im.paste(Image.new('RGB', (255, 255), 'white'), (1, 1))
 
thumb = fromstring(tostring(im, "JPEG", quality=95))
thumb.thumbnail((30, 30), Image.BICUBIC)
thumb.save('_out.thumb.png')
 
im.resize((30, 30), Image.BICUBIC).save('_out.ref.png')

Thumbnail, fixed version, reference without DCT scaling:

_out thumb _out thumb _out ref

Thumbnail, fixed version, reference without DCT scaling:

_out thumb _out fixed _out resize

@homm homm force-pushed the box-in-thumbnail branch 2 times, most recently from c8ac981 to 091aa4d Compare November 24, 2019 12:26
@@ -409,7 +409,8 @@ def draft(self, mode, size):
return

d, e, o, a = self.tile[0]
scale = 0
scale = 1
Copy link
Member Author

Choose a reason for hiding this comment

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

Both values are not handled: https://github.com/python-pillow/Pillow/blob/master/src/libImaging/JpegDecode.c#L238

While 1 is more correct.

@@ -432,7 +433,8 @@ def draft(self, mode, size):
self.tile = [(d, e, o, a)]
self.decoderconfig = (scale, 0)

return self
Copy link
Member Author

Choose a reason for hiding this comment

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

This is breaking change. Previously, draft() could return None or self. This is very strange behavior, I don't think anyone actually used it. Now draft() returns the results of its work or None.

I'll add this in docs.

src/PIL/Image.py Outdated Show resolved Hide resolved
src/PIL/Image.py Outdated Show resolved Hide resolved

if self.size != size:
im = self.resize(size, resample)
im = self.resize(size, resample, box=box)
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
im = self.resize(size, resample, box=box)
im = self.resize(size, resample, box)

I suggest not specifying the argument name - it's not necessary, and the variable name already clearly indicates the purpose.

Copy link
Member Author

Choose a reason for hiding this comment

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

In truth, I think this is a case where we could use Keyword-Only Arguments.

It seems obvious that we can omit the argument name when you watch on it and so you know that it exists and what exactly it is doing. But once you omit it, you can't know exactly where your variable goes.

# Conflicts:
#	Tests/test_image_thumbnail.py
@homm
Copy link
Member Author

homm commented Dec 16, 2019

Rebased

@homm homm mentioned this pull request Dec 17, 2019
3 tasks
# Conflicts:
#	docs/releasenotes/7.0.0.rst
@@ -58,6 +58,20 @@ and :py:meth:`~PIL.ImageOps.fit`` functions.
See :ref:`concept-filters` to learn the difference. In short,
``Image.NEAREST`` is a very fast filter, but simple and low-quality.

Image.draft() return value
Copy link
Member Author

Choose a reason for hiding this comment

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

@radarhere Does this description look good for you?

Copy link
Member

Choose a reason for hiding this comment

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

I've created uploadcare#60 with my suggestion.

@homm homm merged commit b5d06ba into python-pillow:master Dec 25, 2019
@homm homm deleted the box-in-thumbnail branch January 14, 2020 14:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants