diff options
author | Tavian Barnes <tavianator@tavianator.com> | 2014-03-11 19:51:47 -0400 |
---|---|---|
committer | Tavian Barnes <tavianator@tavianator.com> | 2014-03-11 19:55:07 -0400 |
commit | 746fdfc554b5584476df93b6c8717d04814cc2d4 (patch) | |
tree | cf18375a8e7561767315b339365ad5f288a9137b /color.c | |
parent | cd9936f9b09720c949c5fe8262eca45c31244fe3 (diff) | |
download | kd-forest-746fdfc554b5584476df93b6c8717d04814cc2d4.tar.xz |
Allow different color spaces to be used for similarity measurement.
Diffstat (limited to 'color.c')
-rw-r--r-- | color.c | 119 |
1 files changed, 119 insertions, 0 deletions
@@ -0,0 +1,119 @@ +/********************************************************************* + * kd-forest * + * Copyright (C) 2014 Tavian Barnes <tavianator@tavianator.com> * + * * + * This program is free software. It comes without any warranty, to * + * the extent permitted by applicable law. You can redistribute it * + * and/or modify it under the terms of the Do What The Fuck You Want * + * To Public License, Version 2, as published by Sam Hocevar. See * + * the COPYING file or http://www.wtfpl.net/ for more details. * + *********************************************************************/ + +#include "color.h" +#include <math.h> + +void +color_unpack(uint8_t pixel[3], uint32_t color) +{ + pixel[0] = (color >> 16) & 0xFF; + pixel[1] = (color >> 8) & 0xFF; + pixel[2] = color & 0xFF; +} + +void +color_set_RGB(double coords[3], uint32_t color) +{ + uint8_t pixel[3]; + color_unpack(pixel, color); + for (int i = 0; i < 3; ++i) { + coords[i] = pixel[i]/255.0; + } +} + +// Inverse gamma for sRGB +double +sRGB_C_inv(double t) +{ + if (t <= 0.040449936) { + return t/12.92; + } else { + return pow((t + 0.055)/1.055, 2.4); + } +} + +static void +color_set_XYZ(double XYZ[3], uint32_t color) +{ + double RGB[3]; + color_set_RGB(RGB, color); + + RGB[0] = sRGB_C_inv(RGB[0]); + RGB[1] = sRGB_C_inv(RGB[1]); + RGB[2] = sRGB_C_inv(RGB[2]); + + XYZ[0] = 0.4123808838268995*RGB[0] + 0.3575728355732478*RGB[1] + 0.1804522977447919*RGB[2]; + XYZ[1] = 0.2126198631048975*RGB[0] + 0.7151387878413206*RGB[1] + 0.0721499433963131*RGB[2]; + XYZ[2] = 0.0193434956789248*RGB[0] + 0.1192121694056356*RGB[1] + 0.9505065664127130*RGB[2]; +} + +// CIE L*a*b* and L*u*v* gamma +static double +Lab_f(double t) +{ + if (t > 216.0/24389.0) { + return pow(t, 1.0/3.0); + } else { + return 841.0*t/108.0 + 4.0/29.0; + } +} + +// sRGB white point (CIE D50) in XYZ coordinates +static const double WHITE[] = { + [0] = 0.9504060171449392, + [1] = 0.9999085943425312, + [2] = 1.089062231497274, +}; + +void +color_set_Lab(double coords[3], uint32_t color) +{ + double XYZ[3]; + color_set_XYZ(XYZ, color); + + double fXYZ[] = { + [0] = Lab_f(XYZ[0]/WHITE[0]), + [1] = Lab_f(XYZ[1]/WHITE[1]), + [2] = Lab_f(XYZ[2]/WHITE[2]), + }; + + coords[0] = 116.0*fXYZ[1] - 16.0; + coords[1] = 500.0*(fXYZ[0] - fXYZ[1]); + coords[2] = 200.0*(fXYZ[1] - fXYZ[2]); +} + +void +color_set_Luv(double coords[3], uint32_t color) +{ + double XYZ[3]; + color_set_XYZ(XYZ, color); + + double uv_denom = XYZ[0] + 15.0*XYZ[1] + 3.0*XYZ[2]; + if (uv_denom == 0.0) { + coords[0] = 0.0; + coords[1] = 0.0; + coords[2] = 0.0; + return; + } + + double white_uv_denom = WHITE[0] + 16.0*WHITE[1] + 3.0*WHITE[2]; + + double fY = Lab_f(XYZ[1]/WHITE[1]); + double uprime = 4.0*XYZ[0]/uv_denom; + double unprime = 4.0*WHITE[0]/white_uv_denom; + double vprime = 9.0*XYZ[1]/uv_denom; + double vnprime = 9.0*WHITE[1]/white_uv_denom; + + coords[0] = 116.0*fY - 16.0; + coords[1] = 13.0*coords[0]*(uprime - unprime); + coords[2] = 13.0*coords[0]*(vprime - vnprime); +} |