File size: 3,587 Bytes
6dfbcaa ae1988a 6dfbcaa ae1988a 6dfbcaa ae1988a 6dfbcaa ae1988a 6dfbcaa ae1988a 6dfbcaa ae1988a 6dfbcaa ae1988a 6dfbcaa ae1988a 6dfbcaa ae1988a 6dfbcaa ae1988a 6dfbcaa ae1988a 6dfbcaa ae1988a 6dfbcaa ae1988a 6dfbcaa ae1988a 6dfbcaa ae1988a 6dfbcaa |
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 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 |
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()
|