目录
1.前言
CIE 色坐标 是国际照明委员会(Commission Internationale de l'Éclairage,简称 CIE)制定的用于定量描述颜色 的标准化坐标系统,最常用的是 CIE 1931 xy 色度坐标。
效果如下:
CIE屏幕录制 2026-04-22 102714
其中, CIE 1931 马蹄形色度图背景图片 (文件名 cie.png)如下:

2.计算过程
(1)先获取RGB三个通道的均值,可用cv2.mean
x, y, w, h = roi
cropped_img = img[y:y + h, x:x + w]
height, width, channels = img.shape
if( channels==3):
#获取均值
mean_val = cv2.mean(cropped_img)
blue=mean_val[0]
green = mean_val[1]
red = mean_val[2]
(2)RGB转CIE
根据"国际电工委员会(IEC)在 IEC 61966-2-1:1999 标准中精确定义。"进行计算:
#将 sRGB 颜色值(0–255) 转换到 CIE 1931 xy 色度坐标。
def RGBtoCIE(blue,green,red):
var_R = red / 255
var_G = green / 255
var_B = blue / 255
if (var_R > 0.04045):
var_R = pow((var_R + 0.055) / 1.055, 2.4)
else:
var_R = var_R / 12.92
if (var_G > 0.04045):
var_G=pow((var_G + 0.055) / 1.055, 2.4)
else:
var_G = var_G / 12.92
if (var_B > 0.04045):
var_B=pow((var_B + 0.055) / 1.055, 2.4)
else:
var_B = var_B / 12.92
# 这些数字是sRGB(标准红绿蓝颜色空间)官方标准中定义的核心参数,用于将存储的图像数据转换为物理线性的光强度数据,以便进行精确的颜色计算。
# 在色彩科学里被称为sRGB逆向伽马校正或sRGB解码。它由国际电工委员会(IEC)在IEC61966 - 2 - 1: 1999标准中精确定义,是所有显示器、打印机、互联网图像等遵循的统一规范
var_R = var_R * 100
var_G = var_G * 100
var_B = var_B * 100
X = var_R * 0.4124 + var_G * 0.3576 + var_B * 0.1805
Y = var_R * 0.2126 + var_G * 0.7152 + var_B * 0.0722
Z = var_R * 0.0193 + var_G * 0.1192 + var_B * 0.9505
if (X + Y + Z == 0):
CIEx = 0
CIEy = 0
else:
CIEx = X / (X + Y + Z)
CIEy = Y / (X + Y + Z)
return round(CIEx,3),round(CIEy,3)
归一化与反伽马校正(sRGB 解码)
var_R = red / 255
var_G = green / 255
var_B = blue / 255
if var_R > 0.04045:
var_R = pow((var_R + 0.055) / 1.055, 2.4)
else:
var_R = var_R / 12.92
G 和 B 同样处理
-
将 0--255 的整数颜色值映射到 0--1 的线性光强度。
-
这一步称为 sRGB 反向伽马校正 ,公式来自 IEC 61966-2-1 标准:
-
若数值 ≤ 0.04045,按线性缩放;
-
若 > 0.04045,按幂函数校正,指数为 2.4,并加一个小的偏移量。
-
X = var_R * 0.4124 + var_G * 0.3576 + var_B * 0.1805
Y = var_R * 0.2126 + var_G * 0.7152 + var_B * 0.0722
Z = var_R * 0.0193 + var_G * 0.1192 + var_B * 0.9505
-
这是 sRGB → XYZ (D65 白点) 的标准转换矩阵。
-
矩阵数值来源于 国际照明委员会 (CIE) 定义的标准观察者函数与 sRGB 原色坐标的结合。
3.判断与显示
绘制合格区域(本文是三角形区域)
在cie.png上绘制点和区域,并判断是否在区域内
代码如下:
#三角形三个顶点坐标
x1,y1=0.3,0.3
x2,y2=0.5,0.3
x3,y3=0.4,0.4
#-----在色坐标图上显示色坐标----
def showCIEXY(ciex, ciey):
img = cv2.imread("cie.png")
height, width, _ = img.shape
k_x = 750
k_y = -750
b_x = 0
b_y = 700
# 转换三角形顶点坐标到像素坐标
pts = np.array([
[round(x1 * k_x + b_x), round(y1 * k_y + b_y)],
[round(x2 * k_x + b_x), round(y2 * k_y + b_y)],
[round(x3 * k_x + b_x), round(y3 * k_y + b_y)]
], np.int32)
# 绘制三角形轮廓(绿色,线宽2)
cv2.polylines(img, [pts], isClosed=True, color=(0, 255, 0), thickness=2)
# 绘制色坐标点(黑色实心圆)
x = round(ciex * k_x + b_x)
y = round(ciey * k_y + b_y)
cv2.circle(img, (x, y), 10, (0, 0, 0), thickness=-1)
cv2.imshow('CIE Diagram', img)
cv2.waitKey(0)
cv2.destroyAllWindows()
#判断色坐标是否合格
def IsInRegion(ciex, ciey):
"""
判断 CIE 色坐标 (ciex, ciey) 是否在预定义的三角形区域内。
三角形顶点:(0.3,0.3), (0.33,0.3), (0.315,0.315)
返回值:True(在区域内),False(不在区域内)
"""
# 计算各边向量与点P到起点向量的叉积
def cross_product(ax, ay, bx, by, px, py):
"""返回向量 AB × AP 的 z 分量"""
return (bx - ax) * (py - ay) - (by - ay) * (px - ax)
# 计算三个叉积
cp1 = cross_product(x1, y1, x2, y2, ciex, ciey)
cp2 = cross_product(x2, y2, x3, y3, ciex, ciey)
cp3 = cross_product(x3, y3, x1, y1, ciex, ciey)
# 容差处理,绝对值小于 1e-9 视为 0(点在边上)
eps = 1e-9
sign = lambda v: 0 if abs(v) < eps else (1 if v > 0 else -1)
s1, s2, s3 = sign(cp1), sign(cp2), sign(cp3)
# 如果所有符号非负 或 所有符号非正,则点在三角形内(包括边界)
# 注意:若某个叉积为0,则点在对应的边上,仍算区域内
if s1 >= 0 and s2 >= 0 and s3 >= 0:
return True
if s1 <= 0 and s2 <= 0 and s3 <= 0:
return True
return False
4.完整代码
import cv2
import numpy as np
#----计算色坐标---
def getCIE(img,roi):
#抠图
x, y, w, h = roi
cropped_img = img[y:y + h, x:x + w]
height, width, channels = img.shape
if( channels==3):
#获取均值
mean_val = cv2.mean(cropped_img)
blue=mean_val[0]
green = mean_val[1]
red = mean_val[2]
return RGBtoCIE(blue,green,red)
else:
return 0,0
#将 sRGB 颜色值(0--255) 转换到 CIE 1931 xy 色度坐标。
def RGBtoCIE(blue,green,red):
var_R = red / 255
var_G = green / 255
var_B = blue / 255
if (var_R > 0.04045):
var_R = pow((var_R + 0.055) / 1.055, 2.4)
else:
var_R = var_R / 12.92
if (var_G > 0.04045):
var_G=pow((var_G + 0.055) / 1.055, 2.4)
else:
var_G = var_G / 12.92
if (var_B > 0.04045):
var_B=pow((var_B + 0.055) / 1.055, 2.4)
else:
var_B = var_B / 12.92
# 这些数字是sRGB(标准红绿蓝颜色空间)官方标准中定义的核心参数,用于将存储的图像数据转换为物理线性的光强度数据,以便进行精确的颜色计算。
# 在色彩科学里被称为sRGB逆向伽马校正或sRGB解码。它由国际电工委员会(IEC)在IEC61966 - 2 - 1: 1999标准中精确定义,是所有显示器、打印机、互联网图像等遵循的统一规范
var_R = var_R * 100
var_G = var_G * 100
var_B = var_B * 100
X = var_R * 0.4124 + var_G * 0.3576 + var_B * 0.1805
Y = var_R * 0.2126 + var_G * 0.7152 + var_B * 0.0722
Z = var_R * 0.0193 + var_G * 0.1192 + var_B * 0.9505
if (X + Y + Z == 0):
CIEx = 0
CIEy = 0
else:
CIEx = X / (X + Y + Z)
CIEy = Y / (X + Y + Z)
return round(CIEx,3),round(CIEy,3)
#三角形三个顶点坐标
x1,y1=0.3,0.3
x2,y2=0.5,0.3
x3,y3=0.4,0.4
#-----在色坐标图上显示色坐标----
def showCIEXY(ciex, ciey):
img = cv2.imread("cie.png")
height, width, _ = img.shape
k_x = 750
k_y = -750
b_x = 0
b_y = 700
# 转换三角形顶点坐标到像素坐标
pts = np.array([
[round(x1 * k_x + b_x), round(y1 * k_y + b_y)],
[round(x2 * k_x + b_x), round(y2 * k_y + b_y)],
[round(x3 * k_x + b_x), round(y3 * k_y + b_y)]
], np.int32)
# 绘制三角形轮廓(绿色,线宽2)
cv2.polylines(img, [pts], isClosed=True, color=(0, 255, 0), thickness=2)
# 绘制色坐标点(黑色实心圆)
x = round(ciex * k_x + b_x)
y = round(ciey * k_y + b_y)
cv2.circle(img, (x, y), 10, (0, 0, 0), thickness=-1)
cv2.imshow('CIE Diagram', img)
cv2.waitKey(0)
cv2.destroyAllWindows()
#判断色坐标是否合格
def IsInRegion(ciex, ciey):
"""
判断 CIE 色坐标 (ciex, ciey) 是否在预定义的三角形区域内。
三角形顶点:(0.3,0.3), (0.33,0.3), (0.315,0.315)
返回值:True(在区域内),False(不在区域内)
"""
# 计算各边向量与点P到起点向量的叉积
def cross_product(ax, ay, bx, by, px, py):
"""返回向量 AB × AP 的 z 分量"""
return (bx - ax) * (py - ay) - (by - ay) * (px - ax)
# 计算三个叉积
cp1 = cross_product(x1, y1, x2, y2, ciex, ciey)
cp2 = cross_product(x2, y2, x3, y3, ciex, ciey)
cp3 = cross_product(x3, y3, x1, y1, ciex, ciey)
# 容差处理,绝对值小于 1e-9 视为 0(点在边上)
eps = 1e-9
sign = lambda v: 0 if abs(v) < eps else (1 if v > 0 else -1)
s1, s2, s3 = sign(cp1), sign(cp2), sign(cp3)
# 如果所有符号非负 或 所有符号非正,则点在三角形内(包括边界)
# 注意:若某个叉积为0,则点在对应的边上,仍算区域内
if s1 >= 0 and s2 >= 0 and s3 >= 0:
return True
if s1 <= 0 and s2 <= 0 and s3 <= 0:
return True
return False
if __name__ == "__main__":
img = cv2.imread("testimg.png")
#print(img.shape)
#在窗口上画框,获取region
# 按下回车或空格或esc确认
roi = cv2.selectROI("Select ROI", img, showCrosshair=True, fromCenter=False)
print(f"选取的区域为: {roi}") # 返回 (x, y, w, h)
ciex, ciey = getCIE(img,roi)
print(ciex,ciey)
cv2.destroyAllWindows()
showCIEXY(ciex, ciey)
if IsInRegion(ciex, ciey):
print("色坐标在指定三角形区域内,合格!")
else:
print("色坐标不在区域内,不合格。")
testimg.png如下:
