Project 17: Color Extractor Lens 🎨¶
Difficulty: 🟡 Intermediate
Run cells top to bottom. Each step builds on the previous one and saves real files under outputs/color_extractor_lens/.
Description¶
Extract dominant colors from any image and build a palette swatch — essential for design and data viz.
Libraries & Modules¶
PIL.Imagecollections.Countermatplotlib.pyplot
Python Concepts You'll Practice¶
Image quantization, counting, sorting, color theory
🎛️ Parameter Variations¶
N_COLORS— 3 vs 8 dominant colorsRESIZE— speed vs accuracy (50 vs 200 px wide)- Use your own image path instead of the generated sample
In [1]:
# Install dependencies for this project (safe to re-run)
import sys
!{sys.executable} -m pip install pillow numpy matplotlib wordcloud qrcode[pil] python-barcode fpdf2 jinja2 folium plotly pandas scipy ipywidgets -q
Skipping pip install (already installed)
In [2]:
# Interactive Jupyter setup — run this cell first
%matplotlib inline
from pathlib import Path
from IPython.display import display, Image as IPImage, HTML, Markdown, Audio, IFrame
OUTPUT_DIR = Path("outputs") / "color_extractor_lens"
OUTPUT_DIR.mkdir(parents=True, exist_ok=True)
print(f"✅ Outputs folder: {OUTPUT_DIR.resolve()}")
✅ Outputs folder: C:\Users\Hansel Yan\Projects\CodeItAll\outputs\color_extractor_lens
Step 1 — Create or load an image¶
In [3]:
from PIL import Image, ImageDraw
import random
# 🎛️ TWEAK — set IMAGE_PATH to your photo, or None for sample
IMAGE_PATH = None
N_COLORS = 5
RESIZE = 100
if IMAGE_PATH is None:
img = Image.new("RGB", (300, 200))
draw = ImageDraw.Draw(img)
palette = [(255,99,71), (65,105,225), (50,205,50), (255,215,0), (138,43,226)]
for _ in range(40):
x, y = random.randint(0, 280), random.randint(0, 180)
draw.ellipse([x, y, x+40, y+40], fill=random.choice(palette))
sample_path = OUTPUT_DIR / "color_sample.png"
img.save(sample_path)
IMAGE_PATH = sample_path
img = Image.open(IMAGE_PATH).convert("RGB")
w, h = img.size
new_w = RESIZE
img = img.resize((new_w, int(h * new_w / w)))
display(img)
Step 2 — Extract & visualize palette¶
In [4]:
from collections import Counter
import matplotlib.pyplot as plt
# Quantize to reduce unique colors
img_q = img.quantize(colors=64).convert("RGB")
pixels = list(img_q.getdata())
# Round to nearest 16 for grouping
rounded = [((r//16)*16, (g//16)*16, (b//16)*16) for r, g, b in pixels]
top = Counter(rounded).most_common(N_COLORS)
print("Dominant colors (RGB):")
for i, (rgb, count) in enumerate(top, 1):
pct = count / len(rounded) * 100
hex_c = "#{:02x}{:02x}{:02x}".format(*rgb)
print(f" {i}. {rgb} {hex_c} — {pct:.1f}%")
fig, axes = plt.subplots(1, 2, figsize=(12, 4))
axes[0].imshow(Image.open(IMAGE_PATH))
axes[0].set_title("Source Image")
axes[0].axis("off")
swatch = Image.new("RGB", (N_COLORS * 80, 80))
for i, (rgb, _) in enumerate(top):
for x in range(80):
for y in range(80):
swatch.putpixel((i * 80 + x, y), rgb)
axes[1].imshow(swatch)
axes[1].set_title("Extracted Palette")
axes[1].axis("off")
plt.tight_layout()
out = OUTPUT_DIR / "color_palette.png"
fig.savefig(out, dpi=120)
plt.show()
print(f"Saved to {out}")
C:\Users\Hansel Yan\AppData\Local\Temp\ipykernel_51076\1906765735.py:6: DeprecationWarning: Image.Image.getdata is deprecated and will be removed in Pillow 14 (2027-10-15). Use get_flattened_data instead. pixels = list(img_q.getdata())
Dominant colors (RGB): 1. (0, 0, 0) #000000 — 43.5% 2. (64, 96, 224) #4060e0 — 14.5% 3. (48, 192, 48) #30c030 — 7.4% 4. (240, 208, 0) #f0d000 — 6.8% 5. (240, 96, 64) #f06040 — 6.6%
Saved to outputs\color_extractor_lens\color_palette.png
✅ Project complete¶
Check your output folder: outputs/color_extractor_lens/
Next: Project 18: Sentiment Mood Ring · Index