Thumbnail.

🏳️‍🌈 v2

Description

Successor of ?️‍? v1 (obviously). Made with Chatgpt 5, which came to a much better result, much more quickly. The Python source was executed in Google Colab.

#!/usr/bin/env python3
import numpy as np
from PIL import Image
from datetime import datetime

# ============================================================
# Progress Pride — all 24-bit RGB colors exactly once
#  - Stripes allocated FIRST
#  - Size: 8192x2048 (2:1)
#  - Timestamped filename
# ============================================================

H, W = 2048, 8192
TOTAL = H * W  # 16,777,216

# ------------------------------
# Geometry: stripes + chevron
# ------------------------------
y = np.arange(H, dtype=np.int32)
x = np.arange(W, dtype=np.int32)
Y, X = np.meshgrid(y, x, indexing='ij')

# Six equal-height horizontal stripes (R,O,Y,G,B,V)
stripe_idx = (Y * 6) // H  # 0..5

# Chevron bands as nested 45° "V" shapes from the hoist
y_mid = (H - 1) / 2.0
t = X.astype(np.float32) + np.abs(Y.astype(np.float32) - y_mid)
# Set chevron total width to H so it occupies ~25% of the 2:1 flag width
W_total = float(H)
bands = 5
band_w = W_total / bands

# Order at the hoist: black, brown, light blue, pink, white
chev_levels = [(i * band_w, (i + 1) * band_w) for i in range(bands)]
chev_masks = []
for i in range(bands):
    lo, hi = chev_levels[i]
    m = (t < hi) & (t >= lo)
    chev_masks.append(m)

# Mask for any chevron pixel (overlays stripes)
chev_any = np.zeros((H, W), dtype=bool)
for m in chev_masks:
    chev_any |= m

# Stripe masks excluding chevron overlay
stripe_masks = []
for s in range(6):
    stripe_masks.append((stripe_idx == s) & (~chev_any))

# ------------------------------
# Color pool: all unique RGBs
# ------------------------------
# Deterministic order: r major, then g, then b
r = np.repeat(np.arange(256, dtype=np.uint8), 256 * 256)
g = np.tile(np.repeat(np.arange(256, dtype=np.uint8), 256), 256)
b = np.tile(np.arange(256, dtype=np.uint8), 256 * 256)
colors = np.stack([r, g, b], axis=1)  # (16777216, 3), uint8

# Normalize to [0,1] float32 for HSV computations
rf = colors[:, 0].astype(np.float32) / 255.0
gf = colors[:, 1].astype(np.float32) / 255.0
bf = colors[:, 2].astype(np.float32) / 255.0

# Vectorized RGB->HSV (H in [0,1), S,V in [0,1])
maxc = np.maximum(np.maximum(rf, gf), bf)
minc = np.minimum(np.minimum(rf, gf), bf)
v = maxc
c = maxc - minc
s = np.where(maxc > 0.0, c / np.maximum(maxc, 1e-12), 0.0)

h = np.zeros_like(v, dtype=np.float32)
mask = c > 1e-12
rc = np.zeros_like(v); gc = np.zeros_like(v); bc = np.zeros_like(v)
rc[mask] = (maxc[mask] - rf[mask]) / c[mask]
gc[mask] = (maxc[mask] - gf[mask]) / c[mask]
bc[mask] = (maxc[mask] - bf[mask]) / c[mask]
mr = (mask & (rf >= gf) & (rf >= bf))
mg = (mask & (gf > rf) & (gf >= bf))
mb = (mask & (bf > rf) & (bf > gf))
h[mr] = (bc[mr] - gc[mr]) / 6.0
h[mg] = (2.0 + (rc[mg] - bc[mg])) / 6.0
h[mb] = (4.0 + (gc[mb] - rc[mb])) / 6.0
h = h % 1.0

# Luma (Rec.709), useful for dark/light targeting
luma = 0.2126 * rf + 0.7152 * gf + 0.0722 * bf

# ------------------------------
# Region pixel counts
# ------------------------------
def count(mask: np.ndarray) -> int:
    return int(np.count_nonzero(mask))

region_defs = {
    # Stripes first
    "stripe_red":    stripe_masks[0],
    "stripe_orange": stripe_masks[1],
    "stripe_yellow": stripe_masks[2],
    "stripe_green":  stripe_masks[3],
    "stripe_blue":   stripe_masks[4],
    "stripe_violet": stripe_masks[5],
    # Chevron after
    "chev_black": chev_masks[0],
    "chev_brown": chev_masks[1],
    "chev_lblue": chev_masks[2],
    "chev_pink":  chev_masks[3],
    "chev_white": chev_masks[4],
}
region_names = list(region_defs.keys())
region_sizes = {name: count(region_defs[name]) for name in region_names}
assert sum(region_sizes.values()) == TOTAL, "Masking error: pixel total mismatch."

# ------------------------------
# Selection helpers
# ------------------------------
rng = np.random.default_rng(42)  # deterministic shuffles
unused = np.ones(TOTAL, dtype=bool)  # color availability tracker

def circular_hue_distance(hval, hcenter):
    d = np.abs(hval - hcenter)
    return np.minimum(d, 1.0 - d)

