I figured it would be an interesting (read: extra nerdy) exercise to put all the official country flags in an image that contains all 24-bit rgb colors exactly once, while keeping the flags recognizable. This is my first attempt, created with Chatgpt 5, Python 3, and a Google Colab notebook. Please feel free to come up with a better version!
# ============================================
# 4096×4096 "All-RGB" World Flags
# - HSVY + palette-aware mapping (bijective: uses every 24-bit color exactly once)
# - Uniform gray background (constant)
# - Background: lowest-chroma Y≈gray colors first
# - Flags: HSVY rank + palette-hue tiebreaker
# - NEW: shuffle only within each flag's individual color fields
# (per-flag quantization labels), NOT whole-flag regions
# - Verifies via checksum + XOR
# ============================================
!pip -q install pillow requests tqdm
import os, math, hashlib
from datetime import datetime
import requests
import numpy as np
from PIL import Image
from tqdm import tqdm
# -----------------------------
# Config
# -----------------------------
CANVAS_SIZE = 4096
INCLUDE_UN_OBSERVERS = True
USE_GOOGLE_DRIVE = False
SAVE_DIR = "/content"
CACHE_DIR_NAME = "flags_cache"
UNIFORM_GRAY = 224 # 0..255 uniform background gray
MARGIN_PX = 4 # inner cell padding
PALETTE_COLORS = 6 # per-flag quantization & palette tiebreak
# -----------------------------
# Optional: mount Drive
# -----------------------------
if USE_GOOGLE_DRIVE:
from google.colab import drive
drive.mount('/content/drive', force_remount=True)
SAVE_DIR = "/content/drive/MyDrive"
ASSETS_DIR = os.path.join(SAVE_DIR, "allRGB", "assets")
os.makedirs(ASSETS_DIR, exist_ok=True)
CACHE_DIR = os.path.join(ASSETS_DIR, CACHE_DIR_NAME)
os.makedirs(CACHE_DIR, exist_ok=True)
# -----------------------------
# REST Countries fetch
# -----------------------------
def fetch_country_list(include_observers=True, timeout=60):
url = "https://restcountries.com/v3.1/all?fields=name,flags,unMember,cca2"
resp = requests.get(url, timeout=timeout)
resp.raise_for_status()
data = resp.json()
wanted = []
observers_set = {"Holy See", "Palestine", "State of Palestine"}
for c in data:
name = (c.get("name") or {}).get("common")
un_member = bool(c.get("unMember"))
flags = c.get("flags") or {}
png = flags.get("png")
cca2 = c.get("cca2")
if not (name and png and cca2):
continue
if un_member:
wanted.append(dict(name=name, png=png, cca2=cca2.lower(), un_member=True))
elif include_observers and name in observers_set:
wanted.append(dict(name=name, png=png, cca2=cca2.lower(), un_member=False))
wanted.sort(key=lambda d: d["name"])
return wanted
def safe_filename(s):
return "".join(ch if ch.isalnum() or ch in "-_." else "_" for ch in s)
def download_flag_png(item, cache_dir=CACHE_DIR, timeout=60):
url = item["png"]
code = item["cca2"]
base = f"{safe_filename(code)}.png"
path = os.path.join(cache_dir, base)
if os.path.exists(path) and os.path.getsize(path) > 0:
return path
try:
r = requests.get(url, timeout=timeout)
r.raise_for_status()
with open(path, "wb") as f:
f.write(r.content)
return path
except Exception:
try:
if "/w320/" in url:
url2 = url.replace("/w320/", "/w640/")
r2 = requests.get(url2, timeout=timeout)
r2.raise_for_status()
with open(path, "wb") as f:
f.write(r2.content)
return path
except Exception:
pass
return None
# -----------------------------
# Grid & mosaic with per-flag label maps
# -----------------------------
def best_grid(n, width=CANVAS_SIZE, height=CANVAS_SIZE):
cols = int(math.ceil(math.sqrt(n)))
rows = int(math.ceil(n / cols))
return rows, cols
def place_flags_mosaic_with_labels(flags, gray=UNIFORM_GRAY, canvas_size=CANVAS_SIZE,
margin=MARGIN_PX, palette_colors=PALETTE_COLORS):
"""
Returns:
mosaic_rgb (RGB)
flag_id_map (H,W) int32: -1 for background, else flag index
per_flag (list of dicts):
- name, cca2
- hues: palette hues (uint8 0..255) for tiebreaker
- paste_x, paste_y
- label_map: (h_resized, w_resized) int16, -1 for transparent,
else palette index 0..(palette_colors-1)
"""
N = len(flags)
rows, cols = best_grid(N, canvas_size, canvas_size)
cell_w = canvas_size // cols
cell_h = canvas_size // rows
pad = (int(gray), int(gray), int(gray))
mosaic = Image.new("RGBA", (canvas_size, canvas_size), pad + (255,))
flag_id_map = np.full((canvas_size, canvas_size), -1, dtype=np.int32)
per_flag = []
i = 0
for r in range(rows):
for c in range(cols):
if i >= N:
break
info = flags[i]
path = info["local"]
try:
flag = Image.open(path).convert("RGBA")
except Exception:
per_flag.append(dict(name=info["name"], cca2=info["cca2"], hues=np.array([], dtype=np.uint8),
paste_x=0, paste_y=0, label_map=np.zeros((0,0), dtype=np.int16)))
i += 1
continue
max_w = max(1, cell_w - 2 * margin)
max_h = max(1, cell_h - 2 * margin)
fw, fh = flag.size
scale = min(max_w / fw, max_h / fh)
new_w = max(1, int(round(fw * scale)))
new_h = max(1, int(round(fh * scale)))
flag_resized = flag.resize((new_w, new_h), Image.LANCZOS)
paste_x = c * cell_w + (cell_w - new_w) // 2
paste_y = r * cell_h + (cell_h - new_h) // 2
# Composite onto mosaic
mosaic.alpha_composite(flag_resized, (paste_x, paste_y))
# Flag-id map (alpha>0)
alpha = np.array(flag_resized.split()[-1], dtype=np.uint8)
mask = alpha > 0
if mask.any():
yy, xx = np.nonzero(mask)
fy = paste_y + yy
fx = paste_x + xx
ok = (fy >= 0) & (fy < canvas_size) & (fx >= 0) & (fx < canvas_size)
flag_id_map[fy[ok], fx[ok]] = i
# Quantize to get per-pixel palette indices
qimg = flag_resized.convert("RGB").quantize(colors=palette_colors, method=Image.MEDIANCUT)
label_map = np.array(qimg, dtype=np.int16) # 0..K-1 indices over full rect
# Set -1 where flag is transparent
label_map[~mask] = -1
# Palette hues for tiebreaker
palette = qimg.getpalette()[:palette_colors*3]
pal = np.array(palette, dtype=np.uint8).reshape(-1, 3)
pr, pg, pb = pal[:,0].astype(np.float32)/255.0, pal[:,1].astype(np.float32)/255.0, pal[:,2].astype(np.float32)/255.0
pmax = np.maximum.reduce([pr, pg, pb])
pmin = np.minimum.reduce([pr, pg, pb])
pc = pmax - pmin
ph = np.zeros_like(pmax, dtype=np.float32)
maskc = pc > 1e-12
rc = np.zeros_like(pmax); gc = np.zeros_like(pmax); bc = np.zeros_like(pmax)
rc[maskc] = (pmax[maskc] - pr[maskc]) / pc[maskc]
gc[maskc] = (pmax[maskc] - pg[maskc]) / pc[maskc]
bc[maskc] = (pmax[maskc] - pb[maskc]) / pc[maskc]
mr = maskc & (pr >= pg) & (pr >= pb)
mg = maskc & (pg > pr) & (pg >= pb)
mb = maskc & (pb > pr) & (pb > pg)
ph[mr] = (bc[mr] - gc[mr]) / 6.0
ph[mg] = (2.0 + (rc[mg] - bc[mg])) / 6.0
ph[mb] = (4.0 + (gc[mb] - rc[mb])) / 6.0
ph = ph % 1.0
pal_h8 = np.floor(ph * 255.0 + 0.5).astype(np.uint8)
per_flag.append(dict(name=info["name"], cca2=info["cca2"], hues=pal_h8,
paste_x=paste_x, paste_y=paste_y, label_map=label_map))
i += 1
return mosaic.convert("RGB"), flag_id_map, per_flag
# -----------------------------
# HSVY helpers
# -----------------------------
def rgb_to_yhc_u8(r, g, b):
r16 = r.astype(np.uint16); g16 = g.astype(np.uint16); b16 = b.astype(np.uint16)
cmax = np.maximum(np.maximum(r16, g16), b16)
cmin = np.minimum(np.minimum(r16, g16), b16)
delta = (cmax - cmin).astype(np.uint8)
Y = ((54 * r16 + 183 * g16 + 19 * b16 + 128) >> 8).astype(np.uint8) # Rec709-ish
rf = r16.astype(np.float32); gf = g16.astype(np.float32); bf = b16.astype(np.float32)
cmaxf = cmax.astype(np.float32); deltaf = (cmax - cmin).astype(np.float32)
hue = np.zeros_like(cmaxf, dtype=np.float32)
nz = deltaf > 0
mask_r = nz & (cmax == r16)
hue[mask_r] = (60.0 * ((gf[mask_r] - bf[mask_r]) / deltaf[mask_r])) % 360.0
mask_g = nz & (cmax == g16)
hue[mask_g] = 60.0 * (2.0 + (bf[mask_g] - rf[mask_g]) / deltaf[mask_g])
mask_b = nz & (cmax == b16)
hue[mask_b] = 60.0 * (4.0 + (rf[mask_b] - gf[mask_b]) / deltaf[mask_b])
hue = np.where(hue < 0, hue + 360.0, hue)
H = np.floor(hue * (255.0 / 360.0) + 0.5).astype(np.uint8)
return Y, H, delta
def make_hsvy_key(Y, H, C):
return (Y.astype(np.uint32) << 24) | (H.astype(np.uint32) << 16) | (C.astype(np.uint32) << 8)
def hue_circ_dist8(a8, b8):
d = np.abs(a8.astype(np.int16) - b8.astype(np.int16)).astype(np.int16)
return np.minimum(d, 256 - d).astype(np.uint8)
def rng_for_label(label: str):
h = hashlib.blake2b(label.encode("utf-8"), digest_size=8).digest()
return np.random.default_rng(int.from_bytes(h, "little"))
# -----------------------------
# Build with background + flags + per-field shuffle
# -----------------------------
def build_allrgb_palette_aware(target_rgb, flag_id_map, per_flag, out_png_path, bg_gray=UNIFORM_GRAY):
W = H = CANVAS_SIZE
assert target_rgb.size == (W, H)
arr = np.array(target_rgb, dtype=np.uint8)
flat = arr.reshape(-1, 3)
tr, tg, tb = flat[:,0], flat[:,1], flat[:,2]
# Background mask
bgR = np.uint8(bg_gray); bgG = np.uint8(bg_gray); bgB = np.uint8(bg_gray)
bg_mask = (tr == bgR) & (tg == bgG) & (tb == bgB)
K = int(bg_mask.sum())
print(f"Background pixels: {K:,d} / {flat.shape[0]:,d}")
# All 24-bit colors
N = 1 << 24
codes = np.arange(N, dtype=np.uint32)
r_all = (codes >> 16).astype(np.uint8)
g_all = ((codes >> 8) & 255).astype(np.uint8)
b_all = (codes & 255).astype(np.uint8)
print("Precomputing Y,H,C for all colors…")
Y_all, H_all, C_all = rgb_to_yhc_u8(r_all, g_all, b_all)
# -------- Phase A: background = lowest-C, Y≈bgGray ----------
print("Selecting lowest-chroma colors for background…")
if K > 0:
idx_lowC = np.argpartition(C_all, K - 1)[:K]
bgY = np.uint8(bg_gray)
Ydiff = np.abs(Y_all[idx_lowC].astype(np.int16) - int(bgY)).astype(np.uint8)
key_bg = (C_all[idx_lowC].astype(np.uint32) << 24) | (Ydiff.astype(np.uint32) << 16) | (H_all[idx_lowC].astype(np.uint32) << 8) | (codes[idx_lowC] & 0xFF)
order_bg = np.argsort(key_bg, kind="stable")
color_idx_bg = idx_lowC[order_bg]
else:
color_idx_bg = np.array([], dtype=np.int64)
px_idx_all = np.arange(flat.shape[0], dtype=np.uint32)
px_idx_bg = px_idx_all[bg_mask]
assert px_idx_bg.size == color_idx_bg.size, "Background selection mismatch."
out_r = np.empty(N, dtype=np.uint8)
out_g = np.empty(N, dtype=np.uint8)
out_b = np.empty(N, dtype=np.uint8)
# Place background colors (we'll shuffle them later within background)
out_r[px_idx_bg] = r_all[color_idx_bg]
out_g[px_idx_bg] = g_all[color_idx_bg]
out_b[px_idx_bg] = b_all[color_idx_bg]
# -------- Phase B: foreground via HSVY + palette-hue tiebreak ----------
remain_mask_pixels = ~bg_mask
px_idx_fg = px_idx_all[remain_mask_pixels]
taken = np.zeros(N, dtype=bool)
taken[color_idx_bg] = True
color_idx_fg = np.flatnonzero(~taken)
del taken
Yt, Ht, Ct = rgb_to_yhc_u8(tr[remain_mask_pixels], tg[remain_mask_pixels], tb[remain_mask_pixels])
tkey = make_hsvy_key(Yt, Ht, Ct)
print("Computing palette-aware tiebreaker…")
flag_ids_flat = flag_id_map.reshape(-1)[remain_mask_pixels]
pal_delta = np.full(flag_ids_flat.shape, 128, dtype=np.uint8)
unique_flags = np.unique(flag_ids_flat)
for fid in unique_flags:
if fid < 0:
continue
hues = per_flag[fid]["hues"]
idx = np.nonzero(flag_ids_flat == fid)[0]
if idx.size == 0 or hues.size == 0:
continue
h_local = Ht[idx]
mind = np.full(idx.size, 255, dtype=np.uint8)
for hp in hues:
d = hue_circ_dist8(h_local, np.uint8(hp))
mind = np.minimum(mind, d)
pal_delta[idx] = mind
tidx = np.arange(px_idx_fg.size, dtype=np.uint32)
target_order = np.lexsort((tidx, pal_delta.astype(np.uint32), tkey))
Yc = Y_all[color_idx_fg]; Hc = H_all[color_idx_fg]; Cc = C_all[color_idx_fg]
ckey = make_hsvy_key(Yc, Hc, Cc)
color_order = np.lexsort((color_idx_fg, ckey))
sorted_px = px_idx_fg[target_order]
sorted_colors = color_idx_fg[color_order]
out_r[sorted_px] = r_all[sorted_colors]
out_g[sorted_px] = g_all[sorted_colors]
out_b[sorted_px] = b_all[sorted_colors]
# -------- NEW: shuffle within color fields (not whole flags) ----------
print("Shuffling within individual color fields of each flag…")
# Background region shuffle (all background as one region)
def shuffle_region(px_idx, label):
if px_idx.size <= 1:
return
rng = rng_for_label(label)
order = np.arange(px_idx.size)
rng.shuffle(order)
rr = out_r[px_idx].copy(); gg = out_g[px_idx].copy(); bb = out_b[px_idx].copy()
out_r[px_idx] = rr[order]; out_g[px_idx] = gg[order]; out_b[px_idx] = bb[order]
shuffle_region(px_idx_bg, f"background_gray_{bg_gray}")
# For each flag: shuffle inside each palette label cluster separately
Hcvs = H # just to avoid confusion — using globals W,H
for fid in unique_flags:
if fid < 0:
continue
meta = per_flag[fid]
name = meta["name"]; px = meta["paste_x"]; py = meta["paste_y"]
lbl = meta["label_map"] # (h_res, w_res) int16, -1 transparent
if lbl.size == 0:
continue
h_res, w_res = lbl.shape
# Map label pixels to canvas linear indices
yy, xx = np.nonzero(lbl >= 0)
if yy.size == 0:
continue
canvas_y = py + yy
canvas_x = px + xx
ok = (canvas_y >= 0) & (canvas_y < H) & (canvas_x >= 0) & (canvas_x < W)
canvas_y = canvas_y[ok]; canvas_x = canvas_x[ok]
labels_here = lbl[yy[ok], xx[ok]]
# For each palette label value present, shuffle within that set
for k in np.unique(labels_here):
idx_k = (labels_here == k)
if not np.any(idx_k):
continue
cy = canvas_y[idx_k]; cx = canvas_x[idx_k]
px_idx = (cy.astype(np.uint32) * W) + cx.astype(np.uint32)
shuffle_region(px_idx, f"flag_{fid}_{name}_field_{int(k)}")
# -------- Save + exactness checks ----------
out = np.stack([out_r, out_g, out_b], axis=1).reshape(H, W, 3)
del out_r, out_g, out_b, r_all, g_all, b_all, Y_all, H_all, C_all
print("Verifying perfect 24-bit coverage (checksum + XOR)…")
packed = (out[...,0].astype(np.uint32) << 16) | (out[...,1].astype(np.uint32) << 8) | out[...,2].astype(np.uint32)
N24 = 1 << 24
sum1 = int(packed.sum(dtype=np.uint64))
expected_sum = (N24 * (N24 - 1)) // 2
def xor_reduce(u32arr, block=8_000_000):
xr = np.uint32(0)
flat = u32arr.ravel()
for s in range(0, flat.size, block):
e = min(flat.size, s + block)
xr ^= np.bitwise_xor.reduce(flat[s:e])
return int(xr)
def xor_0_to(m): return [m, 1, m+1, 0][m & 3]
xor1 = xor_reduce(packed)
expected_xor = xor_0_to(N24 - 1)
assert sum1 == expected_sum and xor1 == expected_xor, "Coverage mismatch: not all 24-bit colors used exactly once."
Image.fromarray(out, mode="RGB").save(out_png_path, format="PNG", compress_level=0)
print(f"Saved: {out_png_path}")
# -----------------------------
# Main
# -----------------------------
def main():
print("Fetching countries & flag URLs…")
countries = fetch_country_list(INCLUDE_UN_OBSERVERS)
print(f"Fetched {len(countries)} candidates.")
print("Downloading flag PNGs (cached)…")
kept = []
for item in tqdm(countries):
p = download_flag_png(item)
if p:
item["local"] = p
kept.append(item)
print(f"Using {len(kept)} flags.")
if len(kept) == 0:
raise RuntimeError("No flags downloaded. Check network access or the API.")
print("Building mosaic + per-flag label maps (uniform gray background)…")
mosaic, flag_id_map, per_flag = place_flags_mosaic_with_labels(
kept, gray=UNIFORM_GRAY, canvas_size=CANVAS_SIZE, margin=MARGIN_PX, palette_colors=PALETTE_COLORS
)
ts = datetime.utcnow().strftime("%Y%m%d-%H%M%S")
preview_path = os.path.join(ASSETS_DIR, f"flags_target_uniformgray_{CANVAS_SIZE}_{ts}.png")
mosaic.save(preview_path, format="PNG")
print(f"Saved target preview: {preview_path}")
out_path = os.path.join(ASSETS_DIR, f"allrgb_flags_hsvy_paletteaware_fields_shuffled_{CANVAS_SIZE}_{ts}.png")
build_allrgb_palette_aware(mosaic, flag_id_map, per_flag, out_path, bg_gray=UNIFORM_GRAY)
print("Done.")
if __name__ == "__main__":
np.set_printoptions(suppress=True)
main()