M
M
Maxim Siomin2021-12-26 21:25:24
Algorithms
Maxim Siomin, 2021-12-26 21:25:24

How to find the most similar color?

I confess that I am not strong in algorithms, so I ask this question.
I have a list of colors, it looks like this:

/**
 * All colors and their names
 */
@Suppress("SpellCheckingInspection")
val names = mapOf(
    "000000" to "Black",
    "000080" to "Navy Blue",
    "0000C8" to "Dark Blue",
    "0000FF" to "Blue",
    "000741" to "Stratos",
    "001B1C" to "Swamp",
    "002387" to "Resolution Blue",
    "002900" to "Deep Fir",
    "002E20" to "Burnham",
    "002FA7" to "International Klein Blue",

That is, the name is not for every color, but only for some. Then I have this method:
object ColorHandler {

    /**
     * Color sample: 556A74
     */
    fun getColorName(color: String): String {
        if (color in names.keys)
            return names[color]!!

        val red = color.slice(R)
        val green = color.slice(G)
        val blue = color.slice(B)

       // what's here???
    }
}

val R = 0..1
val G = 2..3
val B = 4..5

How to implement the search for the most similar color?
For example, if 0000DD should be 0000C8

Answer the question

In order to leave comments, you need to log in

6 answer(s)
S
Sergey Sokolov, 2021-12-26
@MaxSiominDev

The simplest is to calculate the sum of the squared distances for each of the components: R, G and B.
For a pair 0000DD, 0000C8of "distance" will be:

(0x00 - 0x00)^2 + (0x00 - 0x00)^2 + (0xDD - 0xC8)^2 = 441
So count up to each of the certain colors, find the minimum.
Can be compared in other color models. For example, in HSV (hue, saturation, lightness) - if you consider that a saturated red and a dull red of exactly the same shade are “closer” than an orange of the same brightness. We are talking about possibly different “weights” of channels in determining the proximity of two colors.

G
GavriKos, 2021-12-26
@GavriKos

Convert to hsl and compare h

W
Wataru, 2021-12-26
@wataru

If speed is not particularly important, then just go through all the named colors (better that it be some kind of list or array, not a map) and calculate some kind of metric (for example, the sum of the squared difference for each channel separately). From the received numbers, look for the minimum and give out the color that gave it.
If you need to work very fast, then you need to represent your nominal colors as points in three-dimensional space, build a kd-tree or r-tree and already look for the closest point to the requested point in it.

A
Alexey Cheremisin, 2021-12-26
@leahch

Translate channels from strings to numbers, calculate the absolute difference for all channels separately. Add the difference of all to the total difference, The color with the smallest difference will be the desired one.
abs(00 - 00) + abs(00 - 00) + abs(DD - .C8) = difference

V
Vasily Bannikov, 2021-12-26
@vabka

Since the question is in the "algorithms" tag, I will describe the following algorithm:
1. Colors in RGB are encoded by three numbers, each of which indicates the intensity of some component.
Red, Green, and Blue, respectively.
2. The hexadecimal format shown uses 6 hexadecimal digits, with two digits for each component in the same order. When converted to a decimal system, this will be a value from 0 to 255.
3. The simplest algorithm is to represent the color as a point in a three-dimensional coordinate system, and find the closest known point to the given one.
Here is a naive solution with exhaustive search and without taking into account shades (pseudocode)
In theory, if there are a lot of colors, then it will really find the closest color, and not some kind of game.

struct Color(red: u8,  green: u8, blue: u8);
type ColorName = String;
let KNOWN_COLORS: Map<Color, ColorName> = Map {...};


fn get_nearest_known_color_name(color: Color) -> ColorName {
  if KNOWN_COLORS.has(color) {
    return KNOWN_COLORS.get(color);
  }
  var (nearest_color, nearest_color_name) = KNOWN_COLORS.first();
  var distance = calculate_distance(nearest_color, color);
  for (key, value) of KNOWN_COLORS.skip(1) {
    var distance = calculate_distance(key, color);
    if distance < distance_to_nearest {
      distance_to_nearest = distance;
      nearest_color = key;
      nearest_color_name = value;    
    }
  }
  return nearest_color_name;
}

fn calculate_distance(lhs: Color, rhs: Color) -> f64 {
    var r = lhs.red - rhs.red;
    var g = lhs.green - rhs.green;
    var b = lhs.blue - rhs.blue; // вычитаем один вектор из другого
    return sqrt(r^2+g^2+b^2); // считаем длину получившегося вектора по теореме пифагора
}

All that remains for you is to use numbers instead of strings.
PS: I didn't even think about HSV.

S
splxgf, 2021-12-29
@splxgf

Technically, dE is used for the color similarity criterion. This formula works in Lab space.
You can calculate the Lab value for each RGB value (suppose it is sRGB) and the color difference is found using ready-made formulas.
www.easyrgb.com/en/math.php
For example sRGB-XYZ-Lab and the difference between two DeltaE CIE colors.

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question