Professional tool to convert CIE 1960 UCS chromaticity coordinates (u, v) back to CIE XYZ tristimulus values. Essential for reconstructing full color information from chromaticity data in lighting design, LED manufacturing, and color science applications. Requires luminance (Y) input for complete color specification.
UV coordinates are chromaticity coordinates - they only describe the color's hue and saturation, not its brightness. To reconstruct the complete XYZ color specification, you must provide the Y (luminance) value separately.
Y Value Guidelines:
Common Applications:
Convert UV chromaticity coordinates to XYZ color space with luminance input
UV to XYZ conversion is fundamentally different from XYZ to UV because it involvesreconstructing three-dimensional color information from two-dimensional chromaticity data. This process requires understanding the mathematical relationship between chromaticity and tristimulus values.
Step 1: From the forward transformation:
Step 2: Solve for X in terms of u, v, Y:
Step 3: Solve for Z using the constraint:
Edge Case: When v = 0, the formulas become undefined. This occurs for colors on the u-axis, which are theoretical and rarely encountered in practice.
UV coordinates are chromaticity coordinates that represent color quality (hue and saturation) but not quantity (brightness). They're derived by normalizing XYZ values, which removes the absolute scale. To reconstruct XYZ, you need to specify the brightness level through the Y (luminance) component.
The Y value depends on your application context. For LED measurements, use the measured luminance. For color matching, use the target brightness. For theoretical calculations, common values include 18 (middle gray), 50 (medium brightness), or 100 (maximum white). The choice affects only brightness, not the color appearance.
When v = 0, the conversion formulas become mathematically undefined (division by zero). This represents colors on the u-axis of the UV diagram, which are theoretical edge cases rarely encountered in practice. Our tool handles this gracefully by returning appropriate default values or error messages.
Yes, the conversion is mathematically exact and fully reversible when the Y value is known. Converting XYZ → UV → XYZ (with the same Y) will return the original values within floating-point precision. The accuracy depends only on the precision of your input values and computational environment.
import numpy as np
def uv_to_xyz(u, v, Y):
"""
Convert CIE 1960 UCS coordinates to XYZ
Parameters:
u, v: float or array-like
CIE 1960 UCS chromaticity coordinates
Y: float or array-like
Luminance value (0-100 typical)
Returns:
tuple: (X, Y, Z) tristimulus values
"""
u, v, Y = np.asarray(u), np.asarray(v), np.asarray(Y)
# Handle division by zero
mask = v != 0
X = np.zeros_like(u)
Z = np.zeros_like(u)
# Calculate X and Z where v != 0
X[mask] = (1.5 * u[mask] * Y[mask]) / v[mask]
Z[mask] = (6*Y[mask]/v[mask] - X[mask] - 15*Y[mask]) / 3
return X, Y, Z
# Example usage
u, v, Y = 0.4476, 0.4074, 50.0 # Example values
X, Y_out, Z = uv_to_xyz(u, v, Y)
print(f"XYZ: ({X:.2f}, {Y_out:.2f}, {Z:.2f})")
# Batch processing
uv_data = np.array([[0.4476, 0.4074], # Color 1
[0.3127, 0.3290], # D65 white
[0.1978, 0.4683]]) # Example color
Y_values = np.array([50, 100, 25])
X_vals, Y_vals, Z_vals = uv_to_xyz(uv_data[:, 0],
uv_data[:, 1],
Y_values)
for i, (x, y, z) in enumerate(zip(X_vals, Y_vals, Z_vals)):
print(f"Color {i+1}: XYZ({x:.2f}, {y:.2f}, {z:.2f})")
/**
* Convert CIE 1960 UCS coordinates to XYZ
* @param {number} u - u chromaticity coordinate
* @param {number} v - v chromaticity coordinate
* @param {number} Y - Luminance value
* @returns {Object} {X, Y, Z} tristimulus values
*/
function uvToXyz(u, v, Y) {
// Validate inputs
if (typeof u !== 'number' || typeof v !== 'number' || typeof Y !== 'number') {
throw new Error('All parameters must be numbers');
}
// Handle edge case where v = 0
if (v === 0) {
console.warn('v = 0: Conversion undefined, returning default values');
return { X: 0, Y: Y, Z: 0 };
}
// Calculate X and Z
const X = (1.5 * u * Y) / v;
const Z = (6*Y/v - X - 15*Y) / 3;
return { X, Y, Z };
}
// Class-based implementation
class UVConverter {
static toXyz(uv, Y) {
return uvToXyz(uv.u, uv.v, Y);
}
static batchConvert(uvArray, YArray) {
if (uvArray.length !== YArray.length) {
throw new Error('UV and Y arrays must have same length');
}
return uvArray.map((uv, i) => ({
...uv,
xyz: this.toXyz(uv, YArray[i])
}));
}
}
// Example usage
const uv = { u: 0.4476, v: 0.4074 };
const Y = 50;
const xyz = UVConverter.toXyz(uv, Y);
console.log(`XYZ: (${xyz.X.toFixed(2)}, ${xyz.Y.toFixed(2)}, ${xyz.Z.toFixed(2)})`);
// Batch processing
const uvColors = [
{ u: 0.4476, v: 0.4074 }, // Color 1
{ u: 0.3127, v: 0.3290 }, // D65 white
{ u: 0.1978, v: 0.4683 } // Example color
];
const luminances = [50, 100, 25];
const results = UVConverter.batchConvert(uvColors, luminances);
results.forEach((result, i) => {
const {X, Y, Z} = result.xyz;
console.log(`Color ${i+1}: XYZ(${X.toFixed(2)}, ${Y.toFixed(2)}, ${Z.toFixed(2)})`);
});