Skip to content

Commit 15f094e

Browse files
committed
moving add_thumbnails method to thumbnails file
1 parent 618ae9e commit 15f094e

File tree

7 files changed

+108
-68
lines changed

7 files changed

+108
-68
lines changed

pillow_heif/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,4 +43,4 @@
4343
read_heif,
4444
)
4545
from .misc import get_file_mimetype, getxmp, set_orientation
46-
from .thumbnails import thumbnail
46+
from .thumbnails import add_thumbnails, thumbnail

pillow_heif/heif.py

Lines changed: 0 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -348,38 +348,6 @@ def scale(self, width: int, height: int):
348348
self._img_to_img_data_dict(scaled_heif_img)
349349
return self
350350

351-
def add_thumbnails(self, boxes: Union[List[int], int]) -> None:
352-
"""Add thumbnail(s) to an image.
353-
354-
.. note:: Method creates thumbnails without image data, they will be encoded during `save` operation.
355-
356-
:param boxes: int or list of ints determining size of thumbnail(s) to generate for image.
357-
358-
:returns: None"""
359-
360-
if isinstance(boxes, list):
361-
boxes_list = boxes
362-
else:
363-
boxes_list = [boxes]
364-
self.load()
365-
for box in boxes_list:
366-
if box <= 3:
367-
continue
368-
if self.size[0] <= box and self.size[1] <= box:
369-
continue
370-
if self.size[0] > self.size[1]:
371-
thumb_height = int(self.size[1] * box / self.size[0])
372-
thumb_width = box
373-
else:
374-
thumb_width = int(self.size[0] * box / self.size[1])
375-
thumb_height = box
376-
thumb_height = thumb_height - 1 if (thumb_height & 1) else thumb_height
377-
thumb_width = thumb_width - 1 if (thumb_width & 1) else thumb_width
378-
if max((thumb_height, thumb_width)) in [max(i.size) for i in self.thumbnails]:
379-
continue
380-
__heif_ctx = HeifCtxAsDict(self.mode, (thumb_width, thumb_height), None, stride=0)
381-
self.thumbnails.append(HeifThumbnail(__heif_ctx, self))
382-
383351
def copy_thumbnails(self, thumbnails: List[HeifThumbnail]):
384352
"""Private. For use only in ``add_from_pillow`` and ``add_from_heif``."""
385353

