Add the dominant slices example
- ID
c18a520- date
2022-10-29 08:02:31+00:00- author
Alex Chan <alex@alexwlchan.net>- parent
206ba6c- message
Add the dominant slices example- changed files
Changed files
scripts/README.md (0) → scripts/README.md (774)
diff --git a/scripts/README.md b/scripts/README.md
new file mode 100644
index 0000000..5d8075d
--- /dev/null
+++ b/scripts/README.md
@@ -0,0 +1,36 @@
+This folder has some scripts I've written that use the output of dominant colours to do interesting things.
+
+## dominant_slices.py
+
+This "slices" an image based on its dominant colours: each slice contains then pixels of the original image which are closest to one of the dominant colours.
+
+```console
+$ python3 dominant_slices.py lighthouse.jpg --max-colours=5
+```
+
+Example:
+
+<table>
+ <tr>
+ <td>
+ #e8e3d7
+ <img src="lighthouse.e8e3d7.png">
+ </td>
+ <td>
+ #838882
+ <img src="lighthouse.838882.png">
+ </td>
+ <td>
+ #4576bb
+ <img src="lighthouse.4576bb.png">
+ </td>
+ <td>
+ #292019
+ <img src="lighthouse.292019.png">
+ </td>
+ <td>
+ #c53b4e
+ <img src="lighthouse.c53b4e.png">
+ </td>
+ </tr>
+</table>
scripts/dominant_slices.py (0) → scripts/dominant_slices.py (2617)
diff --git a/scripts/dominant_slices.py b/scripts/dominant_slices.py
new file mode 100755
index 0000000..5789b79
--- /dev/null
+++ b/scripts/dominant_slices.py
@@ -0,0 +1,102 @@
+#!/usr/bin/env python3
+"""
+Given an image, this divides it into "slices" based on its dominant colour
+components.
+
+It starts by finding the dominant colours, then groups the pixels based
+on which colour they're closest to. There's one slice per colour -- containing
+all the pixels which are closest to this colour.
+
+"""
+
+import argparse
+import os
+import subprocess
+
+from PIL import Image
+import tqdm
+
+
+def parse_hex(line):
+ return (int(line[1:3], 16), int(line[3:5], 16), int(line[5:7], 16))
+
+
+def parse_args():
+ parser = argparse.ArgumentParser(
+ description="Slice an image based on its dominant colours."
+ )
+ parser.add_argument("path", metavar="PATH", help="Path to the image to slice")
+ parser.add_argument(
+ "--max-colours",
+ dest="max_colours",
+ default=5,
+ help="how many colours to find",
+ )
+
+ return parser.parse_args()
+
+
+def get_dominant_colours(args):
+ """
+ Finds the dominant colours given the argumrnt.
+
+ Returns a list of RGB tuples, e.g.
+
+ [
+ (233, 228, 215),
+ (133, 139, 136),
+ (69, 118, 187),
+ ]
+
+ """
+ command = [
+ "dominant_colours",
+ "--no-palette",
+ args.path,
+ f"--max-colours={args.max_colours}",
+ ]
+ output = subprocess.check_output(command)
+
+ return [parse_hex(line) for line in output.decode("ascii").strip().splitlines()]
+
+
+if __name__ == "__main__":
+ args = parse_args()
+
+ dominant_colours = get_dominant_colours(args)
+
+ new_image_data = {colour: [] for colour in dominant_colours}
+
+ im = Image.open(args.path)
+
+ for pixel in tqdm.tqdm(im.getdata()):
+ if im.mode == "RGB":
+ r, g, b = pixel
+ alpha = 255
+ elif im.mode == "RGBA":
+ r, g, b, alpha = pixel
+ elif im.mode == "L":
+ r = g = b = pixel
+ alpha = 255
+ else:
+ raise ValueError(f"Unsupported image mode: {im.mode}")
+
+ closest_colour = min(
+ dominant_colours,
+ key=lambda c: (r - c[0]) ** 2 + (g - c[1]) ** 2 + (b - c[2]) ** 2,
+ )
+
+ for colour in new_image_data:
+ if colour == closest_colour:
+ new_image_data[colour].append((r, g, b, alpha))
+ else:
+ new_image_data[colour].append((255, 255, 255, 0))
+
+ for (r, g, b), data in new_image_data.items():
+ new_im = Image.new("RGBA", im.size)
+ new_im.putdata(data)
+
+ name, _ = os.path.splitext(os.path.basename(args.path))
+ out_path = f"{name}.{r:02x}{g:02x}{b:02x}.png"
+
+ new_im.save(out_path)
scripts/lighthouse.292019.png (0) → scripts/lighthouse.292019.png (249451)
diff --git a/scripts/lighthouse.292019.png b/scripts/lighthouse.292019.png
new file mode 100644
index 0000000..7727769
Binary files /dev/null and b/scripts/lighthouse.292019.png differ
scripts/lighthouse.4576bb.png (0) → scripts/lighthouse.4576bb.png (170663)
diff --git a/scripts/lighthouse.4576bb.png b/scripts/lighthouse.4576bb.png
new file mode 100644
index 0000000..0e37750
Binary files /dev/null and b/scripts/lighthouse.4576bb.png differ
scripts/lighthouse.838882.png (0) → scripts/lighthouse.838882.png (264131)
diff --git a/scripts/lighthouse.838882.png b/scripts/lighthouse.838882.png
new file mode 100644
index 0000000..7066d11
Binary files /dev/null and b/scripts/lighthouse.838882.png differ
scripts/lighthouse.c53b4e.png (0) → scripts/lighthouse.c53b4e.png (115743)
diff --git a/scripts/lighthouse.c53b4e.png b/scripts/lighthouse.c53b4e.png
new file mode 100644
index 0000000..79088cf
Binary files /dev/null and b/scripts/lighthouse.c53b4e.png differ
scripts/lighthouse.e8e3d7.png (0) → scripts/lighthouse.e8e3d7.png (156897)
diff --git a/scripts/lighthouse.e8e3d7.png b/scripts/lighthouse.e8e3d7.png
new file mode 100644
index 0000000..8db8a5a
Binary files /dev/null and b/scripts/lighthouse.e8e3d7.png differ
scripts/requirements.in (0) → scripts/requirements.in (27)
diff --git a/scripts/requirements.in b/scripts/requirements.in
new file mode 100644
index 0000000..ce23c47
--- /dev/null
+++ b/scripts/requirements.in
@@ -0,0 +1,2 @@
+Pillow==9.2.0
+tqdm==4.54.0
scripts/requirements.txt (0) → scripts/requirements.txt (170)
diff --git a/scripts/requirements.txt b/scripts/requirements.txt
new file mode 100644
index 0000000..82d44c2
--- /dev/null
+++ b/scripts/requirements.txt
@@ -0,0 +1,10 @@
+#
+# This file is autogenerated by pip-compile
+# To update, run:
+#
+# pip-compile
+#
+pillow==9.2.0
+ # via -r requirements.in
+tqdm==4.54.0
+ # via -r requirements.in