View Raw SPL
rgb2lab(r, g, b)
{
        local ll, aa, bb, x, y, z, xn, yn, zn;
        local nr = -1;

        if (argc == 1)
        {
                if (length(r) == 3)
                {
                        nr = 3;
                        r = transpose(r);
                }

                if (numcols(r) == 3)
                {
                        g = col(r, 2);
                        b = col(r, 3);
                        r = col(r, 1);
                }
                else        
                {
                        /* assume image */
                        (r, g, b) = getrgb(r);

                        nr = numrows(r);
                }
        }
        else if (argc != 3)
        {
                error(sprintf("%s - 1 or 3 input arguments required", __FUNC__));
        }

        if (max(r) > 1 || max(g) > 1 || max(b) > 1)         
        {
                r = r / 255;
                g = g / 255;
                b = b / 255;
        }

        r = rgb2lab_gamma_correct(r);
        g = rgb2lab_gamma_correct(g);
        b = rgb2lab_gamma_correct(b);

        /* sRGB to XYZ */
        x = r * 0.4124564 + g * 0.3575761 + b * 0.1804375;

//        y = r * 0.2126    + g * 0.7152    + b * 0.0722;
        y = r * 0.2126729 + g * 0.7151521 + b * 0.0721750;

        z = r * 0.0193339 + g * 0.1191920 + b * 0.9503041;


//        x = r * 0.412410846488538800 + g * 0.35758456785295190 + b * 0.18045380393360833;
//        y = r * 0.212649342720652830 + g * 0.71516913570590380 + b * 0.07218152157344333;
//        z = r * 0.019331758429150258 + g * 0.11919485595098397 + b * 0.95039003405033730;

//        x = r * 0.4124564 + g * 0.3575761 + b * 0.1804375;
//        y = r * 0.2126729 + g * 0.7151522 + b * 0.0721750;
//        z = r * 0.0193339 + g * 0.1191920 + b * 0.9503041;

        /* reference white point (D65) */
        xn = 0.95047;
        yn = 1.0;
        zn = 1.08883;

        /* Convert XYZ to XYZ' */
        x = x / xn;
        y = y / yn;
        z = z / zn;
        
          /* 3. Calculate L*, a*, and b* */
        ll = 116 * rgb2lab_f(y) - 16;
        aa = 500 * (rgb2lab_f(x) - rgb2lab_f(y));
        bb = 200 * (rgb2lab_f(y) - rgb2lab_f(z));

        if (outargc <= 1)
        {
                /* cast to series */
                ll = {ll};
                aa = {aa};
                bb = {bb};
        }

        /* label */
        if (nr == 3)
        {
                setvunits(ll, "LAB");
        }
        else
        {
                if (isarray(ll)) setvunits(ll, "L✱");
                if (isarray(aa)) setvunits(aa, "a✱");
                if (isarray(bb)) setvunits(bb, "b✱");

        }

        if (outargc > 1)
        {
                return(ll, aa, bb);
        }
        else if (nr == 3)
        {
                return({ll, aa, bb});
        }
        else
        {
                return(ravel(ll, aa, bb));
        }
}


rgb2lab_gamma_correct(rgb)
{
        rgb = (rgb > 0.04045) ?  ((rgb + 0.055) / 1.055)^2.4 : rgb / 12.92;

        return(rgb);
}


rgb2lab_f0(t)
{
        local f;

        f = (t > 0.008856) ? t ^ (1/3) : 7.787 * t + 16/116;

        return(f);
}


rgb2lab_f(t)
{
        local f;
        static epsilon = (6/29)^3;
        static kappa   = (1/3) * ((29/6) ^ 2);

        f = (t > epsilon) ? t ^ (1/3) : kappa * t + 4/29;

        return(f);
}


rgb2lab_cie(t)
{
        local epsilon, kappa, f;
        static epsilon = 216.0 / 24389.0;
        static kappa   = 24389.0 / 27.0;

        f = (t > epsilon) ? t ^ (1/3) : (kappa * t + 16) / 116;

        return(f);
}