Create a "Paint by Numbers" script

Hello GH community,

I am trying to transform a picture into a “paint by numbers” (childhood nostalgia…)
In photoshop for example, it can be made by using a cutout filter, to split the colors into sample.
I would like to reproduce this cutout filter in grasshopper (using gh components only, not custom python/C++ scripts)
I tried by using a voronoi pattern and merging the cells that are in the same domain of brightness but the result is not very satisfiying yet.

Does anyone have ideas?
Script, example, and first result attached. (4.1 MB)

Perhaps these slightly related topics can be combined?

Created MOSAIC - Grasshopper - McNeel Forum

Sorting and re-assigning RGB color values - Grasshopper - McNeel Forum

1 Like

Here an idea like I used for mosaic using K-Mean Clustering, has color are transformed to points (in [-1, 1] space).
I then use mesh IsoSplitting but you could use Mesh Contour, Mesh Plane Intersection …
K-Mean Clustering is very sensitive to initial condition, so change seed value. There is surely a better approach but I don’t know it.
I also did it a bit fast, so there could be some glitches

split (24.1 KB)


And smoothing the contours, filtering by area … A bit long so some Dams could be useful

split image (22.4 KB)


This is what you need. It even has a gui and can output svgs and pngs with multiple configuration of the drawing and the color legend


Good link. It gives some clue of how to improve what i did. For example i must add a clustering in other color space. I did in rgb.
Here a version with 3 colors spaces

split image color (19.7 KB)

1 Like

Wow, that git demo is pretty cool!

Thank you @old_taz, and @laurent_delrieu, these are some serious leads I have to investigate more, and particularly the K-mean Clustering algorithm that seems to be a great solution.
@pavel.m thank you for the reference, this app from Github is amazing, the result is just perfect. Actually I compared the results from this app and from your script @laurent_delrieu and the color zones are the same. Only problem is by working with meshes, the zone outline is a mesh itself.
Anyway your gh script will help me a lot.

Also; the reason why I have to make a gh script without custom components is that I’m planning to use ShapeDiver to make an online app (and shapeDiver is not ideal with custom components).

You can see the mesh as a bitmap, there is quite no difference, except the color is on the vertex of the mesh and not on a face (pixel for a bitmap).
And as you work with a limited number of slice, it could be possible to not use mesh iso splitting but a different tool that extract faces of each color (integer indeed). This will supress the interpolation where 2 zones are separated by more than 1 unit.
The contour of the mesh is extracted and transformed to a polyline, I think it is quite the same for a bitmap.

Here it is, a script with no iso splitting. So except 2 c# there are no plugin involved !

split image color space no iso (22.9 KB)

It could be possible to smooth the multicolored/multimaterial mesh without generating espaces between colors.

But this script is in a dll and is not shared at the moment.
Polyline smoothing (in red the holes) vs multi material mesh smoothing

1 Like

wow amazing, thank you so much! I will work with that and post the result :slight_smile:

Nice! This inspired me to play a bit with the clustering. I know Shapedriver can’t handle Lunchbox ML or Heron, but I found Lunchbox’s K-Means faster than the C# version (maybe multithreaded?) and Heron’s Visual Center helps with the labeling. Some of the labels are misplaced due to self-intersecting outlines, a classic problem when vectorizing a raster.
split image color space no iso splitting (2.1 MB)



Hi Brian, this is great,I was stuck with the smoothing polylines part. I can’t find the visual center component in Heron plug-in, does it work with Rhino7? If not, is there some equivalent to have the numbers well located? I tried with populate geometry on the meshes with 1 point but it is not ideal.

Visual Center should be the last component under Heron>Utilities. Make sure to grab the latest version from the package manager.

Building on your idea of using populate geometry, you might try it with say 100 points then just get the one farthest from the outline.



Here is the updated definition from your script and Laurent’s one, with the “visual center” replaced. I added a part to clean the curves with self intersections (using pufferfish). I guess now the only problem is the blank space left by the small meshes that have been deleted, under 2 kind of meshes (2 colors). A solution could be giving that blank the color (so the number) of the closest mesh. What do you think? (2.1 MB)

That sounds like a good option. Although, depending on the size of the print, adding a number there might occlude the entire outline given the outline will generally be a pixel wide. You might just leave them unlabeled. @laurent_delrieu 's multi material mesh smoothing script would eliminate these small areas because we could just take the outline of the smoothed mesh instead of smoothing a polyline after extracting a stepping mesh. Maybe you could hire him to help?

@Brian_Washburn @Leeora there are surely other ways to smooth the borders with a global logic.
Here the @DanielPiker code

smooth network of (278.3 KB)

With @diff-arch we implemented some way to smooth a lines network (Catmull, Chaikin …)

It is also possible to extract all polylines from the network then using a conversion to “lines and arcs” in order to suppress the steps.