File Exchange

image thumbnail

Maximally Distinct Color Generator

version 2.2.0 (190 KB) by Stephen Cobeldick
Generate maximally distinct colors in an RGB colormap.


Updated 25 Nov 2020

View Version History

View License

MAXDISTCOLOR generates an RGB colormap of maximally-distinct colors.

MAXDISTCOLOR has two required input arguments:
1. the required number of colors.
2. a function handle that converts from sRGB to a uniform colorspace (e.g. CIELAB, DIN99, CAM02-UCS, CAM16-UCS, OSA-UCS, etc.).

Optional input arguments allow the user to:
* Limit the lightness range.
* Limit the chroma range.
* Provide a colormap of colors to be excluded (e.g. background colors).
* Provide a colormap of colors to be included (e.g. company colorscheme).
* Specify the RGB bit depth (e.g. 8 bits per channel, TrueColor).
* Sort the colormap (e.g. by hue, lightness, farthest colors, etc.).

See the Examples tab (HTML documentation) for explanations of the required and optional input arguments.

%% Warning %%

Requesting many colors from a large gamut can require hours/days/.. of processing. Some option combinations are not tractable.

%% Examples %%

>> N = 5;
>> fun = @(m)sRGB_to_OSAUCS(m,true,true); % recommended OSA-UCS
>> rgb = maxdistcolor(N,fun)
rgb =
1.0000 0.0000 1.0000
0.0000 0.0000 1.0000
0.3016 0.0000 0.3492
1.0000 0.0000 0.0000
0.0000 0.4331 0.0000
>> axes('ColorOrder',rgb, 'NextPlot','replacechildren')
>> X = linspace(0,pi*3,1000);
>> Y = bsxfun(@(x,n)n*sin(x+2*n*pi/N), X(:), 1:N);
>> plot(X,Y, 'linewidth',4)

>> maxdistcolor(5,fun, 'exc',[0,0,0]) % Exclude black (e.g. background).
ans =
1.0000 0.0000 1.0000
0.0000 0.0000 1.0000
0.0000 1.0000 0.0000
1.0000 0.0315 0.0000
0.7619 0.8189 1.0000

>> maxdistcolor(5,fun, 'inc',[1,0,1]) % Include magenta.
ans =
1.0000 0.0000 1.0000 % magenta
0.0000 0.0000 1.0000
0.3016 0.0000 0.3492
1.0000 0.0000 0.0000
0.0000 0.4331 0.0000

>> [rgb,Lab] = maxdistcolor(6,@sRGB_to_CIELab, 'Lmin',0.5, 'Lmax',0.7)
rgb =
0.7619 0.0000 1.0000
1.0000 0.0000 0.0000
0.0000 0.7795 0.0000
0.0000 0.5591 1.0000
0.8254 0.6457 0.0794
0.8254 0.2835 0.5397
Lab =
50.3682 89.7713 -77.4020
53.2408 80.0925 67.2032
69.9953 -71.4448 68.9550
58.7226 09.8163 -64.4545
69.9008 05.1696 70.3753
52.1421 59.8639 -06.6541

%% Motivation %%

The development of MAXDISTCOLOR was prompted by:
1. Existing "distinct color" generators use inadequate colorspaces and/or algorithms, leading to suboptimal color distinctiveness.
2. The realization that 64 bit PCs with 8 GB of RAM can operate on the entire 16 million colors of 24 bit TrueColor, allowing for neat and simple vectorized MATLAB code.

These two motivations are closely linked to two non-trivial tasks that have to be solved in order to generate maximally-distinct colors:
1. An algorithm to find the best color combination requires finding the global optimum, a task which grows exponentially with the number of requested colors and with the color gamut size. In MAXDISTCOLOR I use repeated application of a simple greedy algorithm to find the maximally-distinct colors: the repeated greedy algorithm is not particularly fast and is not a general solution for finding a global optimum, but luckily it gives good results for the regularly sampled RGB cube. Note that this algorithm contains no random numbers: it is entirely deterministic and repeatable.
2. Defining a true uniform colorspace: the venerable CIELAB (used by most existing tools I could find) is not really very uniform. For MAXDISTCOLOR I recommend OSA-UCS or CAM02-UCS or CAM16-UCS, all of which provide a more accurate measure of the color distance.

Cite As

