Skip to content

Commit bdaf001

Browse files
committed
Add image optimization script for Instagram and required dependencies
- Introduced `instagram_optimize.py` for processing images to fit Instagram formats. - Added support for RAW and HEIC/HEIF formats with optional libraries. - Implemented functions for user input, image loading, color space conversion, and saving JPEGs under size limits. - Created `requirements.txt` to specify dependencies: Pillow, pillow-heif, and rawpy. - Added a new image file `Aksaray-Universitesi-Konferans-Salunu-1.jpg` to the converted directory.
1 parent eda82bc commit bdaf001

6 files changed

Lines changed: 229 additions & 1 deletion

File tree

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
.venv

image-converter/README.md

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,17 @@
11
# Image Converter
22

3-
Bunu GUI ile proje olarak yapıcam. şimdilik ismi dursun
3+
Bunu GUI ile proje olarak yapıcam. şimdilik ismi dursun
4+
5+
İntagram için yapıyım da fotoğrafları dönüştürecek. HEIC, png, raw to jpeg.
6+
7+
notlar:
8+
dikey - 4:5 - 1080x1350
9+
kare - 1:1 - 1080x1080
10+
yatay - 1.91:1 - 1080x566
11+
12+
her zaman jpg, sRGB, max 8mb, %80 kalite instanın yeniden sıkıştırmasını önler
13+
boşlukları nasıl dolduracağını seç veya otomatik doldur.
14+
15+
cd /mnt/DiskD/SOFTWARE/PYTHON/python-code-snippets/image-converter
16+
source ~/.venvs/image-converter/bin/activate
17+
python instagram_optimize.py
109 KB
Loading
1.24 MB
Loading
Lines changed: 210 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,210 @@
1+
"""
2+
Instagram için görsel optimizasyonu.
3+
- Girdi klasoru: convert
4+
- Cikti klasoru: converted
5+
6+
requirements.txt:
7+
Pillow
8+
pillow-heif
9+
rawpy
10+
11+
"""
12+
13+
from __future__ import annotations
14+
15+
import io, sys
16+
from pathlib import Path
17+
from typing import Iterable, Tuple
18+
19+
from PIL import Image, ImageCms, ImageFilter
20+
21+
# RAW destegi icin istege bagli kutuphane
22+
try:
23+
import rawpy
24+
RAW_AVAILABLE = True
25+
except Exception:
26+
RAW_AVAILABLE = False
27+
28+
# HEIC/HEIF destegi icin istege bagli kutuphane
29+
try:
30+
from pillow_heif import register_heif_opener
31+
register_heif_opener()
32+
HEIF_AVAILABLE = True
33+
except Exception: HEIF_AVAILABLE = False
34+
35+
36+
RAW_EXTS = {".arw"}
37+
SUPPORTED_EXTS = {".jpg", ".jpeg", ".png", ".tif", ".tiff", ".bmp", ".webp", ".heic", ".heif"} | RAW_EXTS
38+
39+
40+
def ask_output_format() -> Tuple[int, int]:
41+
"""Kullanicidan hedef Instagram formatini alir ve boyut dondurur."""
42+
print("Hangi Instagram formatini istiyorsunuz?")
43+
print("1) Dikey 4:5 1080x1350")
44+
print("2) Kare 1:1 1080x1080")
45+
print("3) Yatay 1.91:1 1080x566")
46+
print("4) Reels 9:16 1080x1920")
47+
48+
choice = input("Seciminiz (1/2/3/4) [varsayilan: 1]: ").strip()
49+
if not choice: choice = "1"
50+
if choice == "1": return (2160, 2700)
51+
if choice == "2": return (2160, 2160)
52+
if choice == "3": return (2160, 1131)
53+
if choice == "4": return (2160, 3840)
54+
55+
print("Gecersiz secim.")
56+
return ask_output_format()
57+
58+
59+
def ask_background_mode() -> str:
60+
"""Kullanicidan arka plan modunu alir."""
61+
print("Arka plan tercihi nedir?")
62+
print("1) Siyah")
63+
print("2) Beyaz")
64+
print("3) Blur")
65+
66+
choice = input("Seciminiz (1/2/3) [varsayilan: 1]: ").strip()
67+
if not choice: choice = "1"
68+
if choice == "1": return "black"
69+
if choice == "2": return "white"
70+
if choice == "3": return "blur"
71+
72+
print("Gecersiz secim.")
73+
return ask_background_mode()
74+
75+
76+
def iter_images(folder: Path) -> Iterable[Path]:
77+
"""Desteklenen uzantilara sahip dosyalari listeler."""
78+
for path in sorted(folder.iterdir()):
79+
if path.is_file() and path.suffix.lower() in SUPPORTED_EXTS:
80+
yield path
81+
82+
83+
def load_input_image(path: Path) -> Image.Image:
84+
"""Girdiyi PIL Image olarak yukler (RAW dahil)."""
85+
if path.suffix.lower() in RAW_EXTS:
86+
if not RAW_AVAILABLE:
87+
raise RuntimeError("RAW icin 'rawpy' kurulu degil")
88+
with rawpy.imread(str(path)) as raw:
89+
rgb = raw.postprocess(use_camera_wb=True, output_bps=8)
90+
return Image.fromarray(rgb)
91+
92+
with Image.open(path) as img:
93+
return img.copy()
94+
95+
96+
def convert_to_srgb(img: Image.Image) -> Image.Image:
97+
"""Gorseli sRGB renk uzayina cevirir."""
98+
# ICC profili varsa, profileToProfile ile donusum yapilir.
99+
icc_bytes = img.info.get("icc_profile")
100+
if icc_bytes:
101+
try:
102+
src_profile = ImageCms.ImageCmsProfile(io.BytesIO(icc_bytes))
103+
dst_profile = ImageCms.createProfile("sRGB")
104+
return ImageCms.profileToProfile(img, src_profile, dst_profile, outputMode="RGB")
105+
except Exception: pass
106+
107+
# ICC yoksa (veya hata varsa) RGB'ye zorla.
108+
if img.mode != "RGB": return img.convert("RGB")
109+
return img
110+
111+
112+
def ensure_rgb_no_alpha(img: Image.Image) -> Image.Image:
113+
"""JPEG kaydi icin alfa kanali varsa beyaz zemine birlestirir."""
114+
if img.mode in {"RGBA", "LA"}:
115+
background = Image.new("RGB", img.size, (255, 255, 255))
116+
background.paste(img, mask=img.split()[-1])
117+
return background
118+
if img.mode != "RGB": return img.convert("RGB")
119+
return img
120+
121+
122+
def fit_with_background(img: Image.Image, target_w: int, target_h: int, mode: str) -> Image.Image:
123+
"""Orani bozmadan sigdir ve arka planla tamamla."""
124+
src_w, src_h = img.size
125+
scale = min(target_w / src_w, target_h / src_h)
126+
new_w = int(round(src_w * scale))
127+
new_h = int(round(src_h * scale))
128+
129+
resized = img.resize((new_w, new_h), Image.Resampling.LANCZOS)
130+
if mode == "white":
131+
canvas = Image.new("RGB", (target_w, target_h), (255, 255, 255))
132+
elif mode == "blur":
133+
base = img.resize((target_w, target_h), Image.Resampling.LANCZOS)
134+
canvas = base.filter(ImageFilter.GaussianBlur(radius=32))
135+
else:
136+
canvas = Image.new("RGB", (target_w, target_h), (0, 0, 0))
137+
left = (target_w - new_w) // 2
138+
top = (target_h - new_h) // 2
139+
canvas.paste(resized, (left, top))
140+
return canvas
141+
142+
143+
def save_jpeg_under_limit(img: Image.Image, out_path: Path, max_bytes: int = 8 * 1024 * 1024) -> None:
144+
"""JPEG olarak kaydeder; 8MB uzerindeyse kaliteyi dusurur."""
145+
quality = 100
146+
min_quality = 60
147+
148+
while True:
149+
buffer = io.BytesIO()
150+
img.save(buffer, format="JPEG", quality=quality, optimize=True)
151+
size = buffer.tell()
152+
153+
if size <= max_bytes or quality <= min_quality:
154+
out_path.write_bytes(buffer.getvalue())
155+
return
156+
157+
# Boyut fazla ise kaliteyi kademeli dusur.
158+
quality -= 5
159+
160+
161+
def main() -> None:
162+
target_w, target_h = ask_output_format()
163+
bg_mode = ask_background_mode()
164+
165+
base_dir = Path(__file__).resolve().parent
166+
input_dir = base_dir / "convert"
167+
output_dir = base_dir / "converted"
168+
169+
# Klasorler yoksa olustur.
170+
input_dir.mkdir(parents=True, exist_ok=True)
171+
output_dir.mkdir(parents=True, exist_ok=True)
172+
173+
if not HEIF_AVAILABLE:
174+
print("Not: HEIC/HEIF icin 'pillow-heif' kurulu degil, bu dosyalar atlanacak.")
175+
if not RAW_AVAILABLE:
176+
print("Not: RAW (ARW) icin 'rawpy' kurulu degil, bu dosyalar atlanacak.")
177+
178+
images = list(iter_images(input_dir))
179+
if not images:
180+
print(f"Islenecek gorsel bulunamadi: {input_dir}")
181+
return
182+
183+
total = len(images)
184+
for idx, path in enumerate(images, start=1):
185+
if path.suffix.lower() in {".heic", ".heif"} and not HEIF_AVAILABLE:
186+
print(f"{idx}/{total} atlandi (HEIC destegi yok): {path.name}")
187+
continue
188+
if path.suffix.lower() in RAW_EXTS and not RAW_AVAILABLE:
189+
print(f"{idx}/{total} atlandi (RAW destegi yok): {path.name}")
190+
continue
191+
try:
192+
img = load_input_image(path)
193+
# Renk uzayini sRGB'ye donustur.
194+
img = convert_to_srgb(img)
195+
# JPEG icin alfa kanali temizle.
196+
img = ensure_rgb_no_alpha(img)
197+
# Orani bozmadan sigdir ve arka planla tamamla.
198+
img = fit_with_background(img, target_w, target_h, bg_mode)
199+
200+
out_name = path.stem + ".jpg"
201+
out_path = output_dir / out_name
202+
save_jpeg_under_limit(img, out_path)
203+
204+
print(f"{idx}/{total} islendi: {path.name}")
205+
except Exception as exc:
206+
print(f"{idx}/{total} atlandi (hata): {path.name} -> {exc}")
207+
208+
209+
if __name__ == "__main__":
210+
main()

image-converter/requirements.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Pillow
2+
pillow-heif
3+
rawpy

0 commit comments

Comments
 (0)