Skip to content

Commit 4fb590e

Browse files
committed
Add AVIF/HEIF decoding support and update requirements for pillow-heif
1 parent 0a88a67 commit 4fb590e

3 files changed

Lines changed: 50 additions & 0 deletions

File tree

‎.vscode/settings.json‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"github-actions.workflows.pinned.workflows": [
3+
".github/workflows/build-image.yml"
4+
]
5+
}

‎if_rest/core/utils/image_provider.py‎

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,17 @@
3333
else:
3434
jpeg = TurboJPEG()
3535

36+
# Register Pillow openers for formats TurboJPEG/OpenCV can't decode (AVIF, HEIF/HEIC).
37+
# register_heif_opener() covers both HEIF and AVIF. Optional dependency: if pillow-heif
38+
# isn't installed we simply skip the fallback and behave as before.
39+
try:
40+
import pillow_heif
41+
42+
pillow_heif.register_heif_opener()
43+
_PILLOW_FALLBACK = True
44+
except Exception:
45+
_PILLOW_FALLBACK = False
46+
3647
settings = Settings()
3748
def_headers = settings.defaults.img_req_headers
3849

@@ -178,6 +189,34 @@ def b64_to_bytes(b64encoded, b64_decode=True, **kwargs):
178189
return __bin, None
179190

180191

192+
def decode_with_pillow(im_bytes):
193+
"""
194+
Fallback decoder for formats TurboJPEG and OpenCV can't handle (e.g. AVIF, HEIF/HEIC).
195+
Requires pillow-heif; returns None if it's unavailable or decoding fails.
196+
197+
Args:
198+
im_bytes (np.ndarray): The image bytes to be decoded.
199+
200+
Returns:
201+
np.ndarray: The decoded image in BGR order, or None on failure.
202+
"""
203+
if not _PILLOW_FALLBACK:
204+
return None
205+
try:
206+
from PIL import Image, ImageOps
207+
208+
raw = im_bytes.tobytes() if isinstance(im_bytes, np.ndarray) else im_bytes
209+
with Image.open(io.BytesIO(raw)) as img:
210+
img = ImageOps.exif_transpose(img) # honor EXIF orientation
211+
img = img.convert('RGB')
212+
arr = np.asarray(img)
213+
# Convert RGB -> BGR to match TurboJPEG/cv2 output ordering.
214+
return cv2.cvtColor(arr, cv2.COLOR_RGB2BGR)
215+
except Exception:
216+
logger.debug('Pillow decode failed', exc_info=True)
217+
return None
218+
219+
181220
def decode_img_bytes(im_bytes, **kwargs):
182221
"""
183222
Decodes the image bytes and returns it as a numpy array.
@@ -197,6 +236,9 @@ def decode_img_bytes(im_bytes, **kwargs):
197236
except:
198237
logger.debug('JPEG decoder failed, fallback to cv2.imdecode')
199238
_image = cv2.imdecode(im_bytes, cv2.IMREAD_COLOR)
239+
if _image is None:
240+
logger.debug('cv2.imdecode failed, fallback to Pillow (AVIF/HEIF)')
241+
_image = decode_with_pillow(im_bytes)
200242
t1 = time.perf_counter()
201243
logger.debug(f'Decoding took: {(t1 - t0) * 1000:.3f} ms.')
202244
return _image

‎requirements.txt‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@ tritonclient[http,grpc]
2525
# transitive dep of tritonclient; >=1.21 has no py3.8 wheels (source build needs g++)
2626
python-rapidjson<=1.20
2727
imageio
28+
# AVIF/HEIF decode fallback for decode_img_bytes (image proxies serving image/avif,
29+
# iPhone HEIC uploads). register_heif_opener() covers both formats.
30+
pillow-heif
2831
aiohttp
2932
tenacity
3033
colorlog

0 commit comments

Comments
 (0)