0.3MP / app.py
sayedM's picture
Update app.py
ae1988a verified
import io
import datetime
import gradio as gr
from PIL import Image, ImageFilter, ImageDraw, ImageFont
import numpy as np
def modern_to_nokia_pil(
img,
jpeg_quality=15,
noise_std=12,
color_levels=32,
blur_radius=0.6,
gamma=1.3,
add_date_stamp=True,
):
img = img.convert("RGB")
# --- Crop to 4:3 ---
w, h = img.size
target_ratio = 4 / 3
current_ratio = w / h
if current_ratio > target_ratio:
new_w = int(h * target_ratio)
left = (w - new_w) // 2
img = img.crop((left, 0, left + new_w, h))
else:
new_h = int(w / target_ratio)
top = (h - new_h) // 2
img = img.crop((0, top, w, top + new_h))
# --- Resize to 640×480 ---
img = img.resize((640, 480), resample=Image.BILINEAR)
# --- Blur ---
img = img.filter(ImageFilter.GaussianBlur(radius=blur_radius))
# --- Posterize (reduce color depth) ---
arr = np.array(img, dtype=np.uint8)
step = max(1, 256 // int(color_levels))
arr = (arr // step) * step
img = Image.fromarray(arr, mode="RGB")
# --- Noise ---
arr = np.array(img, dtype=np.int16)
noise = np.random.normal(0, noise_std, arr.shape)
arr = np.clip(arr + noise, 0, 255).astype(np.uint8)
img = Image.fromarray(arr, mode="RGB")
# --- Gamma curve ---
arr = np.array(img, dtype=np.float32) / 255.0
arr = np.power(arr, gamma)
arr = np.clip(arr * 255, 0, 255).astype(np.uint8)
img = Image.fromarray(arr, mode="RGB")
# --- Date stamp (fixed for Pillow >= 10) ---
if add_date_stamp:
draw = ImageDraw.Draw(img)
font = ImageFont.load_default()
text = datetime.datetime.now().strftime("%d-%m-%y %H:%M")
# NEW SAFE SIZE METHOD
bbox = draw.textbbox((0, 0), text, font=font)
text_w = bbox[2] - bbox[0]
text_h = bbox[3] - bbox[1]
margin = 4
x = img.width - text_w - margin
y = img.height - text_h - margin
draw.rectangle(
[x - 2, y - 2, x + text_w + 2, y + text_h + 2],
fill=(0, 0, 0),
)
draw.text((x, y), text, font=font, fill=(255, 255, 0))
# --- JPEG compression ---
buffer = io.BytesIO()
img.save(buffer, format="JPEG", quality=int(jpeg_quality),
optimize=False, progressive=False)
buffer.seek(0)
img = Image.open(buffer)
return img
def convert_fn(image, jpeg_quality, noise_std, color_levels,
blur_radius, gamma, add_date_stamp):
if image is None:
return None
return modern_to_nokia_pil(
image,
jpeg_quality=jpeg_quality,
noise_std=noise_std,
color_levels=color_levels,
blur_radius=blur_radius,
gamma=gamma,
add_date_stamp=add_date_stamp,
)
demo = gr.Interface(
fn=convert_fn,
inputs=[
gr.Image(type="pil", label="Input image"),
gr.Slider(5, 40, value=15, step=1, label="JPEG quality (lower = more Nokia)"),
gr.Slider(0, 40, value=12, step=1, label="Noise amount"),
gr.Slider(2, 64, value=32, step=2, label="Color levels per channel"),
gr.Slider(0.0, 3.0, value=0.6, step=0.1, label="Blur radius"),
gr.Slider(0.5, 2.5, value=1.3, step=0.1, label="Gamma (contrast curve)"),
gr.Checkbox(True, label="Add date stamp"),
],
outputs=gr.Image(type="pil", label="Nokia-style image"),
title="0.3 MP Nokia Camera Filter",
description="Upload a modern photo and turn it into an old-school 0.3 MP Nokia-style photo.",
)
if __name__ == "__main__":
demo.launch()