How do I pick the best color?

2    10 Jul 2015 01:27 by u/cassis

Hi everyone,

I hope you'll be able to help me as I've been struggling with this problem for quite some time now.

First of all, I would like to explain what I'm trying to do.

I have a bunch of pictures with one or two main colors that I know of.

  • image1.jpg has mainly #00a5c2 and #f77117.
  • image2.jpg has mainly #8000ff.

Additionally, I have a series of preset background colors.

  • background 1 is #fe0bbf.
  • background 2 is #07b594.

All of this information comes from a database filled by users.

Given a random image, I need to pick the most "distant" background color, with the most "contrast". For example, if I have a mostly red image, I should pick a green background. If the red color is dark, I should pick a lighter green. The idea is to not display a yellow image over a yellow background so you can't see anything.

What is the best way to achieve this?

I've tried converting the RGB colors to a few color spaces, namely YUV, XYZ, CIE-L*ab, etc., in order to find the largest distance between colors but I can't seem to sort it out.

The answer should be language agnostic. I'm currently working with PHP on this but will port this code to JavaScript at least and maybe other languages too. I can post the current PHP code if needed, however it might be a bit too much here.

Has anyone any experience or hints for this?

Thanks for your help!

1 comment

1

I've worked on a similar problem recently in javascript for my startup. In my case I needed to pick the most prevelant non dark color. So a bit different but some of the same principles should apply. My steps were as follows: 1. Get the colors 2. Get the weight of the colors (how prevelant each one is) 3. Convert RGB over to hex equivelant 4. Perform a luma check to get a brightness value for each color

From there you should be able to pick your color based on your criteria. Here are some of the utility functions I used.

function lumaCheck(c) {
    var c = c.substring(1);
    var rgb = parseInt(c, 16);
    var r = (rgb >> 16) & 0xff;
    var g = (rgb >> 8) & 0xff;
    var b = (rgb >> 0) & 0xff;
    var luma = 0.2126 * r + 0.7152 * g + 0.0722 * b;
    return luma;
}
function componentToHex(c) {
    var hex = c.toString(16);
    return hex.length == 1 ? "0" + hex : hex;
}
function rgbToHex(r, g, b) {
    return "#" + componentToHex(r) + componentToHex(g) + componentToHex(b);
}

In general what you'd probably want to do is get the most prevelant color. Get its brightness (lumaCheck). Then get the color with the furthest luma value away from the picked color in your color set. You can tweak your function to decide between weight & luma value until it's satisfactory. With your example the primary color would be derived from your data result and your compares against the backgrounds, I suppose.