12 days ago
# Keeps 589 bright, boosts jewelry shine, replaces background, and maps to Casinofi duotone.
import cv2, numpy as np
from google.colab import files
from PIL import Image
# Upload your image when prompted
up = files.upload()
fn = list(up.keys())[0]
img_bgr = cv2.imdecode(np.frombuffer(up[fn], np.uint8), cv2.IMREAD_COLOR)
# --- Palette (BGR) ---
HEX = lambda h: (int(h[5:7],16), int(h[3:5],16), int(h[1:3],16))
SHADOW = np.array(HEX("#0F1011"), np.float32)
MID = np.array(HEX("#8E7A55"), np.float32)
HILITE = np.array(HEX("#E6D2A1"), np.float32)
HILITE_PLUS = np.array(HEX("#EBDDB7"), np.float32) # extra-bright cream for 589
# --- Helper: gradient map (shadow -> mid -> highlight) ---
def gradient_map(gray01):
g = gray01[...,None]
t1 = np.clip(g/0.5, 0, 1)
t2 = np.clip((g-0.5)/0.5, 0, 1)
low = SHADOW*(1-t1) + MID*t1
high = MID*(1-t2) + HILITE*t2
return np.where(g<=0.5, low, high)
hsv = cv2.cvtColor(img_bgr, cv2.COLOR_BGR2HSV)
# --- Masks ---
# Background (yellow) range
bg_mask = cv2.inRange(hsv, (15, 120, 120), (40, 255, 255)) # tune if needed
# Shirt (blue) range – helpful for separate contrast if you want
shirt_mask = cv2.inRange(hsv, (95, 80, 40), (130, 255, 255))
# Numbers “589” (white-ish areas on shirt)
gray = cv2.cvtColor(img_bgr, cv2.COLOR_BGR2GRAY)
num_mask = cv2.threshold(gray, 210, 255, cv2.THRESH_BINARY)[1] # bright white
# Jewelry (gold/yellow highlights)
jew_mask = cv2.inRange(hsv, (12, 60, 120), (30, 255, 255)) # gold tones
# Clean masks a bit
def clean(m, k=3):
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (k,k))
m = cv2.morphologyEx(m, cv2.MORPH_OPEN, kernel, iterations=1)
m = cv2.morphologyEx(m, cv2.MORPH_CLOSE, kernel, iterations=1)
return m
bg_mask = clean(bg_mask, 5)
shirt_mask= clean(shirt_mask, 5)
num_mask = clean(num_mask, 3)
jew_mask = clean(jew_mask, 3)
# --- Step 1: Replace background with deep charcoal ---
out = img_bgr.copy()
out[bg_mask>0] = SHADOW
# --- Step 2: Convert subject to Casinofi duotone ---
# Work on non-background regions
subj = out.copy()
subj_mask = (bg_mask==0).astype(np.uint8)*255
subj_gray = cv2.cvtColor(subj, cv2.COLOR_BGR2GRAY).astype(np.float32)/255.0
mapped = gradient_map(subj_gray).astype(np.uint8)
mapped = cv2.bitwise_and(mapped, mapped, mask=subj_mask)
bg_area = cv2.bitwise_and(out, out, mask=bg_mask)
out = cv2.add(mapped, bg_area)
# --- Step 3: Boost numbers “589” to brighter cream and keep edges crisp ---
num_rgb = np.zeros_like(out, dtype=np.uint8)
num_rgb[:] = HILITE_PLUS
num_layer = cv2.bitwise_and(num_rgb, num_rgb, mask=num_mask)
out = cv2.bitwise_and(out, out, mask=cv2.bitwise_not(num_mask))
out = cv2.add(out, num_layer)
# Optional: thin dark stroke around numbers
edges = cv2.Canny(num_mask, 50, 150)
stroke = cv2.dilate(edges, np.ones((2,2), np.uint8), iterations=1)
out[stroke>0] = (out[stroke>0]*0 + SHADOW*0.9).astype(np.uint8)
# --- Step 4: Jewelry shine (Screen-like brighten in cream) ---
# Create a cream layer and blend additively where jewelry mask is
j_layer = np.zeros_like(out, dtype=np.float32)
j_layer[:] = HILITE
j_mask_f = (jew_mask.astype(np.float32)/255.0)[...,None]
out_f = out.astype(np.float32)
out = np.clip(out_f + j_layer*0.35*j_mask_f, 0, 255).astype(np.uint8)
# --- Step 5: Gentle contrast pop on subject only ---
subj_mask3 = cv2.merge([subj_mask, subj_mask, subj_mask])
subj_pix = np.where(subj_mask3>0)
sub = out.astype(np.float32)
sub[subj_pix] = np.clip((sub[subj_pix]-20)*1.08 + 20, 0, 255)
out = sub.astype(np.uint8)
# Save
cv2.imwrite("output_casinofi.png", out)
files.download("output_casinofi.png")
print("Done. Download output_casinofi.png")