@@ -634,15 +602,6 @@ def add_from_heif(self, heif_image, load_one=False, ignore_primary=True, **kwarg
634602
break
635603
return self
636604

637-
def add_thumbnails(self, boxes: Union[List[int], int]) -> None:
638-
"""Add thumbnail(s) to the primary image.
639-
640-
:param boxes: int or list of ints determining size of thumbnail(s) to generate.
641-
642-
:returns: None"""
643-
644-
self.images[self.primary_index()].add_thumbnails(boxes)
645-
646605
def save(self, fp, **kwargs) -> None:
647606
"""Saves image under the given fp.
648607

pillow_heif/thumbnails.py

Lines changed: 55 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,12 @@
11
"""Functions for work with thumbnails."""
22

3+
from typing import List, Union
4+
5+
from PIL import Image
6+
37
from .as_opener import HeifImageFile
4-
from .heif import HeifFile, HeifImage
8+
from .heif import HeifFile, HeifImage, HeifThumbnail
9+
from .private import HeifCtxAsDict
510

611

712
def thumbnail(im, min_box: int = 0):
@@ -28,6 +33,55 @@ def thumbnail(im, min_box: int = 0):
2833
return thumb if thumb else im
2934

3035

36+
def add_thumbnails(im: Union[HeifFile, HeifImage, Image.Image], boxes: Union[List[int], int]) -> None:
37+
"""Add thumbnail(s) to an image(s).
38+
39+
.. note:: Method creates thumbnails without image data, they will be encoded during `save` operation.
40+
41+
:param im: For ``HeifFile`` will add thumbnail(s) to all images in it.
42+
For ``HeifImage`` or a Pillow image it will add thumbnails only to specified image.
43+
:param boxes: int or list of ints determining size of thumbnail(s) to generate for image.
44+
45+
:returns: None"""
46+
47+
if isinstance(im, HeifFile):
48+
for i in im:
49+
_add_thumbnails(i, boxes)
50+
else:
51+
_add_thumbnails(im, boxes)
52+
53+
54+
def _add_thumbnails(im: Union[HeifImage, Image.Image], boxes: Union[List[int], int]) -> None:
55+
if isinstance(boxes, list):
56+
boxes_list = boxes
57+
else:
58+
boxes_list = [boxes]
59+
for box in boxes_list:
60+
if box <= 3:
61+
continue
62+
if im.size[0] <= box and im.size[1] <= box:
63+
continue
64+
if im.size[0] > im.size[1]:
65+
thumb_height = int(im.size[1] * box / im.size[0])
66+
thumb_width = box
67+
else:
68+
thumb_width = int(im.size[0] * box / im.size[1])
69+
thumb_height = box
70+
thumb_height = thumb_height - 1 if (thumb_height & 1) else thumb_height
71+
thumb_width = thumb_width - 1 if (thumb_width & 1) else thumb_width
72+
thumbnails = im.thumbnails if isinstance(im, HeifImage) else im.info.get("thumbnails", [])
73+
if max((thumb_height, thumb_width)) in [max(i.size) for i in thumbnails]:
74+
continue
75+
__heif_ctx = HeifCtxAsDict(im.mode, (thumb_width, thumb_height), None, stride=0)
76+
if isinstance(im, HeifImage):
77+
im.thumbnails.append(HeifThumbnail(__heif_ctx, im))
78+
else:
79+
if im.info.get("thumbnails", None) is None:
80+
im.info["thumbnails"] = [HeifThumbnail(__heif_ctx, __heif_ctx)]
81+
else:
82+
im.info["thumbnails"].append(HeifThumbnail(__heif_ctx, __heif_ctx))
83+
84+
3185
def _choose_thumbnail(thumbnails: list, min_box: int):
3286
for thumb in thumbnails:
3387
if thumb.size[0] >= min_box or thumb.size[1] >= min_box:

tests/helpers.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
from PIL import Image, ImageMath, ImageOps
66

7-
from pillow_heif import HeifFile, HeifImage, HeifThumbnail
7+
from pillow_heif import HeifFile, HeifImage, HeifThumbnail, add_thumbnails
88

99
try:
1010
import numpy as np
@@ -128,7 +128,7 @@ def create_heif(size: tuple = None, thumb_boxes: list = None, n_images=1, **kwar
128128
for i in range(n_images):
129129
im_heif.add_from_pillow(Image.effect_mandelbrot(size, (-3, -2.5, 2, 2.5), 100))
130130
size = (int(size[0] / 2), int(size[1] / 2))
131-
im_heif[i].add_thumbnails(thumb_boxes)
131+
add_thumbnails(im_heif[i], thumb_boxes)
132132
heif_buf = BytesIO()
133133
im_heif.save(heif_buf, **kwargs)
134134
return heif_buf

tests/numpy_test.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
# Creating HEIF file in memory with 1 image and 3 thumbnails.
1616
im_pillow = Image.effect_mandelbrot((512, 512), (-3, -2.5, 2, 2.5), 100)
1717
im_heif = pillow_heif.from_pillow(im_pillow)
18-
im_heif.add_thumbnails(boxes=[192, 128, 64])
18+
pillow_heif.add_thumbnails(im_heif, boxes=[192, 128, 64])
1919
heif_buf = BytesIO()
2020
im_heif.save(heif_buf)
2121

tests/opencv_test.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@
88
from helpers import compare_hashes, gradient_rgb_bytes, gradient_rgba_bytes
99
from PIL import Image
1010

11-
from pillow_heif import from_bytes, open_heif, options, register_heif_opener
11+
from pillow_heif import HeifImagePlugin # noqa
12+
from pillow_heif import from_bytes, open_heif, options
1213

1314
if not options().hevc_enc:
1415
pytest.skip("No HEVC encoder.", allow_module_level=True)
@@ -17,7 +18,6 @@
1718
cv2 = pytest.importorskip("cv2", reason="OpenCV not installed")
1819

1920
os.chdir(os.path.dirname(os.path.abspath(__file__)))
20-
register_heif_opener()
2121

2222

2323
@pytest.mark.parametrize("enc_bits", (10, 12))

tests/thumbnails_test.py

Lines changed: 47 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@ def create_thumbnail_heif(size):
1919
_ = Image.effect_mandelbrot(size, (-3, -2.5, 2, 2.5), 100)
2020
im_heif = pillow_heif.from_pillow(_)
2121
im_heif.add_from_pillow(_.crop((0, 0, 256, 256)))
22-
im_heif[0].add_thumbnails(boxes=[128, 64])
23-
im_heif[1].add_thumbnails(boxes=[128, 64])
22+
pillow_heif.add_thumbnails(im_heif[0], boxes=[128, 64])
23+
pillow_heif.add_thumbnails(im_heif[1], boxes=[128, 64])
2424
im_heif.add_from_pillow(_.crop((0, 0, 192, 192)))
2525
_heif_buf = BytesIO()
2626
exif = Image.Exif()
@@ -216,12 +216,16 @@ def test_heif_thumbnail_references():
216216
),
217217
)
218218
@pytest.mark.parametrize("heif_file_buf", (heif_buf, create_thumbnail_heif((317, 311))))
219-
def test_heif_image_add_thumbs(thumbs, expected_after, heif_file_buf):
219+
@pytest.mark.parametrize("method", ("HeifFile", "HeifImage"))
220+
def test_heif_add_thumbs(thumbs, expected_after, heif_file_buf, method):
220221
output = BytesIO()
221222
heif_file = pillow_heif.open_heif(heif_file_buf)
222-
heif_file[0].add_thumbnails(thumbs)
223-
heif_file[1].add_thumbnails(thumbs)
224-
heif_file[2].add_thumbnails(thumbs)
223+
if method == "HeifFile":
224+
pillow_heif.add_thumbnails(heif_file, thumbs)
225+
else:
226+
pillow_heif.add_thumbnails(heif_file[0], thumbs)
227+
pillow_heif.add_thumbnails(heif_file[1], thumbs)
228+
pillow_heif.add_thumbnails(heif_file[2], thumbs)
225229
heif_file.save(output, quality=-1)
226230
out_heif = pillow_heif.open_heif(output)
227231
for i in range(3):
@@ -232,25 +236,48 @@ def test_heif_image_add_thumbs(thumbs, expected_after, heif_file_buf):
232236
@pytest.mark.parametrize(
233237
"thumbs,expected_after",
234238
(
235-
(-1, 2),
236-
(64, 2),
237-
(96, 3),
238-
([96], 3),
239-
([0, 84], 3),
240-
([96, 84], 4),
239+
(-1, [2, 2, 0]),
240+
([0], [2, 2, 0]),
241+
(64, [2, 2, 1]),
242+
([2048], [2, 2, 0]),
243+
(96, [3, 3, 1]),
244+
([0, 84], [3, 3, 1]),
245+
([96, 84], [4, 4, 2]),
241246
),
242247
)
243-
@pytest.mark.parametrize("heif_file_buf", (heif_buf, create_thumbnail_heif((537, 511))))
244-
def test_heif_primary_add_thumbs(thumbs, expected_after, heif_file_buf):
245-
heif_file = pillow_heif.open_heif(heif_file_buf)
246-
heif_file.add_thumbnails(thumbs)
248+
def test_pillow_add_thumbs(thumbs, expected_after):
247249
output = BytesIO()
248-
heif_file.save(output, quality=-1)
250+
im = Image.open(heif_buf)
251+
for frame in ImageSequence.Iterator(im):
252+
pillow_heif.add_thumbnails(frame, thumbs)
253+
im.save(output, format="HEIF", quality=-1, save_all=True)
254+
out_heif = pillow_heif.open_heif(output)
255+
for i in range(3):
256+
assert len(out_heif[i].thumbnails) == expected_after[i]
257+
compare_hashes([out_heif[0].to_pillow(), out_heif[0].thumbnails[0].to_pillow()], hash_size=8, max_difference=4)
258+
259+
260+
@pytest.mark.parametrize(
261+
"thumbs,expected_after",
262+
(
263+
(-1, 0),
264+
([0], 0),
265+
(64, 1),
266+
([2048], 0),
267+
([0, 128], 1),
268+
([64, 128], 2),
269+
),
270+
)
271+
def test_pillow_add_thumbs_empty_info(thumbs, expected_after):
272+
output = BytesIO()
273+
im = Image.open(heif_buf)
274+
im.info.pop("thumbnails")
275+
pillow_heif.add_thumbnails(im, thumbs)
276+
im.save(output, format="HEIF", quality=-1)
249277
out_heif = pillow_heif.open_heif(output)
250-
assert len(out_heif[0].thumbnails) == len(heif_file[0].thumbnails)
251-
assert len(out_heif[1].thumbnails) == expected_after
252278
assert len(out_heif.thumbnails) == expected_after
253-
assert len(out_heif[2].thumbnails) == len(heif_file[2].thumbnails)
279+
if expected_after >= 1:
280+
compare_hashes([out_heif[0].to_pillow(), out_heif.thumbnails[0].to_pillow()], hash_size=8)
254281

255282

256283
def test_heif_remove_thumbs():

0 commit comments

Comments
 (0)