From 9cb903a90120402750a93fa4f2f577fcae897181 Mon Sep 17 00:00:00 2001
From: Ben Gordon <brgordon@ua.edu>
Date: Sat, 22 Feb 2014 13:57:36 -0600
Subject: [PATCH] Boatloads of Changes

* Added CIE_LAB transformations
* Changed color distance to use CIE standards
* Implemented CIE76, CIE94, CIEDE2000 color distance formulas.
* Added unit tests for CIE->{Array,Dictionary} checking
---
 Colours.h                                     | 131 +++++++-
 Colours.m                                     | 313 ++++++++++++++++--
 .../ColoursDemo Tests/ColoursDemo_Tests.m     |  36 ++
 3 files changed, 445 insertions(+), 35 deletions(-)

diff --git a/Colours.h b/Colours.h
index c045b7b..d4d62d8 100755
--- a/Colours.h
+++ b/Colours.h
@@ -33,6 +33,10 @@ static NSString * kColoursHSBA_H = @"HSBA-h";
 static NSString * kColoursHSBA_S = @"HSBA-s";
 static NSString * kColoursHSBA_B = @"HSBA-b";
 static NSString * kColoursHSBA_A = @"HSBA-a";
+static NSString * kColoursCIE_L = @"LABa-L";
+static NSString * kColoursCIE_A = @"LABa-A";
+static NSString * kColoursCIE_B = @"LABa-B";
+static NSString * kColoursCIE_alpha = @"LABa-a";
 
 
 #pragma mark - Create correct iOS/OSX interface
@@ -40,12 +44,10 @@ static NSString * kColoursHSBA_A = @"HSBA-a";
 #if TARGET_OS_IPHONE
 #import <UIKit/UIKit.h>
 @interface UIColor (Colours)
-#define COLOR_CLASS UIColor
 
 #elif TARGET_OS_MAC
 #import <AppKit/AppKit.h>
 @interface NSColor (Colours)
-#define COLOR_CLASS NSColor
 
 #endif
 
