{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "import os\n",
    "import numpy as np\n",
    "import cv2 \n",
    "import matplotlib.pyplot as plt"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "# I don't quite understand where this value comes from, given the various\n",
    "# Wikipedia values for D65, but this works.\n",
    "CIELAB_D65 = np.asarray ([0.9505, 1., 1.0890])\n",
    "\n",
    "X, Y, Z = range(3)\n",
    "L, A, B = range(3)\n",
    "\n",
    "# from Moreland:\n",
    "_linsrgb_to_xyz = np.asarray ([[0.4124, 0.2126, 0.0193],\n",
    "                               [0.3576, 0.7152, 0.1192],\n",
    "                               [0.1805, 0.0722, 0.9505]])\n",
    "\n",
    "# from Wikipedia, SRGB:\n",
    "_xyz_to_linsrgb = np.asarray ([[3.2406, -0.9689, 0.0557],\n",
    "                               [-1.5372, 1.8758, -0.2040],\n",
    "                               [-0.4986, 0.0415, 1.0570]])\n",
    "\n",
    "\n",
    "def srgb_to_linsrgb (srgb):\n",
    "    \"\"\"Convert sRGB values to physically linear ones. The transformation is\n",
    "    uniform in RGB, so *srgb* can be of any shape.\n",
    "\n",
    "    *srgb* values should range between 0 and 1, inclusively.\n",
    "\n",
    "    \"\"\"\n",
    "    srgb = srgb/255.0\n",
    "    gamma = ((srgb + 0.055) / 1.055)**2.4\n",
    "    scale = srgb / 12.92\n",
    "    return np.where (srgb > 0.04045, gamma, scale)\n",
    "\n",
    "def linsrgb_to_srgb (linsrgb):\n",
    "    \"\"\"Convert physically linear RGB values into sRGB ones. The transform is\n",
    "    uniform in the components, so *linsrgb* can be of any shape.\n",
    "\n",
    "    *linsrgb* values should range between 0 and 1, inclusively.\n",
    "\n",
    "    \"\"\"\n",
    "    # From Wikipedia, but easy analogue to the above.\n",
    "    gamma = 1.055 * linsrgb**(1./2.4) - 0.055\n",
    "    scale = linsrgb * 12.92\n",
    "    linsrgb = np.where (linsrgb > 0.0031308, gamma, scale)\n",
    "    linsrgb = linsrgb * 255.0\n",
    "    return linsrgb\n",
    "\n",
    "\n",
    "def linsrgb_to_xyz (linsrgb):\n",
    "    \"\"\"Convert linearized sRGB values (cf srgb_to_linsrgb) to CIE XYZ values.\n",
    "\n",
    "    *linsrgb* should be of shape (*, 3). Values should range between 0 and 1\n",
    "    inclusively. Return value will be of same shape.\n",
    "\n",
    "    Returned XYZ values range between [0, 0, 0] and [0.9505, 1., 1.089].\n",
    "\n",
    "    \"\"\"\n",
    "    return np.dot (linsrgb, _linsrgb_to_xyz)\n",
    "\n",
    "\n",
    "def xyz_to_linsrgb(xyz): \n",
    "    \"\"\"Convert CIE XYZ values to linearized sRGB values (cf srgb_to_linsrgb).\n",
    "\n",
    "    *xyz* should be of shape (*, 3)\n",
    "\n",
    "    Return value will be of same shape.\n",
    "\n",
    "    \"\"\"\n",
    "    return np.dot(xyz,_xyz_to_linsrgb)\n",
    "\n",
    "def xyz_to_cielab(xyz,refwhite=CIELAB_D65):\n",
    "    \"\"\"Convert CIE xyz color values to CIE L*a*b.\n",
    "    *xyz* should be of shape (*,3). *refwhite* is the reference white value of,\n",
    "    shape(3,)\n",
    "    \n",
    "    Return value will have same shape as *xyz*, but be in CIE L*a*b\n",
    "    coordinates.\n",
    "    \"\"\"\n",
    "    norm = xyz/refwhite\n",
    "    pow = norm**0.333333333333333\n",
    "    scale = 7.787037 * norm + 16./116\n",
    "    mapped = np.where(norm >  0.008856, pow, scale)\n",
    "    \n",
    "    cielab = np.empty_like(xyz)\n",
    "    cielab[...,L] = 116 * mapped[...,Y] - 16\n",
    "    cielab[...,A] = 500 * (mapped[...,X] - mapped[...,Y])\n",
    "    cielab[...,B] = 200 * (mapped[...,Y] - mapped[...,Z])\n",
    "    \n",
    "    return cielab\n",
    "\n",
    "\n",
    "    \n",
    "def cielab_to_xyz(cielab,refwhite=CIELAB_D65):\n",
    "    \"\"\"Convert CIE L*a*b* color values to CIE XYZ,\n",
    "\n",
    "    *cielab* should be of shape (*, 3). *refwhite* is the reference white\n",
    "    value in the L*a*b* color space, of shape (3, ).\n",
    "\n",
    "    Return value has same shape as *cielab*\n",
    "\n",
    "    \"\"\"\n",
    "    def func (t):\n",
    "        pow = t**3\n",
    "        scale = 0.128419 * t -  0.0177129\n",
    "        return np.where(t > 0.206897,pow,scale)\n",
    "    \n",
    "    xyz = np.empty_like(cielab)\n",
    "    lscale = 1./116 * (cielab[...,L] + 16)\n",
    "    xyz[...,X] = func(lscale + 0.002 * cielab[...,A])\n",
    "    xyz[...,Y] = func(lscale)\n",
    "    xyz[...,Z] = func(lscale - 0.005 * cielab[...,B])\n",
    "    xyz *= refwhite\n",
    "    return xyz\n",
    "\n",
    "def test_color_conversions():\n",
    "    testRGB = np.array([118,118,118])\n",
    "    print(\"The value of 18% gray - sRGB: \", testRGB)\n",
    "    \n",
    "    testsRGB_linsRGB = srgb_to_linsrgb(testRGB)\n",
    "    print(\"The value of 18% gray, sRGB - linear RGB: \", testsRGB_linsRGB)\n",
    "    \n",
    "    testlinsRGB_sRGB = linsrgb_to_srgb(testsRGB_linsRGB)\n",
    "    print(\"The value of 18% gray, linear RGB - sRGB: \", testlinsRGB_sRGB)\n",
    "    \n",
    "    testlinsRGB_XYZ = linsrgb_to_xyz(testsRGB_linsRGB)\n",
    "    print(\"The value of 18% gray - linear sRGB to CIE XYZ: \", testlinsRGB_XYZ)\n",
    "    \n",
    "    testlinsRGB_XYZ_LAB = xyz_to_cielab(testlinsRGB_XYZ)\n",
    "    print(\"The value of 18% gray - CIE XYZ to CIE LAB: \", testlinsRGB_XYZ_LAB)\n",
    "    \n",
    "    testLAB_XYZ = cielab_to_xyz(testlinsRGB_XYZ_LAB)\n",
    "    print(\"The value of 18% gray - CIE LAB to CIE XYZ: \", testLAB_XYZ)\n",
    "    \n",
    "    testXYZ_linsRGB = xyz_to_linsrgb(testLAB_XYZ)\n",
    "    print(\"The value of 18% gray - CIE XYZ to linear RGB: \", testXYZ_linsRGB)\n",
    "    \n",
    "    testlinsRGB_sRGB = linsrgb_to_srgb(testXYZ_linsRGB)\n",
    "    print(\"The value of 18% gray - linear RGB to sRGB: \", np.round(testlinsRGB_sRGB))"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
