色彩空间转换(Color Space Conversion)是图像处理中的基础操作,用于在不同颜色表示系统之间进行转换。下面介绍常见的色彩空间及其转换方法,并提供Python实现示例。
常见色彩空间概述
色彩空间 | 主要用途 | 通道组成 | 特点 |
---|---|---|---|
RGB | 显示器显示 | 红(R)、绿(G)、蓝(B) | 设备相关,直观 |
HSV/HSL | 颜色选择、分析 | 色调(H)、饱和度(S)、明度(V)/亮度(L) | 更符合人类感知 |
LAB | 颜色差异测量 | 明度(L)、a(红绿轴)、b(黄蓝轴) | 设备无关,均匀色差 |
YUV/YCbCr | 视频压缩 | 亮度(Y)、色度(UV/CbCr) | 分离亮度和色度 |
CMYK | 印刷 | 青(C)、品红(M)、黄(Y)、黑(K) | 减色混合 |
Python基础转换方法
1. 使用OpenCV进行转换
import cv2
import numpy as np
# 读取图像(BGR格式)
img_bgr = cv2.imread('image.jpg')
# BGR转RGB
img_rgb = cv2.cvtColor(img_bgr, cv2.COLOR_BGR2RGB)
# BGR转HSV
img_hsv = cv2.cvtColor(img_bgr, cv2.COLOR_BGR2HSV)
# BGR转LAB
img_lab = cv2.cvtColor(img_bgr, cv2.COLOR_BGR2LAB)
# BGR转YCrCb
img_ycrcb = cv2.cvtColor(img_bgr, cv2.COLOR_BGR2YCrCb)
# 显示转换结果
def show_images(images, titles):
for i, (img, title) in enumerate(zip(images, titles)):
cv2.imshow(title, img)
cv2.waitKey(0)
cv2.destroyAllWindows()
show_images(
[img_bgr, img_rgb, img_hsv[:,:,0], img_lab[:,:,0]],
['BGR', 'RGB', 'HSV-Hue', 'LAB-L']
)
2. 手动实现RGB转HSV
def rgb_to_hsv(rgb_img):
"""手动实现RGB到HSV转换"""
img = rgb_img.astype('float32') / 255.0
r, g, b = cv2.split(img)
# 计算值(Value)
v = np.maximum.reduce([r, g, b])
# 计算饱和度(Saturation)
min_val = np.minimum.reduce([r, g, b])
delta = v - min_val
s = np.where(v == 0, 0, delta / v)
# 计算色调(Hue)
h = np.zeros_like(v)
mask = delta != 0
# 红色为基准
idx = (v == r) & mask
h[idx] = (60 * ((g[idx] - b[idx]) / delta[idx]) + 360) % 360
# 绿色为基准
idx = (v == g) & mask
h[idx] = (60 * ((b[idx] - r[idx]) / delta[idx]) + 120) % 360
# 蓝色为基准
idx = (v == b) & mask
h[idx] = (60 * ((r[idx] - g[idx]) / delta[idx]) + 240) % 360
# 归一化到OpenCV标准范围
h = h / 2 # [0,180]
s = s * 255 # [0,255]
v = v * 255 # [0,255]
return cv2.merge([h, s, v]).astype('uint8')
# 使用示例
hsv_manual = rgb_to_hsv(img_rgb)
hsv_opencv = cv2.cvtColor(img_rgb, cv2.COLOR_RGB2HSV)
# 比较手动实现与OpenCV结果
print("差异:", np.sum(np.abs(hsv_manual - hsv_opencv)))
高级色彩空间转换
1. RGB与LAB的精确转换
def rgb_to_lab(rgb_img):
"""
RGB转LAB色彩空间(精确实现)
参考: http://www.easyrgb.com/en/math.php
"""
# 转换为float32并归一化
rgb = rgb_img.astype('float32') / 255.0
# 线性化RGB(去除gamma校正)
mask = rgb <= 0.04045
rgb_linear = np.where(mask, rgb / 12.92, ((rgb + 0.055) / 1.055) ** 2.4)
# 转换为XYZ色彩空间
x = rgb_linear[...,0] * 0.4124 + rgb_linear[...,1] * 0.3576 + rgb_linear[...,2] * 0.1805
y = rgb_linear[...,0] * 0.2126 + rgb_linear[...,1] * 0.7152 + rgb_linear[...,2] * 0.0722
z = rgb_linear[...,0] * 0.0193 + rgb_linear[...,1] * 0.1192 + rgb_linear[...,2] * 0.9505
# 归一化到白点D65
x /= 0.95047
y /= 1.0
z /= 1.08883
# 非线性变换
epsilon = 216/24389
kappa = 24389/27
# 计算f(t)
def f(t):
return np.where(t > epsilon, t ** (1/3), (kappa * t + 16) / 116)
fx = f(x)
fy = f(y)
fz = f(z)
# 计算LAB
l = 116 * fy - 16
a = 500 * (fx - fy)
b = 200 * (fy - fz)
# 转换为OpenCV范围
l = l * 255 / 100
a = a + 128
b = b + 128
return cv2.merge([l, a, b]).astype('uint8')
# 使用示例
lab_manual = rgb_to_lab(img_rgb)
lab_opencv = cv2.cvtColor(img_rgb, cv2.COLOR_RGB2LAB)
# 比较结果
print("L通道差异:", np.mean(np.abs(lab_manual[:,:,0] - lab_opencv[:,:,0])))
2. 色彩空间转换矩阵优化
def apply_color_matrix(img, matrix):
"""应用色彩转换矩阵"""
# 重塑为(像素数, 3)
orig_shape = img.shape
pixels = img.reshape(-1, 3).astype('float32')
# 添加齐次坐标
pixels = np.hstack([pixels, np.ones((pixels.shape[0], 1))])
# 应用矩阵变换
transformed = np.dot(pixels, matrix.T)
# 裁剪并重塑回原形状
transformed = np.clip(transformed[:,:3], 0, 255).reshape(orig_shape)
return transformed.astype('uint8')
# RGB转YUV矩阵
rgb_to_yuv_matrix = np.array([
[0.299, 0.587, 0.114, 0],
[-0.14713, -0.28886, 0.436, 128],
[0.615, -0.51499, -0.10001, 128]
])
# 使用示例
yuv_manual = apply_color_matrix(img_rgb, rgb_to_yuv_matrix)
yuv_opencv = cv2.cvtColor(img_rgb, cv2.COLOR_RGB2YUV)
print("YUV转换差异:", np.mean(np.abs(yuv_manual - yuv_opencv)))
色彩空间应用案例
1. 基于HSV的颜色检测
def detect_color_hsv(image, lower_hsv, upper_hsv):
"""使用HSV空间检测特定颜色范围"""
hsv = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)
mask = cv2.inRange(hsv, lower_hsv, upper_hsv)
result = cv2.bitwise_and(image, image, mask=mask)
return result
# 检测红色范围
lower_red = np.array([0, 120, 70])
upper_red = np.array([10, 255, 255])
red_objects = detect_color_hsv(img_bgr, lower_red, upper_red)
# 显示结果
cv2.imshow('Red Objects', red_objects)
cv2.waitKey(0)
2. 基于LAB的色彩增强
def lab_color_enhance(image, l_scale=1.2, a_scale=1.1, b_scale=1.1):
"""在LAB空间增强色彩"""
lab = cv2.cvtColor(image, cv2.COLOR_BGR2LAB)
l, a, b = cv2.split(lab)
# 增强各通道
l = np.clip(l * l_scale, 0, 255)
a = np.clip(a * a_scale - 0.5*(a_scale-1)*128, 0, 255)
b = np.clip(b * b_scale - 0.5*(b_scale-1)*128, 0, 255)
enhanced_lab = cv2.merge([l, a, b])
return cv2.cvtColor(enhanced_lab, cv2.COLOR_LAB2BGR)
# 使用示例
enhanced = lab_color_enhance(img_bgr)
cv2.imshow('Original vs Enhanced', np.hstack([img_bgr, enhanced]))
cv2.waitKey(0)
性能优化
1. 使用查找表(LUT)加速
def build_3d_lut(input_space, output_space, size=33):
"""构建3D色彩查找表"""
# 创建输入网格
axis = np.linspace(0, 255, size)
grid = np.stack(np.meshgrid(axis, axis, axis, indexing='ij'), axis=-1)
# 转换色彩空间
lut = cv2.cvtColor(grid.astype('float32'), input_space, output_space)
return lut.astype('float32')
# 构建RGB到LAB的3D LUT
rgb_to_lab_lut = build_3d_lut(cv2.COLOR_RGB2LAB, size=17)
def apply_3d_lut(image, lut):
"""应用3D LUT进行色彩转换"""
# 归一化图像到LUT尺寸
scaled = (image * (lut.shape[0]-1) / 255.0).astype('float32')
# 三线性插值
from scipy.interpolate import RegularGridInterpolator
axes = [np.linspace(0, 255, lut.shape[i]) for i in range(3)]
interpolator = RegularGridInterpolator(axes, lut, method='linear')
# 应用插值
return interpolator(scaled.reshape(-1, 3)).reshape(image.shape)
# 使用示例(适用于小图像)
small_img = cv2.resize(img_rgb, (200, 200))
lab_lut = apply_3d_lut(small_img, rgb_to_lab_lut)
2. 使用Numba加速
from numba import jit
@jit(nopython=True)
def rgb_to_hsv_numba(rgb_img):
"""使用Numba加速的RGB转HSV"""
hsv = np.empty_like(rgb_img)
for i in range(rgb_img.shape[0]):
for j in range(rgb_img.shape[1]):
r, g, b = rgb_img[i,j] / 255.0
max_val = max(r, g, b)
min_val = min(r, g, b)
delta = max_val - min_val
# 计算Value
v = max_val
# 计算Saturation
s = 0 if max_val == 0 else delta / max_val
# 计算Hue
if delta == 0:
h = 0
elif max_val == r:
h = 60 * ((g - b) / delta % 6)
elif max_val == g:
h = 60 * ((b - r) / delta + 2)
else:
h = 60 * ((r - g) / delta + 4)
# 转换为OpenCV范围
hsv[i,j,0] = h / 2
hsv[i,j,1] = s * 255
hsv[i,j,2] = v * 255
return hsv.astype('uint8')
# 使用示例
hsv_numba = rgb_to_hsv_numba(img_rgb)
注意事项
-
数据范围:
-
OpenCV的HSV:H∈[0,179], S∈[0,255], V∈[0,255]
-
OpenCV的LAB:L∈[0,255], a∈[0,255], b∈[0,255]
-
-
色彩空间特性:
-
HSV/HSL对亮度变化敏感
-
LAB对颜色差异感知均匀
-
YUV/YCrCb适合压缩处理
-
-
性能考虑:
-
优先使用OpenCV内置函数
-
大图像考虑使用LUT或GPU加速
-
避免不必要的色彩空间转换
-
-
gamma校正:
-
RGB↔LAB转换需要考虑gamma校正
-
使用
cv2.COLOR_BGR2LAB
时OpenCV会自动处理
-
完整色彩处理流程示例
def professional_color_grading(image):
"""专业的色彩分级流程"""
# 1. 转换为浮点并归一化
img_float = image.astype('float32') / 255.0
# 2. 反gamma校正(sRGB -> 线性RGB)
linear = np.where(
img_float <= 0.04045,
img_float / 12.92,
((img_float + 0.055) / 1.055) ** 2.4
)
# 3. 转换为LAB色彩空间(通过XYZ)
# 线性RGB转XYZ
xyz = np.empty_like(linear)
xyz[...,0] = linear[...,0] * 0.4124 + linear[...,1] * 0.3576 + linear[...,2] * 0.1805
xyz[...,1] = linear[...,0] * 0.2126 + linear[...,1] * 0.7152 + linear[...,2] * 0.0722
xyz[...,2] = linear[...,0] * 0.0193 + linear[...,1] * 0.1192 + linear[...,2] * 0.9505
# XYZ转LAB
xyz[...,0] /= 0.95047 # D65白点
xyz[...,2] /= 1.08883
epsilon = 216/24389
kappa = 24389/27
def f(t):
return np.where(t > epsilon, t ** (1/3), (kappa * t + 16) / 116)
fx = f(xyz[...,0])
fy = f(xyz[...,1])
fz = f(xyz[...,2])
lab = np.empty_like(xyz)
lab[...,0] = 116 * fy - 16 # L [0,100]
lab[...,1] = 500 * (fx - fy) # a [-128,127]
lab[...,2] = 200 * (fy - fz) # b [-128,127]
# 4. 在LAB空间进行色彩调整
lab[...,0] = np.clip(lab[...,0] * 1.1, 0, 100) # 增强亮度
lab[...,1] *= 1.2 # 增强a通道
lab[...,2] *= 0.9 # 减弱b通道
# 5. LAB转回RGB
# LAB转XYZ
fy = (lab[...,0] + 16) / 116
fx = lab[...,1] / 500 + fy
fz = fy - lab[...,2] / 200
def inv_f(t):
return np.where(t > 6/29, t**3, 3*(6/29)**2*(t-4/29))
xyz[...,1] = inv_f(fy)
xyz[...,0] = inv_f(fx) * 0.95047
xyz[...,2] = inv_f(fz) * 1.08883
# XYZ转线性RGB
linear[...,0] = xyz[...,0] * 3.2406 + xyz[...,1] * -1.5372 + xyz[...,2] * -0.4986
linear[...,1] = xyz[...,0] * -0.9689 + xyz[...,1] * 1.8758 + xyz[...,2] * 0.0415
linear[...,2] = xyz[...,0] * 0.0557 + xyz[...,1] * -0.2040 + xyz[...,2] * 1.0570
# 线性RGB转sRGB
srgb = np.where(
linear <= 0.0031308,
12.92 * linear,
1.055 * (linear ** (1/2.4)) - 0.055
)
# 6. 转换为8位图像
return (np.clip(srgb, 0, 1) * 255).astype('uint8')
# 使用示例
graded = professional_color_grading(img_bgr)
cv2.imshow('Original vs Graded', np.hstack([img_bgr, graded]))
cv2.waitKey(0)
ISP中的颜色空间转换
颜色空间转换是图像信号处理(ISP)流水线中的关键环节,负责在不同颜色表示系统之间进行转换,以满足不同处理阶段的需求。
ISP中颜色空间转换的典型流程
1. ISP流水线中的颜色空间位置
Sensor RAW → 黑电平校正 → 镜头阴影校正 → 去马赛克 → 颜色空间转换 → 白平衡 → 色彩校正 → 伽马校正 → 色彩空间转换 → 输出
2. 常见转换路径
-
传感器原始数据(Raw Bayer) → RGB
-
RGB → YUV/YCbCr (用于视频编码)
-
RGB → HSV/HSL (用于色彩分析)
-
RGB → LAB (用于色彩增强)
-
YUV → RGB (用于显示)
颜色空间转换算法
1. Bayer RAW 转 RGB (去马赛克)
def bayer_to_rgb(bayer, pattern='RGGB'):
"""
Bayer格式转RGB图像(简单双线性插值)
:param bayer: 单通道Bayer图像
:param pattern: Bayer模式(RGGB/GBRG等)
:return: RGB图像
"""
h, w = bayer.shape
rgb = np.zeros((h, w, 3), dtype=bayer.dtype)
if pattern == 'RGGB':
# R通道
rgb[0::2, 0::2, 0] = bayer[0::2, 0::2] # R位置
rgb[0::2, 1::2, 0] = (bayer[0::2, 0::2] + bayer[0::2, 2::2]) // 2 # 水平插值
rgb[1::2, :, 0] = rgb[0::2, :, 0] # 垂直复制
# G通道
rgb[0::2, 1::2, 1] = bayer[0::2, 1::2] # G位置(第一行)
rgb[1::2, 0::2, 1] = bayer[1::2, 0::2] # G位置(第二行)
# 插值缺失的G像素
for i in range(1, h-1):
for j in range(1, w-1):
if (i % 2 == 0 and j % 2 == 1) or (i % 2 == 1 and j % 2 == 0):
continue
rgb[i,j,1] = (bayer[i-1,j] + bayer[i+1,j] + bayer[i,j-1] + bayer[i,j+1]) // 4
# B通道
rgb[1::2, 1::2, 2] = bayer[1::2, 1::2] # B位置
rgb[1::2, 0::2, 2] = (bayer[1::2, 1::2] + bayer[1::2, -1:1:-2]) // 2 # 水平插值
rgb[0::2, :, 2] = rgb[1::2, :, 2] # 垂直复制
return rgb
2. RGB 转 YUV/YCbCr (ISP常用)
def rgb_to_yuv(rgb, version='bt601'):
"""
RGB转YUV色彩空间
:param version: 标准版本(bt601/bt709/bt2020)
"""
if version == 'bt601':
# ITU-R BT.601标准
matrix = np.array([
[0.299, 0.587, 0.114],
[-0.168736, -0.331264, 0.5],
[0.5, -0.418688, -0.081312]
])
elif version == 'bt709':
# ITU-R BT.709标准
matrix = np.array([
[0.2126, 0.7152, 0.0722],
[-0.114572, -0.385428, 0.5],
[0.5, -0.454153, -0.045847]
])
else: # bt2020
matrix = np.array([
[0.2627, 0.6780, 0.0593],
[-0.139630, -0.360370, 0.5],
[0.5, -0.459786, -0.040214]
])
yuv = np.dot(rgb.reshape(-1, 3), matrix.T)
yuv[..., 1:] += 128 # UV分量加偏移
return yuv.reshape(rgb.shape).clip(0, 255).astype('uint8')
3. YUV 转 RGB (显示处理)
def yuv_to_rgb(yuv, version='bt601'):
"""YUV转RGB空间"""
yuv = yuv.copy().astype('float32')
yuv[..., 1:] -= 128 # UV分量减偏移
if version == 'bt601':
matrix = np.array([
[1.0, 0.0, 1.402],
[1.0, -0.344136, -0.714136],
[1.0, 1.772, 0.0]
])
elif version == 'bt709':
matrix = np.array([
[1.0, 0.0, 1.5748],
[1.0, -0.187324, -0.468124],
[1.0, 1.8556, 0.0]
])
else: # bt2020
matrix = np.array([
[1.0, 0.0, 1.4746],
[1.0, -0.164553, -0.571353],
[1.0, 1.8814, 0.0]
])
rgb = np.dot(yuv.reshape(-1, 3), matrix.T)
return rgb.reshape(yuv.shape).clip(0, 255).astype('uint8')
ISP专用颜色空间转换
1. 传感器RGB转标准RGB (Color Correction Matrix)
def apply_ccm(rgb, ccm):
"""
应用色彩校正矩阵(CCM)
:param rgb: 传感器RGB图像(0-255)
:param ccm: 3x3色彩校正矩阵
:return: 校正后的RGB图像
"""
# 转换为浮点并归一化
rgb_float = rgb.astype('float32') / 255.0
# 重塑为(像素数, 3)并应用矩阵
pixels = rgb_float.reshape(-1, 3)
corrected = np.dot(pixels, ccm.T)
# 裁剪并转换回原格式
corrected = np.clip(corrected, 0, 1).reshape(rgb.shape)
return (corrected * 255).astype('uint8')
# 示例CCM矩阵(需根据传感器校准)
ccm_matrix = np.array([
[1.5, -0.3, -0.2],
[-0.1, 1.2, -0.1],
[0.1, -0.2, 1.1]
])
# 使用示例
corrected_rgb = apply_ccm(raw_rgb, ccm_matrix)
2. RGB转HSV用于ISP色彩分析
def rgb_to_hsv_isp(rgb):
"""
ISP优化的RGB转HSV实现
:param rgb: 输入RGB图像(0-255)
:return: HSV图像(H:0-360, S:0-1, V:0-1)
"""
rgb = rgb.astype('float32') / 255.0
r, g, b = rgb[...,0], rgb[...,1], rgb[...,2]
max_val = np.max(rgb, axis=-1)
min_val = np.min(rgb, axis=-1)
delta = max_val - min_val
# 计算Hue
h = np.zeros_like(max_val)
mask = delta != 0
# 红色为基准
idx = (max_val == r) & mask
h[idx] = (60 * ((g[idx] - b[idx]) / delta[idx]) + 360) % 360
# 绿色为基准
idx = (max_val == g) & mask
h[idx] = 60 * ((b[idx] - r[idx]) / delta[idx] + 2)
# 蓝色为基准
idx = (max_val == b) & mask
h[idx] = 60 * ((r[idx] - g[idx]) / delta[idx] + 4)
# 计算Saturation
s = np.where(max_val == 0, 0, delta / max_val)
return np.stack([h, s, max_val], axis=-1)
def hsv_to_rgb_isp(hsv):
"""HSV转RGB(ISP优化版)"""
h, s, v = hsv[...,0], hsv[...,1], hsv[...,2]
c = v * s
x = c * (1 - np.abs((h / 60) % 2 - 1))
m = v - c
rgb_prime = np.zeros_like(hsv)
mask = (0 <= h) & (h < 60)
rgb_prime[mask] = np.stack([c[mask], x[mask], np.zeros_like(c[mask])], axis=-1)
mask = (60 <= h) & (h < 120)
rgb_prime[mask] = np.stack([x[mask], c[mask], np.zeros_like(c[mask])], axis=-1)
mask = (120 <= h) & (h < 180)
rgb_prime[mask] = np.stack([np.zeros_like(c[mask]), c[mask], x[mask]], axis=-1)
mask = (180 <= h) & (h < 240)
rgb_prime[mask] = np.stack([np.zeros_like(c[mask]), x[mask], c[mask]], axis=-1)
mask = (240 <= h) & (h < 300)
rgb_prime[mask] = np.stack([x[mask], np.zeros_like(c[mask]), c[mask]], axis=-1)
mask = (300 <= h) & (h < 360)
rgb_prime[mask] = np.stack([c[mask], np.zeros_like(c[mask]), x[mask]], axis=-1)
rgb = (rgb_prime + m[..., np.newaxis]) * 255
return np.clip(rgb, 0, 255).astype('uint8')
ISP颜色空间转换优化
1. 定点数优化 (适合硬件实现)
def rgb_to_yuv_fixed_point(rgb, bits=8):
"""
定点数实现的RGB转YUV
:param bits: 定点数位数
"""
scale = 1 << bits
rgb_int = (rgb * (scale / 255.0)).astype('int32')
# BT.601系数转换为定点数
y = ((77 * rgb_int[...,0] + 150 * rgb_int[...,1] + 29 * rgb_int[...,2]) + 128) >> 8
u = ((-43 * rgb_int[...,0] - 85 * rgb_int[...,1] + 128 * rgb_int[...,2]) >> 8) + (128 << bits)
v = ((128 * rgb_int[...,0] - 107 * rgb_int[...,1] - 21 * rgb_int[...,2]) >> 8) + (128 << bits)
yuv = np.stack([y, u, v], axis=-1)
return (yuv / scale * 255).clip(0, 255).astype('uint8')
2. 查表法(LUT)优化
def build_rgb_to_yuv_lut(version='bt601'):
"""构建RGB到YUV的3D LUT"""
lut = np.zeros((256, 256, 256, 3), dtype='float32')
for r in range(256):
for g in range(256):
for b in range(256):
rgb = np.array([r, g, b], dtype='float32')
lut[r,g,b] = rgb_to_yuv(rgb[np.newaxis, np.newaxis], version)[0,0]
return lut
# 预构建LUT(实际使用时应预计算保存)
yuv_lut = build_rgb_to_yuv_lut()
def rgb_to_yuv_lut(rgb, lut):
"""使用LUT进行RGB到YUV转换"""
return lut[rgb[...,0], rgb[...,1], rgb[...,2]]
3. 多线程加速
from multiprocessing import Pool
def parallel_convert(args):
"""并行转换函数"""
chunk, convert_func = args
return convert_func(chunk)
def rgb_to_yuv_parallel(rgb, convert_func, workers=4):
"""多线程颜色空间转换"""
# 分割图像
chunks = np.array_split(rgb, workers)
# 创建进程池
with Pool(workers) as pool:
results = pool.map(parallel_convert, [(chunk, convert_func) for chunk in chunks])
return np.concatenate(results)
ISP颜色处理流水线示例
def isp_color_pipeline(raw_bayer, ccm, gamma=2.2):
"""
简化的ISP颜色处理流水线
:param raw_bayer: 原始Bayer数据
:param ccm: 色彩校正矩阵
:param gamma: 伽马值
:return: 处理后的RGB图像
"""
# 1. Bayer转RGB
rgb = bayer_to_rgb(raw_bayer, 'RGGB')
# 2. 应用色彩校正矩阵
rgb = apply_ccm(rgb, ccm)
# 3. 白平衡(简单灰度世界算法)
rgb_float = rgb.astype('float32')
avg_r = np.mean(rgb_float[...,0])
avg_g = np.mean(rgb_float[...,1])
avg_b = np.mean(rgb_float[...,2])
rgb_float[...,0] *= avg_g / avg_r
rgb_float[...,2] *= avg_g / avg_b
rgb = np.clip(rgb_float, 0, 255).astype('uint8')
# 4. 色彩空间转换(RGB->YUV用于降噪)
yuv = rgb_to_yuv(rgb)
# 5. 只在Y通道进行降噪(示例:简单均值滤波)
yuv[...,0] = cv2.blur(yuv[...,0], (3,3))
# 6. 转回RGB
rgb = yuv_to_rgb(yuv)
# 7. 伽马校正
rgb = gamma_correction(rgb, gamma)
return rgb
颜色空间转换的验证方法
1. 往返测试(Round-trip Testing)
def test_round_trip_conversion():
"""测试颜色空间转换的准确性"""
test_img = np.random.randint(0, 256, (100, 100, 3), dtype='uint8')
# RGB <-> YUV
yuv = rgb_to_yuv(test_img)
rgb_back = yuv_to_rgb(yuv)
error = np.mean(np.abs(test_img - rgb_back))
print(f"RGB<->YUV 平均误差: {error:.2f}")
# RGB <-> HSV
hsv = rgb_to_hsv_isp(test_img)
rgb_back = hsv_to_rgb_isp(hsv)
error = np.mean(np.abs(test_img - rgb_back))
print(f"RGB<->HSV 平均误差: {error:.2f}")
test_round_trip_conversion()
2. 色彩一致性检查
def check_color_constancy():
"""检查色彩一致性"""
# 创建测试色块
colors = [
('Red', [255, 0, 0]),
('Green', [0, 255, 0]),
('Blue', [0, 0, 255]),
('White', [255, 255, 255]),
('Gray', [128, 128, 128])
]
for name, color in colors:
rgb = np.array(color).reshape(1, 1, 3)
yuv = rgb_to_yuv(rgb)
rgb_back = yuv_to_rgb(yuv)
print(f"{name:6s} RGB: {rgb[0,0]} -> YUV: {yuv[0,0].astype(int)} -> RGB: {rgb_back[0,0]}")
check_color_constancy()
ISP中的颜色空间转换需要兼顾精度和性能,在实际实现中通常会根据具体硬件平台(如DSP、GPU或专用ISP)选择最优的实现方式。理解不同颜色空间的特性对于设计高效的ISP流水线至关重要。