@@ -62,7 +64,15 @@ typedef NS_ENUM(NSInteger, ColorScheme) {
 // ColorFormulation Type
 typedef NS_ENUM(NSInteger, ColorFormulation) {
     ColorFormulationRGBA,
-    ColorFormulationHSBA
+    ColorFormulationHSBA,
+    ColorFormulationLAB
+};
+
+// ColorDistance
+typedef NS_ENUM(NSInteger, ColorDistance) {
+    ColorDistanceCIE76,
+    ColorDistanceCIE94,
+    ColorDistanceCIE2000,
 };
 
 
@@ -104,6 +114,21 @@ typedef NS_ENUM(NSInteger, ColorFormulation) {
  */
 + (instancetype)colorFromHSBADictionary:(NSDictionary *)hsbaDict;
 
+/**
+ Creates a Color from an array of 4 NSNumbers (L,a,b,alpha)
+ @param colors   4 NSNumbers for LABa between 0 - 1
+ @return Color
+ */
++ (instancetype)colorFromCIE_LabArray:(NSArray *)colors;
+
+/**
+ Creates a Color from a dictionary of 4 NSNumbers
+ Keys: kColoursCIE_L, kColoursCIE_A, kColoursCIE_B, kColoursCIE_alpha
+ @param colors   4 NSNumbers for CIE_LAB between 0 - 1
+ @return Color
+ */
++ (instancetype)colorFromCIE_LabDictionary:(NSDictionary *)colors;
+
 
 #pragma mark - Hex/RGBA/HSBA from Color
 /**
@@ -136,6 +161,20 @@ typedef NS_ENUM(NSInteger, ColorFormulation) {
  */
 - (NSDictionary *)hsbaDictionary;
 
+/**
+ *  Creates an array of 4 NSNumbers representing the float values of L*, a, b, alpha in that order.
+ *
+ *  @return NSArray
+ */
+- (NSArray *)CIE_LabArray;
+
+/**
+ *  Creates a dictionary of 4 NSNumbers representing the float values with keys: kColoursCIE_L, kColoursCIE_A, kColoursCIE_B, kColoursCIE_alpha
+ *
+ *  @return NSDictionary
+ */
+- (NSDictionary *)CIE_LabDictionary;
+
 
 #pragma mark - Color Components
 /**
@@ -145,6 +184,76 @@ typedef NS_ENUM(NSInteger, ColorFormulation) {
  */
 - (NSDictionary *)colorComponents;
 
+/**
+ *  Returns the red value from an RGBA formulation of the UIColor.
+ *
+ *  @return CGFloat
+ */
+- (CGFloat)red;
+
+/**
+ *  Returns the green value from an RGBA formulation of the UIColor.
+ *
+ *  @return CGFloat
+ */
+- (CGFloat)green;
+
+/**
+ *  Returns the blue value from an RGBA formulation of the UIColor.
+ *
+ *  @return CGFloat
+ */
+- (CGFloat)blue;
+
+/**
+ *  Returns the hue value from an HSBA formulation of the UIColor.
+ *
+ *  @return CGFloat
+ */
+- (CGFloat)hue;
+
+/**
+ *  Returns the saturation value from an HSBA formulation of the UIColor.
+ *
+ *  @return CGFloat
+ */
+- (CGFloat)saturation;
+
+/**
+ *  Returns the brightness value from an HSBA formulation of the UIColor.
+ *
+ *  @return CGFloat
+ */
+- (CGFloat)brightness;
+
+/**
+ *  Returns the alpha value from an RGBA formulation of the UIColor.
+ *
+ *  @return CGFloat
+ */
+- (CGFloat)alpha;
+
+/**
+ *  Returns the lightness value from a CIELAB formulation of the UIColor.
+ *
+ *  @return CGFloat
+ */
+- (CGFloat)CIE_Lightness;
+
+/**
+ *  Returns the a value from a CIELAB formulation of the UIColor.
+ *
+ *  @return CGFloat
+ */
+- (CGFloat)CIE_a;
+
+/**
+ *  Returns the lightness value from b CIELAB formulation of the UIColor.
+ *
+ *  @return CGFloat
+ */
+- (CGFloat)CIE_b;
+
 
 #pragma mark - 4 Color Scheme from Color
 /**
@@ -173,13 +282,25 @@ typedef NS_ENUM(NSInteger, ColorFormulation) {
 
 #pragma mark - Distance between Colors
 /**
- *  Returns a float of the distance between 2 colors.
+ *  Returns a float of the distance between 2 colors. Defaults to the
+ *  CIE94 specification found here: http://en.wikipedia.org/wiki/Color_difference
  *
  *  @param color Color to check self with.
  *
  *  @return CGFloat
  */
-- (CGFloat)distanceFromColor:(COLOR_CLASS *)color;
+- (CGFloat)distanceFromColor:(id)color;
+
+/**
+ *  Returns a float of the distance between 2 colors, using one of
+ *
+ *
+ *  @param color        Color to check against
+ *  @param distanceType Formula to calculate with
+ *
+ *  @return CGFloat
+ */
+- (CGFloat)distanceFromColor:(id)color type:(ColorDistance)distanceType;
 
 
 #pragma mark - Colors
diff --git a/Colours.m b/Colours.m
index 392ade9..1f7f79c 100755
--- a/Colours.m
+++ b/Colours.m
@@ -27,12 +27,10 @@
 #if TARGET_OS_IPHONE
 #import <UIKit/UIKit.h>
 @implementation UIColor (Colours)
-#define COLOR_CLASS UIColor
 
 #elif TARGET_OS_MAC
 #import <AppKit/AppKit.h>
 @implementation NSColor (Colours)
-#define COLOR_CLASS NSColor
 
 #endif
 
@@ -44,7 +42,7 @@ + (instancetype)colorFromHexString:(NSString *)hexString
     hexString = [hexString stringByReplacingOccurrencesOfString:@"#" withString:@""];
     NSScanner *scanner = [NSScanner scannerWithString:hexString];
     [scanner scanHexInt:&rgbValue];
-
+    
     return [[self class] colorWithR:((rgbValue & 0xFF0000) >> 16) G:((rgbValue & 0xFF00) >> 8) B:(rgbValue & 0xFF) A:1.0];
 }
 
@@ -59,7 +57,7 @@ - (NSString *)hexString
     NSString *red = [NSString stringWithFormat:@"%02x", r];
     NSString *green = [NSString stringWithFormat:@"%02x", g];
     NSString *blue = [NSString stringWithFormat:@"%02x", b];
-
+    
     return [NSString stringWithFormat:@"#%@%@%@", red, green, blue];
 }
 
