Skip to main content

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
9 files, 150 additions

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