Spaces:
Runtime error
Runtime error
| """ | |
| Generate Ishihara-style pseudoisochromatic plates using PIL text for the digit mask. | |
| Guaranteed correct digit shapes. | |
| """ | |
| import numpy as np | |
| from PIL import Image, ImageDraw, ImageFont | |
| import random, math | |
| def hex2rgb(h): | |
| h = h.lstrip('#') | |
| return tuple(int(h[i:i+2], 16) for i in (0, 2, 4)) | |
| def jitter_color(rgb, amount=20): | |
| return tuple(max(0, min(255, c + random.randint(-amount, amount))) for c in rgb) | |
| def make_text_mask(number_str, size, font_size=None): | |
| """Render number_str as a clean white-on-black mask using PIL default font.""" | |
| if font_size is None: | |
| font_size = int(size * 0.38) # big β ~212px tall for 560 | |
| mask = Image.new("L", (size, size), 0) | |
| draw = ImageDraw.Draw(mask) | |
| # Try to get a large font; fall back to default if not available | |
| try: | |
| font = ImageFont.truetype("/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf", font_size) | |
| except Exception: | |
| try: | |
| font = ImageFont.truetype("/usr/share/fonts/truetype/liberation/LiberationSans-Bold.ttf", font_size) | |
| except Exception: | |
| font = ImageFont.load_default() | |
| # Get bounding box and centre text | |
| bbox = draw.textbbox((0, 0), number_str, font=font) | |
| tw, th = bbox[2] - bbox[0], bbox[3] - bbox[1] | |
| x = (size - tw) // 2 - bbox[0] | |
| y = (size - th) // 2 - bbox[1] | |
| draw.text((x, y), number_str, fill=255, font=font) | |
| return np.array(mask) > 128 | |
| def generate_plate( | |
| size=560, | |
| number_str="74", | |
| fg_colors=None, | |
| bg_colors=None, | |
| out_path="plate.png", | |
| font_size=None, | |
| ): | |
| rng = random.Random(42) | |
| mask = make_text_mask(number_str, size, font_size=font_size) | |
| img = Image.new("RGB", (size, size), (215, 215, 215)) | |
| draw = ImageDraw.Draw(img) | |
| cx, cy, radius = size // 2, size // 2, size // 2 - 6 | |
| draw.ellipse([cx-radius, cy-radius, cx+radius, cy+radius], fill=(210, 210, 210)) | |
| step = 9 | |
| dot_r_min, dot_r_max = 3, 7 | |
| for gx in range(dot_r_max, size - dot_r_max, step): | |
| for gy in range(dot_r_max, size - dot_r_max, step): | |
| px = gx + rng.randint(-3, 3) | |
| py = gy + rng.randint(-3, 3) | |
| if math.hypot(px - cx, py - cy) > radius - dot_r_max: | |
| continue | |
| r = rng.randint(dot_r_min, dot_r_max) | |
| py_c = max(0, min(size-1, py)) | |
| px_c = max(0, min(size-1, px)) | |
| is_fg = bool(mask[py_c, px_c]) | |
| pool = fg_colors if is_fg else bg_colors | |
| color = hex2rgb(rng.choice(pool)) | |
| color = jitter_color(color, 18) | |
| draw.ellipse([px-r, py-r, px+r, py+r], fill=color) | |
| draw.ellipse([cx-radius, cy-radius, cx+radius, cy+radius], | |
| outline=(150,150,150), width=3) | |
| img.save(out_path) | |
| fg = int(mask.sum()) | |
| print(f" {out_path} number={number_str!r} fg_pixels={fg}") | |
| OUT = "/home/ai24mtech02001/.openclaw/workspace/colorization-webapp/tutorial/colorblind" | |
| print("Generating plates...") | |
| # 1. DEMO β "12" vivid red vs blue, visible to everyone | |
| generate_plate( | |
| number_str="12", | |
| fg_colors=["#C0392B","#E74C3C","#A93226","#B03A2E","#D44333"], | |
| bg_colors=["#2471A3","#1A5276","#2E86C1","#1F618D","#5499C2","#2980B9"], | |
| out_path=f"{OUT}/plate_demo.png", | |
| ) | |
| # 2. RED-GREEN β "74" | |
| # Normal: green pops on orange. Protan/Deutan: both look similar brownish. | |
| generate_plate( | |
| number_str="74", | |
| fg_colors=["#4A7C00","#5B8C00","#6B9E0E","#527A00","#3D6B00","#618500"], | |
| bg_colors=["#C47A1E","#D4892E","#BF6D10","#CC8022","#D99030", | |
| "#B86008","#E09840","#A85500","#CB7818"], | |
| out_path=f"{OUT}/plate_redgreen.png", | |
| ) | |
| # 3. BLUE-YELLOW β "6" | |
| # Normal: yellow pops on blue. Tritanope: similar. | |
| generate_plate( | |
| number_str="6", | |
| fg_colors=["#E8C200","#F0CA00","#D4B000","#DCBA00","#F5D000","#EBC400"], | |
| bg_colors=["#2C3E7A","#1C2D68","#3B4F90","#253580","#162870","#324488","#2A3C82"], | |
| out_path=f"{OUT}/plate_blueyellow.png", | |
| ) | |
| print("Done.") | |