@@ -70,7 +68,7 @@ + (instancetype)colorFromRGBAArray:(NSArray *)rgbaArray
     if (rgbaArray.count < 4) {
         return [[self class] clearColor];
     }
-
+    
     return [[self class] colorWithRed:[rgbaArray[0] floatValue]
                                 green:[rgbaArray[1] floatValue]
                                  blue:[rgbaArray[2] floatValue]
@@ -85,7 +83,7 @@ + (instancetype)colorFromRGBADictionary:(NSDictionary *)rgbaDict
                                      blue:[rgbaDict[kColoursRGBA_B] floatValue]
                                     alpha:[rgbaDict[kColoursRGBA_A] floatValue]];
     }
-
+    
     return [[self class] clearColor];
 }
 
@@ -94,7 +92,7 @@ + (instancetype)colorFromRGBADictionary:(NSDictionary *)rgbaDict
 - (NSArray *)rgbaArray
 {
     CGFloat r=0,g=0,b=0,a=0;
-
+    
     if ([self respondsToSelector:@selector(getRed:green:blue:alpha:)]) {
         [self getRed:&r green:&g blue:&b alpha:&a];
     }
@@ -125,7 +123,7 @@ - (NSDictionary *)rgbaDictionary
         b = components[2];
         a = components[3];
     }
