欢迎光临
我们一直在努力

python opencv 计算 色坐标(CIE 1931 )

目录

1.前言

2.计算过程

3.判断与显示

4.完整代码


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如下:

赞(0)
未经允许不得转载:171主机测评 » python opencv 计算 色坐标(CIE 1931 )
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址