Professional online tool to convert CIE XYZ tristimulus values to CIE 1960 UCS (Uniform Chromaticity Scale) coordinates (u, v). Essential for color temperature calculations, LED characterization, lighting design, and optical device calibration in the lighting industry.
Convert XYZ coordinates to UV chromaticity coordinates with real-time calculations
The CIE 1960 UCS (Uniform Chromaticity Scale) was developed by David MacAdam to create a more perceptually uniform chromaticity diagram. Unlike the CIE 1931 xy chromaticity diagram, the UCS provides better visual uniformity where equal distances represent more similar perceived color differences.
Key advantages of UV coordinates:
Industry Note: The CIE 1960 UCS is still widely used in the lighting industry for color temperature specifications, even though it was superseded by the CIE 1976 UCS (u', v') for general colorimetric applications.
Input Parameters:
Output Coordinates:
Mathematical Note: The denominator (X + 15Y + 3Z) normalizes the coordinates, ensuring they represent chromaticity (color quality) independent of luminance. The coefficients 4, 15, 6, and 3 were chosen by MacAdam to optimize perceptual uniformity.
UV coordinates are fundamental for calculating Correlated Color Temperature (CCT), which describes how "warm" or "cool" a light source appears. This is crucial in the lighting industry for LED specification and quality control.
CIE 1960 UCS uses (u, v) coordinates, while CIE 1976 UCS uses (u', v') coordinates. The 1976 version provides better perceptual uniformity and is preferred for general colorimetric applications. However, the 1960 version is still widely used in the lighting industry for color temperature calculations and LED specifications.
UV coordinates enable precise LED binning and color consistency control. LEDs with similar UV coordinates will appear the same color to human observers, making them essential for quality control in LED production. The ANSI C78.377 standard uses UV coordinates to define acceptable color variation ranges for LED products.
The conversion is mathematically exact and deterministic. The accuracy depends on the precision of your input XYZ values. Our tool uses double-precision floating-point arithmetic to ensure maximum accuracy for professional applications. The conversion follows the official CIE formulas without approximations.
Yes! UV coordinates are essential for calculating Correlated Color Temperature (CCT). The process involves finding the closest point on the Planckian locus in the UV diagram and using methods like Robertson's algorithm to determine the temperature in Kelvin. This is the standard approach used in the lighting industry.
For visible colors, u coordinates typically range from about 0.0 to 0.7, and v coordinates from 0.0 to 0.6. Common white light sources have u values around 0.15-0.25 and v values around 0.3-0.5. The exact values depend on the color temperature and tint of the light source.
import numpy as np
def xyz_to_uv(X, Y, Z):
"""
Convert CIE XYZ to CIE 1960 UCS coordinates
Parameters:
X, Y, Z: float or array-like
CIE XYZ tristimulus values
Returns:
tuple: (u, v) chromaticity coordinates
"""
# Handle array inputs
X, Y, Z = np.asarray(X), np.asarray(Y), np.asarray(Z)
# Calculate denominator
denominator = X + 15*Y + 3*Z
# Handle division by zero
mask = denominator != 0
u = np.zeros_like(X)
v = np.zeros_like(Y)
u[mask] = (4 * X[mask]) / denominator[mask]
v[mask] = (6 * Y[mask]) / denominator[mask]
return u, v
# Example usage
X, Y, Z = 41.24, 21.26, 1.93 # sRGB Red (D65)
u, v = xyz_to_uv(X, Y, Z)
print(f"UV: ({u:.6f}, {v:.6f})")
# Batch processing example
xyz_colors = np.array([[41.24, 21.26, 1.93], # Red
[35.76, 71.52, 11.92], # Green
[18.05, 7.22, 95.05]]) # Blue
u_vals, v_vals = xyz_to_uv(xyz_colors[:, 0],
xyz_colors[:, 1],
xyz_colors[:, 2])
for i, (u, v) in enumerate(zip(u_vals, v_vals)):
print(f"Color {i+1}: u={u:.4f}, v={v:.4f}")
/**
* Convert CIE XYZ to CIE 1960 UCS coordinates
* @param {number} X - X tristimulus value
* @param {number} Y - Y tristimulus value
* @param {number} Z - Z tristimulus value
* @returns {Object} {u, v} chromaticity coordinates
*/
function xyzToUv(X, Y, Z) {
// Validate inputs
if (typeof X !== 'number' || typeof Y !== 'number' || typeof Z !== 'number') {
throw new Error('XYZ values must be numbers');
}
// Calculate denominator
const denominator = X + 15*Y + 3*Z;
// Handle edge case
if (denominator === 0) {
return { u: 0, v: 0 };
}
const u = (4 * X) / denominator;
const v = (6 * Y) / denominator;
return { u, v };
}
// Class-based implementation for color management
class ColorConverter {
static xyzToUv(xyz) {
const { X, Y, Z } = xyz;
return xyzToUv(X, Y, Z);
}
static batchXyzToUv(xyzArray) {
return xyzArray.map(xyz => this.xyzToUv(xyz));
}
}
// Example usage
const srgbRed = { X: 41.24, Y: 21.26, Z: 1.93 };
const uv = ColorConverter.xyzToUv(srgbRed);
console.log(`UV: (${uv.u.toFixed(6)}, ${uv.v.toFixed(6)})`);
// Batch processing
const colors = [
{ X: 41.24, Y: 21.26, Z: 1.93 }, // Red
{ X: 35.76, Y: 71.52, Z: 11.92 }, // Green
{ X: 18.05, Y: 7.22, Z: 95.05 } // Blue
];
const uvResults = ColorConverter.batchXyzToUv(colors);
uvResults.forEach((uv, i) => {
console.log(`Color ${i+1}: u=${uv.u.toFixed(4)}, v=${uv.v.toFixed(4)}`);
});
#include <iostream>
#include <vector>
#include <iomanip>
struct UV {
double u, v;
};
struct XYZ {
double X, Y, Z;
};
/**
* Convert CIE XYZ to CIE 1960 UCS coordinates
*/
UV xyzToUv(const XYZ& xyz) {
double denominator = xyz.X + 15.0 * xyz.Y + 3.0 * xyz.Z;
if (denominator == 0.0) {
return {0.0, 0.0};
}
double u = (4.0 * xyz.X) / denominator;
double v = (6.0 * xyz.Y) / denominator;
return {u, v};
}
// Example usage
int main() {
// sRGB Red color
XYZ red = {41.24, 21.26, 1.93};
UV uv = xyzToUv(red);
std::cout << std::fixed << std::setprecision(6);
std::cout << "UV: (" << uv.u << ", " << uv.v << ")" << std::endl;
// Batch processing
std::vector<XYZ> colors = {
{41.24, 21.26, 1.93}, // Red
{35.76, 71.52, 11.92}, // Green
{18.05, 7.22, 95.05} // Blue
};
for (size_t i = 0; i < colors.size(); ++i) {
UV result = xyzToUv(colors[i]);
std::cout << "Color " << (i+1) << ": u="
<< std::setprecision(4) << result.u
<< ", v=" << result.v << std::endl;
}
return 0;
}
#[derive(Debug, Clone, Copy)]
pub struct Xyz {
pub x: f64,
pub y: f64,
pub z: f64,
}
#[derive(Debug, Clone, Copy)]
pub struct Uv {
pub u: f64,
pub v: f64,
}
impl Xyz {
pub fn new(x: f64, y: f64, z: f64) -> Self {
Self { x, y, z }
}
/// Convert XYZ to CIE 1960 UCS coordinates
pub fn to_uv(&self) -> Uv {
let denominator = self.x + 15.0 * self.y + 3.0 * self.z;
if denominator == 0.0 {
return Uv { u: 0.0, v: 0.0 };
}
let u = (4.0 * self.x) / denominator;
let v = (6.0 * self.y) / denominator;
Uv { u, v }
}
}
fn main() {
// sRGB Red color
let red = Xyz::new(41.24, 21.26, 1.93);
let uv = red.to_uv();
println!("UV: ({:.6}, {:.6})", uv.u, uv.v);
// Batch processing with iterator
let colors = vec![
Xyz::new(41.24, 21.26, 1.93), // Red
Xyz::new(35.76, 71.52, 11.92), // Green
Xyz::new(18.05, 7.22, 95.05), // Blue
];
for (i, color) in colors.iter().enumerate() {
let uv = color.to_uv();
println!("Color {}: u={:.4}, v={:.4}", i + 1, uv.u, uv.v);
}
}