forked from MirrorHub/synapse
media/thumbnailer: Better quality for 1-bit / 8-bit color palette images (#2142)
Pillow will use nearest neighbour as the resampling algorithm if the source image is either 1-bit or a color palette using 8 bits. If we convert to RGB before scaling, we'll probably get a better result.
This commit is contained in:
parent
66537e10ce
commit
39b40d6d99
2 changed files with 12 additions and 3 deletions
1
changelog.d/2142.feature
Normal file
1
changelog.d/2142.feature
Normal file
|
@ -0,0 +1 @@
|
||||||
|
Improve quality of thumbnails for 1-bit/8-bit color palette images.
|
|
@ -82,13 +82,21 @@ class Thumbnailer(object):
|
||||||
else:
|
else:
|
||||||
return (max_height * self.width) // self.height, max_height
|
return (max_height * self.width) // self.height, max_height
|
||||||
|
|
||||||
|
def _resize(self, width, height):
|
||||||
|
# 1-bit or 8-bit color palette images need converting to RGB
|
||||||
|
# otherwise they will be scaled using nearest neighbour which
|
||||||
|
# looks awful
|
||||||
|
if self.image.mode in ["1", "P"]:
|
||||||
|
self.image = self.image.convert("RGB")
|
||||||
|
return self.image.resize((width, height), Image.ANTIALIAS)
|
||||||
|
|
||||||
def scale(self, width, height, output_type):
|
def scale(self, width, height, output_type):
|
||||||
"""Rescales the image to the given dimensions.
|
"""Rescales the image to the given dimensions.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
BytesIO: the bytes of the encoded image ready to be written to disk
|
BytesIO: the bytes of the encoded image ready to be written to disk
|
||||||
"""
|
"""
|
||||||
scaled = self.image.resize((width, height), Image.ANTIALIAS)
|
scaled = self._resize(width, height)
|
||||||
return self._encode_image(scaled, output_type)
|
return self._encode_image(scaled, output_type)
|
||||||
|
|
||||||
def crop(self, width, height, output_type):
|
def crop(self, width, height, output_type):
|
||||||
|
@ -107,13 +115,13 @@ class Thumbnailer(object):
|
||||||
"""
|
"""
|
||||||
if width * self.height > height * self.width:
|
if width * self.height > height * self.width:
|
||||||
scaled_height = (width * self.height) // self.width
|
scaled_height = (width * self.height) // self.width
|
||||||
scaled_image = self.image.resize((width, scaled_height), Image.ANTIALIAS)
|
scaled_image = self._resize(width, scaled_height)
|
||||||
crop_top = (scaled_height - height) // 2
|
crop_top = (scaled_height - height) // 2
|
||||||
crop_bottom = height + crop_top
|
crop_bottom = height + crop_top
|
||||||
cropped = scaled_image.crop((0, crop_top, width, crop_bottom))
|
cropped = scaled_image.crop((0, crop_top, width, crop_bottom))
|
||||||
else:
|
else:
|
||||||
scaled_width = (height * self.width) // self.height
|
scaled_width = (height * self.width) // self.height
|
||||||
scaled_image = self.image.resize((scaled_width, height), Image.ANTIALIAS)
|
scaled_image = self._resize(scaled_width, height)
|
||||||
crop_left = (scaled_width - width) // 2
|
crop_left = (scaled_width - width) // 2
|
||||||
crop_right = width + crop_left
|
crop_right = width + crop_left
|
||||||
cropped = scaled_image.crop((crop_left, 0, crop_right, height))
|
cropped = scaled_image.crop((crop_left, 0, crop_right, height))
|
||||||
|
|
Loading…
Reference in a new issue