Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Are there known implementations of the CIEDE2000 or CIE94 Delta-E color difference calculation algorithm?

I need to calculate the Delta-E distance between two colors. The algorithm to do so with two colors in CIELab color space looks like this:

enter image description here

Are there known open-sourced implementations of this algorithm? It's not hard to implement, but from my last attempt of implementing a color-space conversion algorithm I'd prefer to not re-develop the wheel when it's already on the road and tested.

CIEDE2000 would be nice too and more accurate, but might also be overkill on the iPhone. CIE94 would be just fine I guess.

like image 455
SecretService - not really Avatar asked Jul 08 '11 21:07

SecretService - not really


People also ask

How do you calculate Delta E in color?

To calculate the Delta E variance from the produced color to the target, you square each reading's distance (to ensure a positive number). Then you add all of the squares of those readings and take the square root of that number and this gives you the Delta E variance.

What is an acceptable Delta E?

A Delta E of 1 between two colors that are not touching one another is generally considered to be barely perceptible by the average human observer; a Delta E between 3 and 6 is typically considered an acceptable match in commercial reproduction on printing presses.

What is CIEDE2000?

CIEDE2000 [1, 2] is a color-difference formula recommended by the CIE in year 2001. It has also recently been published as an ISO and CIE Joint Standard [3]. It is a modification of CIELAB [4] and gives an overall best performance in predicting experimental datasets.


2 Answers

There's an open-source C# implementation of the CIE94 formula you provided:

https://github.com/THEjoezack/ColorMine/blob/master/ColorMine/ColorSpaces/Comparisons/Cie94Comparison.cs

It requires your colors be in LAB color space, the conversion source for which is in the same library if needed.

You can check your cIE94 calculations online using the same library as well.

Here's the relevant snippet of code, labA and labB are the inputs:

var deltaL = labA.L - labB.L;
var deltaA = labA.A - labB.A;
var deltaB = labA.B - labB.B;

var c1 = Math.Sqrt(Math.Pow(labA.A, 2) + Math.Pow(labA.B, 2));
var c2 = Math.Sqrt(Math.Pow(labB.A, 2) + Math.Pow(labB.B, 2));
var deltaC = c1 - c2;

var deltaH = Math.Pow(deltaA,2) + Math.Pow(deltaB,2) - Math.Pow(deltaC,2);
deltaH = deltaH < 0 ? 0 : Math.Sqrt(deltaH);

const double sl = 1.0;
const double kc = 1.0;
const double kh = 1.0;

var sc = 1.0 + Constants.K1*c1;
var sh = 1.0 + Constants.K2*c1;

var i = Math.Pow(deltaL/(Constants.Kl*sl), 2) +
                Math.Pow(deltaC/(kc*sc), 2) +
                Math.Pow(deltaH/(kh*sh), 2);
var finalResult = i < 0 ? 0 : Math.Sqrt(i);

The "Constants" are defined based on your application type:

case Application.GraphicArts:
    Kl = 1.0;
    K1 = .045;
    K2 = .015;
    break;
case Application.Textiles:
    Kl = 2.0;
    K1 = .048;
    K2 = .014;
    break;
like image 95
Joe Zack Avatar answered Oct 21 '22 13:10

Joe Zack


I typed in the equation (for Common Lisp code see at bottom) and ran a few random evaluations. The parameters are listed in this order: L*1 a*1 b*1 L*2 a*2 b*2 DeltaE*

I'm not perfectly sure that the results are correct. But if your code gives the same results, then it's probably sufficient.

   ((53.0 0.65 0.15 33.0 -0.45 -0.1 20.03112)
    (42.0 -0.3 0.1 74.0 -0.2 -0.15 32.001118)
    (12.0 -1.0 -0.45 32.0 0.3 0.9 20.084782)
    (94.0 -0.1 -0.55 77.0 0.5 0.45 17.03928)
    (75.0 -0.8 0.35 46.0 -0.6 -0.85 29.02483)
    (83.0 -0.65 -0.7 67.0 0.75 0.0 16.074173)
    (70.0 -0.7 0.9 54.0 0.35 -0.95 16.13608)
    (81.0 0.45 -0.8 53.0 -0.35 0.05 28.023375)
    (40.0 -0.2 -0.65 25.0 -1.0 0.8 15.088856)
    (66.0 0.85 -0.7 93.0 0.55 0.15 27.014244)
    (44.0 -0.5 0.5 23.0 -0.9 0.5 21.00363)
    (67.0 0.4 0.25 42.0 -0.25 0.6 25.010727)
    (32.0 0.6 0.55 86.0 0.0 0.25 54.003925)
    (96.0 -0.15 -0.9 87.0 0.25 -0.3 9.027307)
    (100.0 -0.6 0.3 61.0 -0.25 -0.75 39.015385)
    (2.0 -0.2 -0.65 73.0 -0.3 0.65 71.01173)
    (74.0 0.1 -0.65 96.0 -0.5 0.8 22.05474)
    (22.0 -0.3 -0.85 64.0 -0.65 -0.95 42.0015)
    (73.0 -0.35 0.3 38.0 0.25 -1.0 35.02875)
    (91.0 0.6 0.45 82.0 -0.25 0.2 9.042115))