-
+    
     return @{kColoursRGBA_R:@(r),
              kColoursRGBA_G:@(g),
              kColoursRGBA_B:@(b),
@@ -138,11 +136,11 @@ - (NSArray *)hsbaArray
 {
     // Takes a [self class] and returns Hue,Saturation,Brightness,Alpha values in NSNumber form
     CGFloat h=0,s=0,b=0,a=0;
-
+    
     if ([self respondsToSelector:@selector(getHue:saturation:brightness:alpha:)]) {
         [self getHue:&h saturation:&s brightness:&b alpha:&a];
     }
-
+    
     return @[@(h),
              @(s),
              @(b),
@@ -152,11 +150,11 @@ - (NSArray *)hsbaArray
 - (NSDictionary *)hsbaDictionary
 {
     CGFloat h=0,s=0,b=0,a=0;
-
+    
     if ([self respondsToSelector:@selector(getHue:saturation:brightness:alpha:)]) {
         [self getHue:&h saturation:&s brightness:&b alpha:&a];
     }
-
+    
     return @{kColoursHSBA_H:@(h),
              kColoursHSBA_S:@(s),
              kColoursHSBA_B:@(b),
@@ -164,13 +162,13 @@ - (NSDictionary *)hsbaDictionary
 }
 
 
-#pragma mark Color from HSBA
+#pragma mark - Color from HSBA
 + (instancetype)colorFromHSBAArray:(NSArray *)hsbaArray
 {
     if (hsbaArray.count < 4) {
         return [[self class] clearColor];
     }
-
+    
     return [[self class] colorWithHue:[hsbaArray[0] doubleValue]
                            saturation:[hsbaArray[1] doubleValue]
                            brightness:[hsbaArray[2] doubleValue]
@@ -185,19 +183,177 @@ + (instancetype)colorFromHSBADictionary:(NSDictionary *)hsbaDict
                                brightness:[hsbaDict[kColoursHSBA_B] doubleValue]
                                     alpha:[hsbaDict[kColoursHSBA_A] doubleValue]];
     }
-
+    
     return [[self class] clearColor];
 }
 
 
+#pragma mark - LAB from Color
+- (NSArray *)CIE_LabArray {
+    // Convert Color to XYZ format first
+    NSArray *rgba = [self rgbaArray];
+    CGFloat R = [rgba[0] floatValue];
+    CGFloat G = [rgba[1] floatValue];
+    CGFloat B = [rgba[2] floatValue];
+    
+    // Create deltaR block
+    void (^deltaRGB)(CGFloat *R);
+    deltaRGB = ^(CGFloat *R) {
+        *R = (*R > 0.04045) ? pow((*R + 0.055)/1.055, 2.40) : (*R/12.92);
+    };
+    deltaRGB(&R);
+    deltaRGB(&G);
+    deltaRGB(&B);
+    CGFloat X = R*41.24 + G*35.76 + B*18.05;
+    CGFloat Y = R*21.26 + G*71.52 + B*7.22;
+    CGFloat Z = R*1.93 + G*11.92 + B*95.05;
+    
+    // Convert XYZ to L*a*b*
+    X = X/95.047;
+    Y = Y/100.000;
+    Z = Z/108.883;
+    
+    // Create deltaF block
+    void (^deltaF)(CGFloat *f);
+    deltaF = ^(CGFloat *f){
+        *f = (*f > pow((6.0/29.0), 3.0)) ? pow(*f, 1.0/3.0) : (1/3)*pow((29.0/6.0), 2.0) * *f + 4/29.0;
+    };
+    deltaF(&X);
+    deltaF(&Y);
+    deltaF(&Z);
+    NSNumber *L = @(116*Y - 16);
+    NSNumber *a = @(500 * (X - Y));
+    NSNumber *b = @(200 * (Y - Z));
+    
+    return @[L,
+             a,
+             b,
+             rgba[3]];
+}
+
+- (NSDictionary *)CIE_LabDictionary {
+    NSArray *colors = [self CIE_LabArray];
+    return @{kColoursCIE_L:colors[0],
+             kColoursCIE_A:colors[1],
+             kColoursCIE_B:colors[2],
+             kColoursCIE_alpha:colors[3],};
+}
+
+
+#pragma mark Color from LAB
++ (instancetype)colorFromCIE_LabArray:(NSArray *)colors {
+    if (!colors) {
+        return [UIColor clearColor];
+    }
+    
+    // Convert LAB to XYZ
+    CGFloat L = [colors[0] floatValue];
+    CGFloat A = [colors[1] floatValue];
+    CGFloat B = [colors[2] floatValue];
+    CGFloat Y = (L + 16.0)/116.0;
+    CGFloat X = A/500 + Y;
+    CGFloat Z = Y - B/200;
+    
+    void (^deltaXYZ)(CGFloat *);
+    deltaXYZ = ^(CGFloat *k){
+        *k = (pow(*k, 3.0) > 0.008856) ? pow(*k, 3.0) : (*k - 4/29.0)/7.787;
+    };
+    
+    deltaXYZ(&X);
+    deltaXYZ(&Y);
+    deltaXYZ(&Z);
+    X = X*.95047;
+    Y = Y*1.00000;
+    Z = Z*1.08883;
+    
+    // Convert XYZ to RGB
+    CGFloat R = X*3.2406 + Y*-1.5372 + Z*-0.4986;
+    CGFloat G = X*-0.9689 + Y*1.8758 + Z*0.0415;
+    CGFloat _B = X*0.0557 + Y*-0.2040 + Z*1.0570;
+    
+    void (^deltaRGB)(CGFloat *);
+    deltaRGB = ^(CGFloat *k){
+        *k = (*k > 0.0031308) ? 1.055 * (pow(*k, (1/2.4))) - 0.055 : *k * 12.92;
+    };
+    
+    deltaRGB(&R);
+    deltaRGB(&G);
+    deltaRGB(&_B);
+    
+    // return Color
+    return [[self class] colorFromRGBAArray:@[@(R), @(G), @(_B), colors[3]]];
+}
+
++ (instancetype)colorFromCIE_LabDictionary:(NSDictionary *)colors {
+    if (!colors) {
+        return [[self class] clearColor];
+    }
+    
+    return [self colorFromCIE_LabArray:@[colors[kColoursCIE_L],
+                                         colors[kColoursCIE_A],
+                                         colors[kColoursCIE_B],
+                                         colors[kColoursCIE_alpha]]];
+}
+
+
 #pragma mark - Color Components
 - (NSDictionary *)colorComponents
 {
     NSMutableDictionary *components = [[self rgbaDictionary] mutableCopy];
     [components addEntriesFromDictionary:[self hsbaDictionary]];
+    [components addEntriesFromDictionary:[self CIE_LabDictionary]];
     return components;
 }
 
+- (CGFloat)red
+{
+    return [[self rgbaArray][0] floatValue];
+}
+
+- (CGFloat)green
+{
+    return [[self rgbaArray][1] floatValue];
+}
+
+- (CGFloat)blue
+{
+    return [[self rgbaArray][2] floatValue];
+}
+
+- (CGFloat)hue
+{
+    return [[self hsbaArray][0] floatValue];
+}
+
+- (CGFloat)saturation
+{
+    return [[self hsbaArray][1] floatValue];
+}
+
+- (CGFloat)brightness
+{
+    return [[self hsbaArray][2] floatValue];
+}
+
+- (CGFloat)alpha
+{
+    return [[self rgbaArray][3] floatValue];
+}
+
+- (CGFloat)CIE_Lightness
+{
+    return [[self CIE_LabArray][0] floatValue];
+}
+
+- (CGFloat)CIE_a
+{
+    return [[self CIE_LabArray][1] floatValue];
+}
+
+- (CGFloat)CIE_b
+{
+    return [[self CIE_LabArray][2] floatValue];
+}
 
 #pragma mark - Generate Color Scheme
 - (NSArray *)colorSchemeOfType:(ColorScheme)type
@@ -207,7 +363,7 @@ - (NSArray *)colorSchemeOfType:(ColorScheme)type
     float sat = [hsbArray[1] floatValue] * 100;
     float bright = [hsbArray[2] floatValue] * 100;
     float alpha = [hsbArray[3] floatValue];
-
+    
     switch (type) {
         case ColorSchemeAnalagous:
             return [[self class] analagousColorsFromHue:hue saturation:sat brightness:bright alpha:alpha];
@@ -273,27 +429,119 @@ - (instancetype)complementaryColor
     float newH = [[self class] addDegrees:180.0f toDegree:([hsba[kColoursHSBA_H] floatValue]*360)];
     [hsba setObject:@(newH) forKey:kColoursHSBA_H];
     return [[self class] colorFromHSBADictionary:hsba];
-
+    
 }
 
 
 #pragma mark - Distance between Colors
-- (CGFloat)distanceFromColor:(COLOR_CLASS *)color
-{
-    // General solution idea from: http://www.compuphase.com/cmetric.htm
-    NSArray *selfColorArray = [self rgbaArray];
-    NSArray *checkColorArray = [color rgbaArray];
+- (CGFloat)distanceFromColor:(id)color
+{
+    // Defaults to CIE94
+    return [self distanceFromColor:color type:ColorDistanceCIE94];
+}
+
+- (CGFloat)distanceFromColor:(id)color type:(ColorDistance)distanceType {
+    /**
+     *
+     *  Detecting a difference in two colors is not as trivial as it sounds.
+     *  One's first instinct is to go for a difference in RGB values, leaving
+     *  you with a sum of the differences of each point. It looks great! Until
+     *  you actually start comparing colors. Why do these two reds have a different
+     *  distance than these two blues *in real life* vs computationally?
+     *  Human visual perception is next in the line of things between a color
+     *  and your brain. Some colors are just perceived to have larger variants inside
+     *  of their respective areas than others, so we need a way to model this
+     *  human variable to colors. Enter CIELAB. This color formulation is supposed to be
+     *  this model. So now we need to standardize a unit of distance between any two
+     *  colors that works independent of how humans visually perceive that distance.
+     *  Enter CIE76,94,2000. These are methods that use user-tested data and other
+     *  mathematically and statistically significant correlations to output this info.
+     *  You can read the wiki articles below to get a better understanding historically
+     *  of how we moved to newer and better color distance formulas, and what
+     *  their respective pros/cons are.
+     *
+     *  References:
+     *  
+     *  http://en.wikipedia.org/wiki/Color_difference
+     *  http://en.wikipedia.org/wiki/Just_noticeable_difference
+     *  http://en.wikipedia.org/wiki/CIELAB
+     *
+     */
+    
+    // Check if it's a color
+    if (![color isKindOfClass:[UIColor class]]) {
+        // NSLog(@"Not a %@ object.", NSStringFromClass([self class]));
+        return MAXFLOAT;
+    }
+    
+    // Set Up Common Variables
+    NSArray *lab1 = [self CIE_LabArray];
+    NSArray *lab2 = [color CIE_LabArray];
+    CGFloat L1 = [lab1[0] floatValue];
+    CGFloat A1 = [lab1[1] floatValue];
+    CGFloat B1 = [lab1[2] floatValue];
+    CGFloat L2 = [lab2[0] floatValue];
+    CGFloat A2 = [lab2[1] floatValue];
+    CGFloat B2 = [lab2[2] floatValue];
+    
+    // CIE76 first
+    if (distanceType == ColorDistanceCIE76) {
+        CGFloat distance = sqrtf(pow((L1-L2), 2) + pow((A1-A2), 2) + pow((B1-B2), 2));
+        return distance;
+    }
+    
+    // More Common Variables
+    CGFloat kL = 1;
+    CGFloat kC = 1;
+    CGFloat kH = 1;
+    CGFloat k1 = 0.045;
+    CGFloat k2 = 0.015;
+    CGFloat deltaL = L1 - L2;
+    CGFloat C1 = sqrt((A1*A1) + (B1*B1));
+    CGFloat C2 = sqrt((A2*A2) + (B2*B2));
+    CGFloat deltaC = C1 - C2;
+    CGFloat deltaH = sqrt(pow((A1-A2), 2.0) + pow((B1-B2), 2.0) - pow(deltaC, 2.0));
+    CGFloat sL = 1;
+    CGFloat sC = 1 + k1*(sqrt((A1*A1) + (B1*B1)));
+    CGFloat sH = 1 + k2*(sqrt((A1*A1) + (B1*B1)));
     
-    // Set up variables
-    CGFloat meanR = ([selfColorArray[0] doubleValue]*255.0f + [checkColorArray[0] doubleValue]*255.0f)/2;
-    CGFloat deltaR = [selfColorArray[0] doubleValue]*255.0f - [checkColorArray[0] doubleValue]*255.0f;
-    CGFloat deltaG = [selfColorArray[1] doubleValue]*255.0f - [checkColorArray[1] doubleValue]*255.0f;
-    CGFloat deltaB = [selfColorArray[2] doubleValue]*255.0f - [checkColorArray[2] doubleValue]*255.0f;
+    // CIE94
+    if (distanceType == ColorDistanceCIE94) {
+        return sqrt(pow((deltaL/(kL*sL)), 2.0) + pow((deltaC/(kC*sC)), 2.0) + pow((deltaH/(kH*sH)), 2.0));
+    }
     
-    // Calculate Distance
-    CGFloat distance = sqrtf((2 + (meanR/256))*(deltaR*deltaR) + 4*(deltaG*deltaG) + (2 + (255 - meanR)/256)*(deltaB*deltaB))/255.0f;
+    // CIE2000
+    // More variables
+    CGFloat deltaLPrime = L2 - L1;
+    CGFloat meanL = (L1 + L2)/2;
+    CGFloat meanC = (C1 + C2)/2;
+    CGFloat aPrime1 = A1 + A1/2*(1 - sqrt(pow(meanC, 7.0)/(pow(meanC, 7.0) + pow(25.0, 7.0))));
+    CGFloat aPrime2 = A2 + A2/2*(1 - sqrt(pow(meanC, 7.0)/(pow(meanC, 7.0) + pow(25.0, 7.0))));
+    CGFloat cPrime1 = sqrt((aPrime1*aPrime1) + (B1*B1));
+    CGFloat cPrime2 = sqrt((aPrime2*aPrime2) + (B2*B2));
+    CGFloat cMeanPrime = (cPrime1 + cPrime2)/2;
+    CGFloat deltaCPrime = cPrime1 - cPrime2;
+    CGFloat hPrime1 = atan2(B1, aPrime1);
+    CGFloat hPrime2 = atan2(B2, aPrime2);
+    hPrime1 = fmodf(hPrime1, [self radiansFromDegree:360]);
+    hPrime2 = fmodf(hPrime2, [self radiansFromDegree:360]);
+    CGFloat deltahPrime = 0;
+    if (fabsf(hPrime1 - hPrime2) <= [self radiansFromDegree:180]) {
+        deltahPrime = hPrime2 - hPrime1;
+    }
+    else {
+        deltahPrime = (hPrime2 <= hPrime1) ? hPrime2 - hPrime1 + [self radiansFromDegree:360] : hPrime2 - hPrime1 - [self radiansFromDegree:360];
+    }
+    CGFloat deltaHPrime = 2 * sqrt(cPrime1*cPrime2) * sin(deltahPrime/2);
+    CGFloat meanHPrime = (fabsf(hPrime1 - hPrime2) <= [self radiansFromDegree:180]) ? (hPrime1 + hPrime2)/2 : (hPrime1 + hPrime2 + [self radiansFromDegree:360])/2;
+    CGFloat T = 1 - 0.17*cos(meanHPrime - [self radiansFromDegree:30]) + 0.24*cos(2*meanHPrime)+0.32*cos(3*meanHPrime + [self radiansFromDegree:6]) - 0.20*cos(4*meanHPrime - [self radiansFromDegree:63]);
+    sL = 1 + (0.015 * pow((meanL - 50), 2))/sqrt(20 + pow((meanL - 50), 2));
+    sC = 1 + 0.045*cMeanPrime;
+    sH = 1 + 0.015*cMeanPrime*T;
+    CGFloat Rt = -2 * sqrt(pow(cMeanPrime, 7)/(pow(cMeanPrime, 7) + pow(25.0, 7))) * sin([self radiansFromDegree:60]* exp(-1 * pow((meanHPrime - [self radiansFromDegree:275])/[self radiansFromDegree:25], 2)));
     
-    return distance;
+    // Finally return CIE2000 distance
+    return sqrt(pow((deltaLPrime/(kL*sL)), 2) + pow((deltaCPrime/(kC*sC)), 2) + pow((deltaHPrime/(kH*sH)), 2) + Rt*(deltaC/(kC*sC))*(deltaHPrime/(kH*sH)));
 }
 
 
@@ -838,4 +1086,9 @@ + (float)addDegrees:(float)addDeg toDegree:(float)staticDeg
     }
 }
 
+- (CGFloat)radiansFromDegree:(CGFloat)degree {
+    return degree * M_PI/180;
+}
+
+
 @end
diff --git a/ColoursDemo/ColoursDemo Tests/ColoursDemo_Tests.m b/ColoursDemo/ColoursDemo Tests/ColoursDemo_Tests.m
index 59f6ff8..4a25ab9 100644
--- a/ColoursDemo/ColoursDemo Tests/ColoursDemo_Tests.m	
+++ b/ColoursDemo/ColoursDemo Tests/ColoursDemo_Tests.m	
@@ -100,6 +100,42 @@ - (void)testToAndFromHSBADictionary {
     XCTAssertEqualObjects(testColor, [UIColor redColor], @"Serializing to and from HSBA Dictionary does not yield the same color.");
 }
 
+- (void)testToAndFromCIE_LabArray {
+    NSArray *testArray = [[UIColor tomatoColor] CIE_LabArray];
+    UIColor *testColor = [UIColor colorFromCIE_LabArray:testArray];
+    
+    CGFloat R1 = [[UIColor tomatoColor] red];
+    CGFloat R2 =[testColor red];
+    CGFloat G1 = [[UIColor tomatoColor] green];
+    CGFloat G2 =[testColor green];
+    CGFloat B1 = [[UIColor tomatoColor] blue];
+    CGFloat B2 =[testColor blue];
+    CGFloat A1 = [[UIColor tomatoColor] alpha];
+    CGFloat A2 =[testColor alpha];
+    XCTAssertEqualWithAccuracy(R1, R2, 0.001,  @"Serializing to and from CIE_LAB Array does not yield the same color.");
+    XCTAssertEqualWithAccuracy(G1, G2, 0.001,  @"Serializing to and from CIE_LAB Array does not yield the same color.");
+    XCTAssertEqualWithAccuracy(B1, B2, 0.001,  @"Serializing to and from CIE_LAB Array does not yield the same color.");
+    XCTAssertEqualWithAccuracy(A1, A2, 0.001,  @"Serializing to and from CIE_LAB Array does not yield the same color.");
+}
+
+- (void)testToAndFromCIE_LabDictionary {
+    NSDictionary *testDictionary = [[UIColor tomatoColor] CIE_LabDictionary];
+    UIColor *testColor = [UIColor colorFromCIE_LabDictionary:testDictionary];
+    
+    CGFloat R1 = [[UIColor tomatoColor] red];
+    CGFloat R2 =[testColor red];
+    CGFloat G1 = [[UIColor tomatoColor] green];
+    CGFloat G2 =[testColor green];
+    CGFloat B1 = [[UIColor tomatoColor] blue];
+    CGFloat B2 =[testColor blue];
+    CGFloat A1 = [[UIColor tomatoColor] alpha];
+    CGFloat A2 =[testColor alpha];
+    XCTAssertEqualWithAccuracy(R1, R2, 0.001,  @"Serializing to and from CIE_LAB Dictionary does not yield the same color.");
+    XCTAssertEqualWithAccuracy(G1, G2, 0.001,  @"Serializing to and from CIE_LAB Dictionary does not yield the same color.");
+    XCTAssertEqualWithAccuracy(B1, B2, 0.001,  @"Serializing to and from CIE_LAB Dictionary does not yield the same color.");
+    XCTAssertEqualWithAccuracy(A1, A2, 0.001,  @"Serializing to and from CIE_LAB Dictionary does not yield the same color.");
+}
+
 - (void)testContrastingColors {
     XCTAssertEqualObjects([[UIColor eggplantColor] blackOrWhiteContrastingColor], [UIColor whiteColor], @"Contrasting color over dark does not yield white.");
     XCTAssertEqualObjects([[UIColor antiqueWhiteColor] blackOrWhiteContrastingColor], [UIColor blackColor], @"Contrasting color over light does not yield black.");