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/.

← Back to index

Description¶

Extract dominant colors from any image and build a palette swatch — essential for design and data viz.

Libraries & Modules¶

  • PIL.Image
  • collections.Counter
  • matplotlib.pyplot

Python Concepts You'll Practice¶

Image quantization, counting, sorting, color theory

🎛️ Parameter Variations¶

  • N_COLORS — 3 vs 8 dominant colors
  • RESIZE — 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)
No description has been provided for this image

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%
No description has been provided for this image
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