0.4.6 • Published 9 years ago

ciecam02 v0.4.6

Weekly downloads
3
License
BSD-2-Clause
Repository
github
Last release
9 years ago

API

Converters

cam(viewingConditions?, correlates?) -> {
	fromXyz(XYZ) -> CAM,
	toXyz(CAM) -> XYZ,
	fillOut(correlates, inputs) -> outputs
}

ucs(name?="UCS") -> {
	fromCam(CAM) -> UCS,
	toCam(UCS) -> CAM,
	distance(UCS1, UCS2) -> number
}

hq: {
	fromHue(h) -> H,
	toHue(H) -> h,
	fromNotation(N) -> H,
	toNotation(H) -> N
}

Default viewing conditions

{
	whitePoint: illuminant.D65,
	adaptingLuminance: 40,
	backgroundLuminance: 20,
	surroundType: "average",
	discounting: false
}

Gamut helpers

gamut(xyz, cam, epsilon?=1e-6) -> {
	contains(CAM) -> (boolean, RGB),
	limit(camIn, camOut, prec?=1e-3) -> CAM,
	spine(t) -> CAM
}

Misc helpers

cfs(str) -> correlates,
lerp(CAM1, CAM2, t) -> CAM

Examples

Getting back into the gamut

// The reference for understanding CIECAM02 is:
// http://www.springer.com/cda/content/document/cda_downloaddocument/9781441961891-c1.pdf

import * as ciebase from "ciebase";
import * as ciecam02 from "ciecam02";
import {merge} from "mout/object";

var {rgb, workspace, illuminant} = ciebase,
    {cfs} = ciecam02,
    xyz = ciebase.xyz(workspace.sRGB, illuminant.D65);

var viewingConditions = {
	whitePoint: illuminant.D65,
	adaptingLuminance: 40,
	backgroundLuminance: 20,
	surroundType: "average",
	discounting: false
};

// By default, 7 correlates are returned when converting from XYZ to CAM.
// For the purpose of this example, we will limit ourselves to the JCh correlates.
// (J is the lightness, C the chroma and h the hue.)
var cam = ciecam02.cam(viewingConditions, cfs("JCh")),
    gamut = ciecam02.gamut(xyz, cam),
    {min, max} = Math;

function hexToCam(hex) {
	return cam.fromXyz(xyz.fromRgb(rgb.fromHex(hex)));
}

function camToHex(CAM) {
	return rgb.toHex(xyz.toRgb(cam.toXyz(CAM)));
}

function crop (v) {
	return max(0, min(1, v));
}

var camSand = hexToCam("e0cda9"),                       // {J: 77.82, C: 16.99, h: 81.01}
    camOrange = merge(camSand, {C: 90}),                // {J: 77.82, C: 90.00, h: 81.01}
    [isInside, rgbOrange] = gamut.contains(camOrange);  // [false, [1.09, 0.73, -0.7]]

if (!isInside) {
	// The gamut.limit function interpolates between an inside and an outside point
	// and return an inside point as close as possible to the boundary.
	// (The gamut is the set of CAM values that maps to valid RGB coordinates.)
	let camOrange1 = gamut.limit(camSand, camOrange),                // {J: 77.82, C: 55.23, h: 81.01}
	// The alternative method is to simply crop the RGB coordinates
	    camOrange2 = cam.fromXyz(xyz.fromRgb(rgbOrange.map(crop)));  // {J: 74.43, C: 67.60, h: 81.30}
	console.log([camOrange1, camOrange2].map(camToHex));             // #ffc447   #ffb900
} else {
	console.log(rgb.toHex(rgbOrange));
}

Example 1 Output

Gradient

var {lerp} = ciecam02,
    camStart = hexToCam("17657d"),
    camEnd = hexToCam("fee7f0");

function gradient (camStart, camEnd, steps=3) {
	var result = [];
	for (var ε = 1/(steps+1), t = 0; steps > -2; t += ε, steps -= 1) {
		let camBetween = lerp(camStart, camEnd, crop(t)),
		    hex = rgb.toHex(xyz.toRgb(cam.toXyz(camBetween)).map(crop));
		result.push(hex);
	}
	return result;
}

var hexCodes = gradient(camStart, camEnd, 8);

Example 2 Output

Chroma maximization

import {map} from "mout/object";

var {hq} = ciecam02,
    ucs = ciecam02.ucs();

function ucsLimit (camIn, camOut, prec=1e-3) {
	// UCS is based on the JMh correlates
	var [ucsIn, ucsOut] = [camIn, camOut].map(v => ucs.fromCam(cam.fillOut(cfs("JMh"), v)));
	while (ucs.distance(ucsIn, ucsOut) > prec) {
		let ucsMid = lerp(ucsIn, ucsOut, 0.5),
		    [isInside,] = gamut.contains(ucs.toCam(ucsMid));
		if (isInside) {
			ucsIn = ucsMid;
		} else {
			ucsOut = ucsMid;
		}
	}
	return cam.fillOut(map(camIn, v => true), ucs.toCam(ucsIn));
}

// The hue notation is a different writting of the hue quadrant,
// of the form a(p?b)? where a and b are in {R, Y, G, B} (a ≠ b)
// and p is in ]0, 100[. apb = b(100-p)a, ab = a50b.
function hue (N) {
	return hq.toHue(hq.fromNotation(N));
}

var topChroma = max(...["f00", "0f0", "00f"].map(v => hexToCam(v).C)),
    camRed = {J: 60, h: hue("R")},
    camYellow = {J: 90, h: hue("Y")},
    camGreen = {J: 90, h: hue("G")},
    camBlue = {J: 70, h: hue("B")};

var hexCodes = [camRed, camYellow, camGreen, camBlue].map(function (CAM) {
	CAM = merge(CAM, {C: topChroma+1});
	CAM = ucsLimit(gamut.spine(CAM.J/100), CAM);
	return camToHex(CAM);
});

Example 3 Output

0.4.6

9 years ago

0.4.5

10 years ago

0.4.4

10 years ago

0.4.3

10 years ago

0.4.2

10 years ago

0.4.1

10 years ago

0.4.0

10 years ago

0.3.9

10 years ago

0.3.8

10 years ago

0.3.7

10 years ago

0.3.6

10 years ago

0.3.5

10 years ago

0.3.4

10 years ago

0.3.3

10 years ago

0.3.2

10 years ago

0.3.1

10 years ago

0.3.0

10 years ago

0.2.9

10 years ago

0.2.8

10 years ago

0.2.7

10 years ago

0.2.6

10 years ago

0.2.5

10 years ago

0.2.4

10 years ago

0.2.3

10 years ago

0.2.2

10 years ago

0.2.1

10 years ago

0.2.0

10 years ago

0.1.9

10 years ago

0.1.8

10 years ago

0.1.7

10 years ago

0.1.6

10 years ago

0.1.5

10 years ago

0.1.4

10 years ago

0.1.3

10 years ago

0.1.2

10 years ago

0.1.1

10 years ago

0.1.0

10 years ago