← back to Corkwallcovering
scripts/crop_logo_header.py
78 lines
#!/usr/bin/env python3
"""
Detect & crop the PHILLIPE ROMANO logo-header banner off the TOP of cork product
images (corkwallcovering.com).
The banner is a solid BLACK horizontal band flush to the top of the image,
carrying the white/gold wordmark "PHILLIPE ROMANO / WALLPAPER - FABRICS -
LOS ANGELES". It is visually unmistakable: the very top rows are ~100% black
pixels (text sits inside the black field), and the band ends with a SHARP
transition to bright cork texture.
Signal (per-row, top region only):
dark_frac[r] = fraction of pixels in row r that are near-black (gray < DARK_T).
- A banner requires the very top to be a near-solid black band
(mean dark_frac over the first few rows >= TOP_REQ).
- The band bottom = first row, after the band, where dark_frac collapses to
~0 (cork texture) and STAYS low for a sustained run.
Safety:
- Band must be within [MIN_FRAC, MAX_FRAC] of height, else NO crop.
- If the top is not a solid black band, NO crop (image left untouched).
- Crop-only, never upscale. Add a tiny SAFETY_PAD downward so we never clip
into the product (errs toward leaving a sliver of band rather than product).
"""
import sys, numpy as np
from PIL import Image
DARK_T = 60 # pixel is "dark" if gray < this
TOP_REQ = 0.85 # first TOP_ROWS must average >= this dark_frac to be a banner
TOP_ROWS_F = 0.015 # fraction of height used as the "very top" probe (min 3 rows)
LOW_T = 0.15 # dark_frac considered "product texture" below this
RUN = 10 # sustained low rows required to confirm band end
MIN_FRAC = 0.04
MAX_FRAC = 0.30
SAFETY_PAD = 0 # extra rows kept below boundary (transition row is product)
def detect_band(path):
im = Image.open(path).convert('RGB')
a = np.asarray(im).astype(float)
h, w, _ = a.shape
gray = a.mean(axis=2)
df = (gray < DARK_T).mean(axis=1)
top_rows = max(3, int(h * TOP_ROWS_F))
if df[:top_rows].mean() < TOP_REQ:
return None, dict(reason='top is not a solid black banner',
top_darkfrac=round(float(df[:top_rows].mean()), 3))
# Walk down to find sustained product texture (band end).
boundary = None
limit = int(h * MAX_FRAC) + 1
for r in range(top_rows, limit):
if df[r] < LOW_T and (df[r:r+RUN] < LOW_T).all():
boundary = r
break
if boundary is None:
return None, dict(reason='no sharp band->texture transition within max_frac',
top_darkfrac=round(float(df[:top_rows].mean()), 3))
boundary = min(boundary + SAFETY_PAD, int(h * MAX_FRAC))
frac = boundary / h
if not (MIN_FRAC <= frac <= MAX_FRAC):
return None, dict(reason=f'band {frac:.2%} out of range', boundary=boundary)
return boundary, dict(frac=round(frac, 4),
top_darkfrac=round(float(df[:top_rows].mean()), 3))
def crop(path, out, boundary):
im = Image.open(path)
w, h = im.size
im.crop((0, boundary, w, h)).save(out, quality=92)
if __name__ == '__main__':
p = sys.argv[1]
b, info = detect_band(p)
print(p, '-> BAND row', b, info)
if b and len(sys.argv) > 2:
crop(p, sys.argv[2], b)
print('cropped ->', sys.argv[2])