def take_best(k: int, score: np.ndarray) -> np.ndarray:
    avail_idx = np.nonzero(unused)[0]
    if avail_idx.size < k:
        raise RuntimeError("Not enough remaining colors to satisfy allocation.")
    s_local = score[avail_idx]
    pick_local = np.argpartition(s_local, kth=k-1)[:k]
    pick_local = pick_local[np.argsort(s_local[pick_local], kind='mergesort')]
    chosen = avail_idx[pick_local]
    unused[chosen] = False
    return chosen

# ------------------------------
# Scoring per region
# ------------------------------
scores = {}

# Stripe targeting — vibrant, stripe-specific hue centers, good V:
scores["stripe_red"]    = (circular_hue_distance(h,   0/360) * 2.0 + (1.0 - s) + 0.4 * (0.75 - v) ** 2)
scores["stripe_orange"] = (circular_hue_distance(h,  30/360) * 2.0 + (1.0 - s) + 0.4 * (0.75 - v) ** 2)
scores["stripe_yellow"] = (circular_hue_distance(h,  60/360) * 2.0 + (1.0 - s) + 0.4 * (0.90 - v) ** 2)
scores["stripe_green"]  = (circular_hue_distance(h, 120/360) * 2.0 + (1.0 - s) + 0.4 * (0.75 - v) ** 2)
scores["stripe_blue"]   = (circular_hue_distance(h, 240/360) * 2.0 + (1.0 - s) + 0.4 * (0.70 - v) ** 2)
scores["stripe_violet"] = (circular_hue_distance(h, 285/360) * 2.0 + (1.0 - s) + 0.4 * (0.70 - v) ** 2)

# Chevron targeting (from remaining colors after stripes):
scores["chev_black"] = v  # darker is better
scores["chev_brown"] = (circular_hue_distance(h, 30/360) * 2.0
                        + 0.7 * np.abs(s - 0.5)
                        + 3.0 * (v - 0.35) ** 2)
scores["chev_lblue"] = (circular_hue_distance(h, 200/360) * 2.0
                        + 1.2 * (1.0 - s)
                        + 0.6 * (1.0 - v))
scores["chev_pink"]  = (circular_hue_distance(h, 330/360) * 2.0
                        + 1.2 * (1.0 - s)
                        + 0.6 * (1.0 - v))
scores["chev_white"] = (1.0 - v) + s  # bright and desaturated

# ------------------------------
# Allocate colors to regions
# ------------------------------
region_color_indices = {}

# 1) Allocate to stripes FIRST
for key in ["stripe_red", "stripe_orange", "stripe_yellow",
            "stripe_green", "stripe_blue", "stripe_violet"]:
    k = region_sizes[key]
    idx = take_best(k, scores[key])
    region_color_indices[key] = idx

# 2) Allocate to chevron from remaining pool
for key in ["chev_black", "chev_brown", "chev_lblue", "chev_pink", "chev_white"]:
    k = region_sizes[key]
    idx = take_best(k, scores[key])
    region_color_indices[key] = idx

# All colors must be consumed exactly once
assert not np.any(unused), "Not all colors were used exactly once."

# ------------------------------
# Paint the image
# ------------------------------
img = np.empty((H, W, 3), dtype=np.uint8)

# Helper to paint a region with a deterministic shuffle
def paint_region(name, mask):
    pix = np.column_stack(np.nonzero(mask))  # (N, 2)
    order = np.arange(pix.shape[0])
    seed = (abs(hash(name)) % (2**32))
    rng_reg = np.random.default_rng(seed)
    rng_reg.shuffle(order)
    chosen_colors = colors[region_color_indices[name]][order]
    img[pix[:, 0], pix[:, 1]] = chosen_colors

# Stripes underlay
for k in ["stripe_red", "stripe_orange", "stripe_yellow",
          "stripe_green", "stripe_blue", "stripe_violet"]:
    paint_region(k, region_defs[k])

# Chevron overlay
for k in ["chev_black", "chev_brown", "chev_lblue", "chev_pink", "chev_white"]:
    paint_region(k, region_defs[k])

# ------------------------------
# Final verifications
# ------------------------------
flat = img.reshape(-1, 3)
packed = (flat[:, 0].astype(np.uint32) << 16) | (flat[:, 1].astype(np.uint32) << 8) | flat[:, 2].astype(np.uint32)
uniq = np.unique(packed)
assert uniq.size == TOTAL, f"Color uniqueness failed: {uniq.size} != {TOTAL}"

# Optional (heavy) full-set check:
# all24 = (r.astype(np.uint32) << 16) | (g.astype(np.uint32) << 8) | b.astype(np.uint32)
# assert np.array_equal(np.sort(all24), uniq), "Used colors are not the full 24-bit set."

# ------------------------------
# Save with timestamp
# ------------------------------
ts = datetime.now().strftime("%Y%m%d-%H%M%S")
filename = f"progress_pride_allrgb_8192x2048_{ts}.png"
Image.fromarray(img, mode="RGB").save(filename, compress_level=0)
print(f"Done: {filename}")

Author

ACJ
65 entries

Stats

Date
Colors16,777,216
Pixels16,777,216
Dimensions8,192 × 2,048
Bytes50,348,740