quantize hsl colors to colormap

Diemo Schwarz's icon

Hi, for displaying a jit matrix on an LED matrix with only 11 different colors I need to find which color is closest to the hue value of each pixel. It seems this problem is similar to reducing a color space to an indexed color map (looking at you, GIF). Is there a primitive in jitter or has someone done this already? Cheers!

Roman Thilenius's icon

.../ ...round? (non-jit)

Jean-Francois Charles's icon

You got me interested, Diemo, so I tried your idea of quantized hue lookup map.
Here is what I came up with:

Max Patch
Copy patch and select New From Clipboard in Max.

Jean-Francois Charles's icon

Here with an option for "linearly spaced hues".
By the way, what are your 11 colored LEDs? I've used different colors, as well as RBG LEDs, but what is your set of 11 colors?

Max Patch
Copy patch and select New From Clipboard in Max.

Diemo Schwarz's icon

Thanks for the example, Jean François! Using jit.charmap is very neat! I wonder if a little gen might help, also.

The LEDs are the ones on a Linnstrument, and have these color choices:
1=red, 2=yellow, 3=green, 4=cyan, 5=blue, 6=magenta, 7=off, 8=white, 9= orange, 10=lime and 11=pink

However, there's the additional difficulty of black and white, which have to be detected by looking at S and L, argh! I guess I need to find out more about perceptive distances in color-spaces...

Jean-Francois Charles's icon

I understand: you don't have control on the intensity of each LED. I guess it's (hardware) RGB LEDs, but only 11 options are exposed. So, you would have to look for "the closest color" in 3D space (RGB or HSL, should make no difference) instead of "the closest hue".
I would start with Euclidian distance. The Lookup Table would be a 4-plane matrix.

Jean-Francois Charles's icon

Actually, charmap would not work for that. Charmap works plane by plane, but not depending on the (RGB) or (HSL) triplet.
I feel like your quickest solution for a first version would be something like my example above, then doing "white if L is greater than..." and "off if L is less than..."
Still, your question is quite interesting and a 3D version would be nice. https://en.wikipedia.org/wiki/Color_quantization

Jean-Francois Charles's icon

The good thing with your problem is that the grid is small, so, it might be OK to use a kind of inefficient programming. See how that works for you: here, I'm computing the distance of each pixel in the original picture with every color from a set of 11. That's fast, staying in Jitter world. The "inefficient" part is getting the minimum distance for each cell (I do it here with a list and bubble sort - that part could be made in jit.gen).
Still, the space is so small that it takes no time.

Max Patch
Copy patch and select New From Clipboard in Max.

Diemo Schwarz's icon

Thanks for the wikipedia link, it confirms that Euclidean distance in RGB space should work (not in HSL, supposedly because it is quite warped). For this there is the efficient kd-tree of mubu.knn used like this:

Max Patch
Copy patch and select New From Clipboard in Max.

The nearest-neighbour lookup could probably be done in gen, or exist already (Octree, anyone?)

Here'a a fun thing I made: it allows to play a movie on the Linnstrument, but also shows that the colour quantisation is not perfect (Maybe I could augment contrast/brightness at the source?):

Jean-Francois Charles's icon

Could you share the 11 colors you're using? I'd like to try to see if I get the same results as you.

Diemo Schwarz's icon

They're in the dict linnstrument_colors in the patch.

jirc's icon

Hi there, I need something very similar, more simple even:

I have a set of 7 RGB colors pre-defined and need to find out which is closest to an incoming arbitrary RGB color.

I think what you were discussing here is quite similar, but I can't figure out how to adapt your solutions to my simple need.
If you have a hint… Very appreciated.
Cheers.
J.

TFL's icon

I slightly adapted the patch from DIEMO SCHWARZ to give it a bit of context and make it adapt itself to the size of the incoming matrix. You need the MuBu package for this patch to work.
To set your list of defined colors, just edit the dict inside the subpatcher.

Max Patch
Copy patch and select New From Clipboard in Max.


jirc's icon

Thanks for jumping in TFL,

and I managed to get it going somehow. However, it seems a bit overkill to use jitter and especially mubu for my task? I need 16 instances of this "quantization" and I am getting a lot of "mubu.track: MuBu container colors already has a predefined track rgb" errors.
I am not familiar with mubu and don't know if this error can be ignored?

I am also wondering if there is a straight and simple max/math solution to "quantize" an incoming RGB color to a palette of 7 RGB colors?

Your help is very appreciated.
Cheers,
J.

TFL's icon

There is no simple solution for color quantization (see also posterization). Depending on your use case, you might prefer a GPU solution. I remember a shader I found that can do exactly that (mapping a pixel color to the closest color from a defined list) but I cannot find it again. Maybe dig in shadertoy.

I noticed my example throws an error when instantiating the patch, it's because the mubu.track receives orders while the track has not been initialized yet. Throw a [del 100] just after the [loadbang] in the subpatcher and you're should be good.

Regarding the error you get, I assume you simply copy-pasted the whole subpatcher several times, which throws the error you see, because you try to recreate the rgb track for each subpatcher.
here is a modified version of the previous patch where I separated the initialization phase (creation of the mubu container and the rgb track, filling the rgb track with desired color list) from the color quantization phase (using mubu.knn).

By "16 instances of this 'quantization'", you mean quantize 16 different matrices at the same time? Or quantize with 16 different color palettes?

Max Patch
Copy patch and select New From Clipboard in Max.

Diemo Schwarz's icon

Hi Jirc, if you do this per pixel, esp. for video, you really want sub-linear complexity like with a kd-tree (knn in gen, anyone?). Even faster would be to use a precalculated look-up table from 8bit rgb colour space to quantised colour index (takes 16 MB).
Here is the patch with TFL's improvements (thanks!) with the init separated from the lookup. For different target colours, you could use different mubu buffers, or simply localise the mubu track name (e.g. #0rgb). Also, a deferlow reliably assures that the track is created before using it.
Best!

quantize rgb colours.maxpat
Max Patch


jirc's icon

Thanks so much, everyone. With help from You and Trial and Error, I got something going. Much appreciated, J.