ianmayo
ianmayo

Reputation: 2402

Algorithm to successively deliver colors of maximum contrast

I need to generate colors to use to highlight types of content. There may be 10s of color shades in one document.

My use case is that I wish to inform scientists regarding how data has been extracted from an input document. Color-coded background shading (with descriptive) tooltips will be used to highlight the blocks imported. Here is a mock input file: raw input file

And here is a mock version of how it would look once highlighted to indicate which fields were imported. A tooltip on each highlighted block would give further detail on the tool that recognised it, plus the value (with units) that was pushed to the database: marked up input file

Each time I encounter a new type of content, I need to generate a new color. That color should have maximum contrast to the existing colors. Clearly, the further we go, the less contrast there will be.

In trying to imagine a solution to this, I've imagined a color wheel. We start with one color. For the next color to have maximum contrast, it will be opposite the first on the wheel.

For each successive color, an algorithm will have to look for the largest "unoccupied" arc on the color wheel and generate the color at the mid-point of it.

Does this seem like any existing color generation strategy?

If so, are there any documented algorithms to implement it?

(My target environment is Python, but that seems a mere implementation detail)

Upvotes: 0

Views: 971

Answers (2)

feature_engineer
feature_engineer

Reputation: 1158

You want to have colors equidistant, and as far away as possible from each other, with white and black already inserted as used.

An easy, and surprisingly good metric to use is mean-red:

formula

formula

formula

formula

formula

If you're doing it on the fly, you'd have to recalculate the positions of your colors with each new color you add, and if you know beforehand how many colors there are, you can calculate their positions all at once.

Here's an example implementation in python:

import numpy as np
from itertools import combinations
from scipy.optimize import minimize, Bounds

BLACK_AND_WHITE = np.array((0.0, 0.0, 0.0, 255.0, 255.0, 255.0))

we consider three consecutive numbers in the array as representing a color, given two such triplets, and using the distance as defined above, our distance function is

def distance(c1, c2):
    r = (c1[0] + c2[0]) / 2.0
    coeffs = np.array(((2.0 + r/256), 4, (2.0 + (255 - r)/256.0)))
    diff = c1 - c2
    return np.sqrt(np.sum(coeffs * diff ** 2))

We want to maximize the minimal distance between all color pairs, which is the same as minimizing the negative the minimal distance. To get the pairs, we use combinations(..., 2) which does just that, and to make it iterate over the triplets we reshape the colors array so that each row would contain a color:

def cost_function(x):
    colors = np.concatenate((BLACK_AND_WHITE, x)).reshape(-1, 3)
    return -min(mean_red_distance(color_pairs[0], color_pairs[1]) for color_pairs in combinations(colors, 2))

Now it's time to minimize the our cost function, the colors are allowed to range between 0 and 255:

def get_new_colors_after_adding(existing_colors):
    if len(existing_colors):
        guess = np.mod(existing_colors.reshape(-1, 3)[0] + np.array((100, 100, 100)), 256)
    else:
        guess = np.array((0, 255, 255))
    guess = np.concatenate((guess, existing_colors))
    # let all colors range between 0 and 255
    result = minimize(cost_function, guess, bounds=Bounds(0, 255))
    if not result.success:
        raise ValueError('Failed adding new color')
    return result.x

And finally, we add 10 colors at each step and print the resulting triplets:

if __name__ == '__main__':
    # start with no colors
    existing_colors = np.empty(0, dtype=np.int32)
    # example of consequently adding colors.
    for i in range(10):
        existing_colors = get_new_colors_after_adding(existing_colors)
        print(np.round(existing_colors.reshape(-1, 3)).astype(np.int))

Upvotes: 1

ianmayo
ianmayo

Reputation: 2402

Aah, I've thought of an alternate strategy for this problem.

Instead of generating colors on the fly, the color-coding could be deferred until the end of the process.

Once we know how many colors are required, we can generate that many permutations evenly spaced throughout the hues in the HSB/HSV color spectrum. That would provide the most contrast, I think.

Upvotes: 0

Related Questions