

Like a cliché nerd (think Sheldon Cooper), I’ve always been fascinated by country flags. So much so, that my company de idee even has a free to use online platform dedicated to it called de vlag (the flag in Dutch).
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()
Date | |
---|---|
Colors | 16,777,216 |
Pixels | 16,777,216 |
Dimensions | 4,096 × 4,096 |
Bytes | 50,351,853 |