Calculate distance between colors in HSV space
Asked Answered
H

5

19

I intend to find a distance metric between two colours in HSV space.

Suppose that each colour element has 3 components: hue, saturation, and value. Hue is ranged between 0 to 360, saturation is ranged between 0 to 1, and value is ranged between 0 to 255.

Also hue has a circular property, for example, 359 in hue is closer to 0 in hue value than 10 in hue.

Can anyone provide a good metric to calculate the distance between 2 colour element in HSV space here?

Henriquez answered 31/1, 2016 at 12:45 Comment(0)
G
32

First a short warning: Computing the distance of colors does not make sense (in most cases). Without considering the results of 50 years of research in Colorimetry, things like the CIECAM02 Color Space or perceptual linearity of distance measures, the result of such a distance measure will be counterintuitive. Colors that are "similar" according to your distance measure will appear "very different" to a viewer, and other colors, that have a large "distance" will be undistinguishable by viewers. However...


The actual question seems to aim mainly at the "Hue" part, which is a value between 0 and 360. And in fact, the values of 0 and 360 are the same - they both represent "red", as shown in this image:

Hue

Now, computing the difference of two of these values boils down to computing the distance of two points on a circle with a circumference of 360. You already know that the values are strictly in the range [0,360). If you did not know that, you would have to use the Floating-Point Modulo Operation to bring them into this range.

Then, you can compute the distance between these hue values, h0 and h1, as

hueDistance = min(abs(h1-h0), 360-abs(h1-h0));

Imagine this as painting both points on a circle, and picking the smaller "piece of the cake" that they describe - that is, the distance between them either in clockwise or in counterclockwise order.


EDIT Extended for the comment:

  • The "Hue" elements are in the range [0,360]. With the above formula, you can compute a distance between two hues. This distance is in the range [0,180]. Dividing the distance by 180.0 will result in a value in [0,1]

  • The "Saturation" elements are in the range [0,1]. The (absolute) difference between two saturations will also be in the range [0,1].

  • The "Value" elements are in the range [0,255]. The absolute difference between two values will thus be in the range [0,255] as well. Dividing this difference by 255.0 will result in a value in [0,1].

So imagine you have two HSV tuples. Call them (h0,s0,v0) and (h1,s1,v1). Then you can compute the distances as follows:

dh = min(abs(h1-h0), 360-abs(h1-h0)) / 180.0
ds = abs(s1-s0)
dv = abs(v1-v0) / 255.0

Each of these values will be in the range [0,1]. You can compute the length of this tuple:

distance = sqrt(dh*dh+ds*ds+dv*dv)

and this distance will be a metric for the HSV space.

Glossectomy answered 31/1, 2016 at 13:52 Comment(12)
I am implementing a clustering algorithm for image segmentation in HSV space. Each element or pixel has 3 components, (h,s,v). I need to have a distance metric to measure how far each pixel is away from its cluster centroid. Since there are two problems here, problem 1: different range in each channel, problem 2: circular property in hue channel, final distance metric need to take these factors in. A single distance metric in between two hue values is not helpful considering I am dealing with 3 dimension data here.Henriquez
@Henriquez I'm not entirely sure what you mean. But I extended the answer, and described how you can use the given formula to obtain a metric for the HSV space.Glossectomy
thanks, linear normalization (by dividing the max value) maybe not be an effective solution but at least I can use it to compute the distance between to 2 colors in HSV space.Henriquez
@Henriquez Strictly speaking, the normalization is not necessary. The distance from (0,0,0) to (180,0,0) will be twice as large as the distance to (90,0,0) - and after normalization, the distance of (0,0,0) to (1.0, 0,0) will be twice as large as the distance to (0.5, 0,0). Regardless of the normalization, it will be a metric in both cases. But note that the distance should be 0 in both cases, because two HSV colors with a Value of 0 will be black - regardless of the Hue part. (This is one of the many things that my first paragraph referred to...)Glossectomy
+1 Very good answer. You got me curious though... How would you calculate color distance in CIECAM02 Color Space?Warfield
@MikaelHögström As mentioned in the answer, there has been some research. One relevant paper is "Uniform colour spaces based on CIECAM02 colour appearance model" by Luo et al. There are implementations based on this (e.g. github.com/igd-geo/pcolor/blob/master/de.fhg.igd.pcolor/src/de/… , originally based on painterly.costar.sfu.ca/browser/trunk/source/eduPainter/pcolor ), but it's a remarkably complex topic (particularly considering that in computer science, "color" is often considered to mean "4 bytes - RGBA" ;-))Glossectomy
@Glossectomy Thanks for the answer. How should we proceed when initial hue value returned by skimage is in the range [0, 1]? Should it be scaled to degrees and then calculated to with your formula? or dh can be calculated directly: dh = min(abs(h1-h0), 1-abs(h1-h0)) ?Immingle
@Immingle In order to define a metric, it can be calculated directly. But again, referring to the initial paragraph: Imagine you have red and cyan. In HSV, these could be represented with H being in [0,1], as (0,1,1) and (0.5,1,1) and their distance would be 0.5. Alternatively, you could set H to be in [0,360], and the colors would be (0,1,1), (180,1,1), and their distance would be 180. It's a "metric" in both cases, but it is not clear what the "distance" means exactly.Glossectomy
I am not sure if I understand, in both cases, final distance should be in range: [0,1], so in first case it is 0.5, you are right :) but according to you formula in second case it is 1. That is why I was asking if it is better to start from degrees or from "raw" [0,1] range values...Immingle
@Immingle You can compute dh directly.Glossectomy
Your formula distance = sqrt(dh*dh+ds*ds+dv*dv) is a bit stupid, because for example (120,180,0) and (0,0,0) are the same color (black) in hsv and your distance metric does not account for that.Tyus
@TadeoHepperle 1. Read the disclaimer at the beginning of my answer. 2. Accumulate the knowledge of mankind. 3. Write a better answer. 4. Profit.Glossectomy
G
26