And here is the source code (tested in SBCL):

;; http://en.wikipedia.org/wiki/Hypot thats not necessary if numbers
;; are not float and even if they are float the values of L*, a* and
;; b* are bound to tiny range
(defun hypot (x y)
  "Compute hypotenuse, prevent overflow."
  (declare (type number x y)
       (values number &optional))
  (let ((ax (abs x))
    (ay (abs y)))
    (if (or (< ax 1e-6) (< ay 1e-6))
    (sqrt (+ (* ax ax) (* ay ay)))
    (if (< ay ax)
        (* ax (sqrt (1+ (expt (/ y x) 2))))
        (* ay (sqrt (1+ (expt (/ x y) 2))))))))
#+nil
(list
 (hypot 1 0)
 (hypot 0 1)
 (hypot (sqrt 2) (sqrt 2))
 (hypot 2 10000))

;; http://www.devmaster.net/forums/archive/index.php/t-12680.html
(defun hypot3 (x y z)
  (hypot (hypot x y) z))

(defun delta-e*-94 (l1 a1 b1 l2 a2 b2 &key (application :graphic-arts))
  "Distance in CIE L* a* b* color space."
  (declare (type number l1 a1 b1 l2 a2 b2)
       (type (member :graphic-arts :textiles) application)
       (values number &optional))
  (destructuring-bind (kl k1 k2)
      (ecase application
    (:graphic-arts '(1 .045 .015))
    (:textiles '(2 .048 .014)))
   (let* ((delta-l (- l1 l2))
      (c1 (hypot a1 b1))
      (c2 (hypot a2 b2))
      (delta-c (- c1 c2))
      (delta-a (- a1 a2))
      (delta-b (- b1 b2))
      (delta-h (sqrt (+ (expt delta-a 2)
            (expt delta-b 2)
            (* -1 (expt delta-c 2)))))
      (l/k (/ delta-l kl))
      (c/k (/ delta-c (1+ (* k1 c1))))
      (h/k (/ delta-h (1+ (* k2 c1)))))
     (hypot3 l/k c/k h/k))))


#+nil ;; some test runs
(labels ((rL () ;; random number from 0..100 inclusive
       (random 101))
     (r- ()
       (/ (- (random 40) 20) 20))
     (r3 ()
       (list (rL) (r-) (r-))))
  (loop for i below 20 collect
   (destructuring-bind (l a b) (r3)
     (destructuring-bind (ll aa bb) (r3)
       (mapcar #'(lambda (x) (* 1s0 x)) 
           (list l a b ll aa bb (delta-e*-94 l a b ll aa bb))))))) 

#+nil ;; example test run
((80.0 0.85 0.35 13.0 0.4 -0.8 67.01107)
 (11.0 0.25 -0.35 66.0 0.45 0.15 55.002594)
 (74.0 -0.55 0.45 98.0 0.7 -0.85 24.066118)
 (37.0 -0.3 0.35 60.0 0.55 -0.3 23.02452)
 (20.0 -0.85 0.5 20.0 -0.25 0.1 0.6907073)
 (23.0 0.25 -0.05 15.0 0.55 -0.8 8.039892)
 (29.0 -0.55 0.05 9.0 -0.2 -0.8 20.020708)
 (11.0 0.55 -0.45 60.0 0.9 -0.15 49.00211)
 (70.0 0.5 -0.15 66.0 -0.8 0.85 4.3169336)
 (18.0 -0.5 0.55 49.0 0.5 -0.25 31.025839)
 (27.0 -0.95 0.3 43.0 -0.1 0.2 16.021187)
 (5.0 -0.4 0.5 70.0 -0.75 -0.75 65.012665)
 (9.0 -1.0 -0.2 66.0 0.4 0.05 57.01702)
 (10.0 0.25 -0.75 13.0 -0.85 -0.75 3.1900785)
 (16.0 -0.65 -0.4 31.0 -0.6 -0.5 15.000405)
 (90.0 0.4 0.1 18.0 -0.6 -0.85 72.01298)
 (92.0 0.4 0.1 31.0 -0.7 0.2 61.009853)
 (99.0 -0.7 -0.5 40.0 -0.9 0.35 59.006287)
 (40.0 0.95 -0.2 62.0 -0.7 -0.25 22.06002)
 (16.0 0.5 0.7 35.0 0.35 -0.45 19.03436))
like image 41
whoplisp Avatar answered Oct 21 '22 11:10

whoplisp