FajarHidaa commited on
Commit
e7646eb
·
verified ·
1 Parent(s): d106e1b

Create image_utils.py

Browse files
Files changed (1) hide show
  1. image_utils.py +42 -0
image_utils.py ADDED
@@ -0,0 +1,42 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import base64
2
+ import io
3
+ import logging
4
+ from typing import Optional, Tuple
5
+
6
+ from PIL import Image, ImageOps
7
+
8
+ logger = logging.getLogger(__name__)
9
+
10
+ MAX_SIDE = 768 # sisi terpanjang setelah resize
11
+ JPEG_QUALITIES = (72, 60, 50)
12
+ MAX_OUTPUT_BYTES = 200_000
13
+ MAX_PIXELS = 40_000_000 # tolak gambar raksasa (decompression bomb)
14
+ Image.MAX_IMAGE_PIXELS = MAX_PIXELS
15
+
16
+
17
+ def compress_image_to_data_url(path: Optional[str]) -> Tuple[Optional[str], str]:
18
+ """Return (data_url, error). Semua di memori; tidak ada file baru ditulis."""
19
+ if not path:
20
+ return None, ""
21
+ try:
22
+ with Image.open(path) as src:
23
+ if src.width * src.height > MAX_PIXELS:
24
+ return None, "Resolusi foto terlalu besar."
25
+ src.draft("RGB", (MAX_SIDE * 2, MAX_SIDE * 2)) # percepat decode JPEG
26
+ img = ImageOps.exif_transpose(src) # perbaiki rotasi HP
27
+ img = img.convert("RGB")
28
+ img.thumbnail((MAX_SIDE, MAX_SIDE), Image.Resampling.LANCZOS)
29
+
30
+ data = b""
31
+ for q in JPEG_QUALITIES:
32
+ buf = io.BytesIO()
33
+ img.save(buf, "JPEG", quality=q, optimize=True) # EXIF/GPS tidak ikut
34
+ data = buf.getvalue()
35
+ if len(data) <= MAX_OUTPUT_BYTES:
36
+ break
37
+
38
+ b64 = base64.b64encode(data).decode("ascii")
39
+ return f"data:image/jpeg;base64,{b64}", ""
40
+ except Exception as e:
41
+ logger.warning(f"Image compress failed: {e}")
42
+ return None, "Foto tidak dapat diproses."