Given hsv values, normalized to be in the ranges [0, 2pi), [0, 1], [0, 1], this formula will project the colors into the HSV cone and give you the squared (cartesian) distance in that cone:

   ( sin(h1)*s1*v1 - sin(h2)*s2*v2 )^2
 + ( cos(h1)*s1*v1 - cos(h2)*s2*v2 )^2
 + ( v1 - v2 )^2
Griffe answered 24/8, 2016 at 2:27 Comment(3)
Note that you can also scale the final (v1 - v2)^2 term by adding a coefficient to it, since it's a bit arbitrary how much impact the "value" should have (I would scale it if it if and only if it helps your application).Griffe
This answer looks more correct than the accepted one. Because HSV space is nonlinear and applying the L2-norm leads to an incorrect distance. it would be better to transform the HSV color to linear space (a,b,c): a = sv cos(h), b = sv sin(h), and c = vTherron
Apparently, it is a cylinder, not a cone. So the first two terms should not make use of v1 and v2. See en.wikipedia.org/wiki/HSL_and_HSVBevus
S
8

In case you are looking for checking just the hue, Marco's answer will do it. However, for a more accurate comparison considering hue, saturation and value, Sean's answer is the right one. You can't simply check the distance from hue, saturation and value equally, because hue is a circle, not a normal vector. It's not like RGB where red, green and blue are vectors


It is a cone if you use chroma.

Cone with Chroma


It is a cylinder if you use saturation.

Cylinder with Saturation


PS: I know I am not giving any new solutions with this post, but Sean's answer really saved me and I wanted to acknowledge it besides upvoting since it is not the top answer here.

Slone answered 10/10, 2020 at 23:6 Comment(1)
It is a cone if you use chroma. If you use saturation, it is a cylinder. See en.wikipedia.org/wiki/HSL_and_HSVBevus
P
0

Lets start with:

c0 = HSV( h0, s0, v0 )
c1 = HSV( h1, s1, v1 )

Here are two more solutions:

  1. (Helix Length)Find the length of curve in euclidean space:

    x = ( s0+t*(s1-s0) ) * cos( h0+t*( h1-h0 ) )
    y = ( s0+t*(s1-s0) ) * sin( h0+t*( h1-h0 ) )
    z = ( v0+t*(v1-v0) )

t goes from 0 to 1.

Note: h1-h0 is not just subtraction it is modulus subtraction. This can be optimized by rotation and then use: h0=0, and h1 = min(abs(h1-h0), 360-abs(h1-h0))

  1. (Helix Length over RGB)Same as above but convert above curve in to RGB instead to euclidean space then calculate arc length. And again convex combination by coordinate of HSV colors, each point on HSV-line convert to RGB.
    Calculate the length of that line in RGB space with euclidean norm.

    helix_rgb( t ) = RGB( HSV( h0+t*( h1-h0 ), s0+t*(s1-s0), v0+t*(v1-v0) ) )

t goes from 0 to 1.

Note: h1-h0 is not just subtraction it is (more than) modulus subtraction e.g.

D(HSV(300,50,50),HSV(10,50,50)) = D(HSV(300,50,50),HSV( 0,50,50)) + D(HSV( 0,50,50), HSV(10,50,50))

Comparison of metrics:
Metrisc comparison
RGB(0,1,0) is referent point and calculate distance to color in right-down corner image.
Color image is generated by rule HSL([0-360], 100, [1-100] ).

EM is short from Euclid with Modulo as Marco13 propose with Sean Gerrish's scale. Comparison of solutions over HSI, HSL and HSV, there are also distance in RGB and CIE76(LAB).

Comparing EM to other solutions like Helix len, RGB2RGB, CIE76 appears that EM give acceptable result at very low cost.

In https://github.com/dmilos/color.git it is implemented EM with arbitrary scaling.
Example:

typedef ::color::hsv<double> color_t; // or HSI, HSL
color_t A = ::color::constant::orange_t{};  \
color_t B = ::color::constant::lime_t{};  \
auto distance = ::color::operation::distance<::color::constant::distance::hue_euclid_entity>( A, B, 3.1415926/* pi is default */ );
Precipitin answered 29/5, 2021 at 19:51 Comment(0)
I
0

I stumbled upon the same problem. The answers here are good. You can't simply draw a line and calculate the distance. If you want to, my fastest Calculation was:

dH = min(h1-h0, 360 - h1+h0);
a = dH*2*pi/360;
dR = s1-s0;
dV = v1-v0;
r = dR/2 + s0;
s = sqrt(dR^2 + (a*r)^2 + dV^2);

However this is just an approximation and i higly suggest everyone reading this to transfer your colors to LAB and use the CIEDE2000 standard to calculate the difference. The results are awesome.

Intercrop answered 17/11, 2023 at 1:34 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.