-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathimage_processor.py
More file actions
95 lines (85 loc) · 3.81 KB
/
Copy pathimage_processor.py
File metadata and controls
95 lines (85 loc) · 3.81 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
import os
from PIL import Image
from wand.image import Image as WandImage
import io
from typing import BinaryIO, Tuple, Optional
class ImageProcessor:
@staticmethod
def is_animated_gif(file_storage) -> bool:
"""Check if the image is an animated GIF"""
try:
# Save current position
pos = file_storage.tell()
# Go to beginning
file_storage.seek(0)
with Image.open(file_storage) as img:
try:
img.seek(1) # Try to move to the second frame
is_animated = True
except EOFError:
is_animated = False
# Restore position
file_storage.seek(pos)
return is_animated
except Exception:
# Restore position in case of error
file_storage.seek(pos)
return False
@staticmethod
def convert_to_webp(file_storage, quality: int = 90) -> Tuple[BinaryIO, str]:
"""
Convert an image to WebP format.
Returns a tuple of (file_object, extension)
"""
# Save current position
pos = file_storage.tell()
# Go to beginning
file_storage.seek(0)
try:
# Check if it's an animated GIF
if ImageProcessor.is_animated_gif(file_storage):
# Convert animated GIF to animated WebP
file_storage.seek(0)
with WandImage(file=file_storage) as img:
# Configure WebP animation settings
img.format = 'WEBP'
# Higher quality settings for animation
img.options['webp:lossless'] = 'true' # Use lossless for animations
img.options['webp:method'] = '6' # Best compression method
img.options['webp:image-hint'] = 'graph' # Better for animations
img.options['webp:minimize-size'] = 'false' # Prioritize quality
# Animation specific settings
img.options['webp:animation-type'] = 'default'
img.options['webp:loop'] = '0' # Infinite loop
# Save with high quality
webp_bytes = io.BytesIO(img.make_blob(format='webp'))
webp_bytes.seek(0)
return webp_bytes, '.webp'
else:
# Handle static images
file_storage.seek(0)
with Image.open(file_storage) as img:
# Convert RGBA to RGB if necessary
if img.mode in ('RGBA', 'LA'):
background = Image.new('RGB', img.size, (255, 255, 255))
background.paste(img, mask=img.getchannel('A'))
img = background
elif img.mode != 'RGB':
img = img.convert('RGB')
# Save as WebP with high quality
output = io.BytesIO()
img.save(output,
format='WEBP',
quality=quality, # Higher quality
method=6, # Best compression method
lossless=False, # Use lossy for static images
exact=True) # Preserve color exactness
output.seek(0)
return output, '.webp'
finally:
# Restore original position
file_storage.seek(pos)
@staticmethod
def process_featured_image(file_storage) -> Tuple[BinaryIO, str]:
"""Process featured image, converting to WebP format"""
return ImageProcessor.convert_to_webp(file_storage, quality=90)