Stephen Cobeldick (2021). Maximally Distinct Color Generator (, MATLAB Central File Exchange. Retrieved .

Comments and Ratings (12)

zhian xue

Stephen Cobeldick

Note that since November 2020 all colorspace conversion functions have been renamed to use a consistent naming format based on the colorspace names and no longer use the colorspace units (because the units are not unique, e.g. there are many RGB colorspaces). This also resolves the requests made in the following comments.

per isakson

Daniel Savage

Stephen Cobeldick

"But in the current version the demo and doc should still be updated to not include the sgrb_to_Jab function. If you only copy from the demo or doc it will not work..."

Actually by default the demo uses the DIN99 colorspace. The conversion from sRGB to DIN99 is provided by the functions SRGB_TO_LAB and LAB_TO_DIN99, both of which are included with this submission. So in fact the demo script will work straight out of the box.

The demo script also contains lines of code which are **commented-out** by default. Some of those lines refer to the CAM02 colorspace conversion function SRGB_TO_JAB that must be downloaded separately.The third line of the demo script clearly states: "Some examples use CAM02 colorspace functions, which must be downloaded separately". It must be assumed that the user can read documentation.

The first section of the HTML examples also uses SRGB_TO_LAB (which as already mentioned is included) and then in the second section explains that this is not the preferred colorspace, and gives a link to where the recommended CAM02 conversion functions can be downloaded. The rest of the examples use CAM02 because it is more uniform than CIELAB. Of course it is trivial to use SRGB_TO_LAB or any other suitable colorspace conversion function, just supply the desired conversion function as the second input argument.

"...and people are likely to miss the difference between _Lab and _Jab"

The CIELAB colorspace has axes L*, a*, and b*.

The CAM02-UCS colorspace has axes J', a', and b'.

Anyone working with color representation models must pay careful attention to the different colorspaces, their parameters, etc. It is not within the scope of this one FEX submission to provide an introduction to color theory nor color representation modelling. Any user who does want to learn about different colorspaces will find plenty of information online.

Sascha Duczek

Sorry Stephen, I did not see your earlier post. But in the current version the demo and doc should still be updated to not include the sgrb_to_Jab function. If you only copy from the demo or doc it will not work and people are likely to miss the difference between _Lab and _Jab, as I did.


Stephen Cobeldick

_"Does this scale well?"_

About as well as any other problem which exhibits exponential growth with the number of terms... which is why I wrote that finding many colors from a large gamut can be slow, and also that the problem of finding maximally-distinct points in a discrete space is basically an NP-hard one (disclaimer: I have not done a deeper analysis of what exact category of problem this is, and it is quite possible that I am wrong about this category).

The timing depends on the specific options that you are using: finding 1000 colors from a gamut of 1001 colors will be much faster than from a gamut of 16 million colors. On my old computer, 1000 maximally distinct colors from a gamut of 1001 colors took 1 minute and 40 seconds to identify. If you use the default [6,7,6] bit depth and the entire RGB cube then the gamut contains 524288 colors: here are some timings (based on a logarithmic increase in N) using that gamut:

N time (s)
1 0.56
2 0.63
5 2.35
10 9.29
22 73.38
46 784.75
100 2597.60
215 10246.20

From this exponential trajectory I estimate that 1000 colors would require about one week to generate.

This is why I provided options to control the bit-depth, and limit the lightness and chroma ranges: use these to reduce the size of the gamut. There is no single parameter setting that works for all cases, you will need to experiment and get a feeling for what is tractable.

Aditya Tannu

Does this scale well? I'm trying to generate over a 1000 colors and anything above 50 takes way too long to complete.


Wonderful! Thanks, it solved my issue!

Patrick Morhai

Stephen Cobeldick

@Yu Li: I suspect that you renamed some function in an attempt to make that example work. I can see two main candidates for your mistake:

1. The function SRGB_TO_LAB is supplied with this submission. It has only one input argument, and it will not work like you are trying to use it (the documentation has examples of how to use it).

2. Earlier versions of my CIECAM02 repository included a function named SRGB_2_JAB which only accepted two input arguments, and it will not work like you are trying to use it (it does not have the second input <isd>)

Note that it really makes absolutely no difference to MAXDISTCOLOR. MAXDISTCOLOR will work with almost any RGB->UCS conversion function, as long as you read and follow the documentation for the conversion function that you use!

In any case, the simplest solution is to download the current CIECAM02 version and use it. I tried it just now: I downloaded both the CIECAM02 and MAXDISTCOLOR zip files to a new folder, unzipped them, and it worked without any error:

>> addpath CIECAM02-master
>> N = 5;
>> fun = @(m)srgb_to_Jab(m,true,'LCD');
>> rgb = maxdistcolor(N,fun)
rgb =
1.0000 0.3228 0.0000
0.8730 0.0000 1.0000
0.0000 0.0000 0.7937
0.0000 0.5512 0.0000
0.2222 0.0000 0.0000

Yu Li


I tried the test code:
>> N = 5;
>> fun = @(m)srgb_to_Jab(m,true,'LCD'); % CAM02-LCD, the recommended colorspace.
>> rgb = maxdistcolor(N,fun)

but it reported that:
Error using srgb_to_Jab
Too many input arguments.

Error in @(m)srgb_to_Jab(m,true,'LCD')

Error in maxdistcolor (line 157)
map = fun([0,0,0;1,1,1]);

is there any mistake with my operation?


MATLAB Release Compatibility
Created with R2015b
Compatible with R2009a and later releases
Platform Compatibility
Windows macOS Linux

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!