Color Space Conversion(颜色空间转换)
是图像处理中的一个重要步骤,它将图像从一个颜色空间(Color Space)转换到另一个,以满足 显示、分析、压缩或算法需求。
为什么转换颜色空间?
应用场景 | 常用颜色空间 |
---|---|
图像压缩(JPEG) | RGB → YCbCr(压缩色度) |
人脸/物体检测 | RGB → HSV(更容易识别肤色/颜色区域) |
白平衡 / 曝光控制 | RGB / Lab / YCbCr |
图像增强 / 调色 | HSV / HSL |
色彩分割 / 识别 | HSV / Lab |
-
Y(Luma) 表示图像的亮度(明暗信息)
-
Cb(Blue-difference Chroma) 表示蓝色色差
-
Cr(Red-difference Chroma) 表示红色色差
它们是从 RGB 转换而来的三个通道,组合在一起可以重建出原始的颜色图像。
为什么用 YCbCr 而不是 RGB?
因为:人眼对 亮度(Y)很敏感,但对颜色细节(CbCr)没那么敏感。
所以我们可以:
-
高质量保留 Y(清晰度、轮廓都在这里)
-
对 Cb、Cr 进行压缩、降采样、去噪都不会影响视觉感受太多
RGB → YCbCr 转换公式:
这里加 128 是为了让 Cb/Cr 落在 [0, 255]
的无符号整型范围内(方便存储)
RGB->YCbCr
def CSC(gac_img):"""Convert gamma-corrected RGB image to Y and CbCr channels.Args:gac_img: np.ndarray, dtype=uint8, RGB image after gamma correction.Returns:y: np.ndarray, dtype=uint8, grayscale image (luma channel)cbcr_img: np.ndarray, dtype=uint8, two-channel image (Cb and Cr)"""gac_img = gac_img.astype(np.float32)r = gac_img[:, :, 0]g = gac_img[:, :, 1]b = gac_img[:, :, 2]# Y is luma (perceptual grayscale)y = 0.299 * r + 0.587 * g + 0.114 * b# Cb and Cr are chroma (color difference)cb = 0.564 * (b - y)cr = 0.713 * (r - y)# Shift cb/cr to 128-centered range to store in 8-bit (optional, like JPEG)cb = np.clip(cb + 128, 0, 255).astype(np.uint8)cr = np.clip(cr + 128, 0, 255).astype(np.uint8)y = np.clip(y, 0, 255).astype(np.uint8)cbcr_img = np.dstack((cb, cr))return y, cbcr_img
为啥 Cb/Cr 要加 128?
-
原始 Cb/Cr 值在
[-127, +127]
左右 -
加上 128 把它们映射到
[0, 255]
范围,以便 8-bit 存储 -
这是 JPEG 和 H.264 中的做法
YCBCR->RGB:
def YCbCr_to_RGB(y, cbcr_img):"""Convert Y (luma) + CbCr (chroma) back to RGB.Inputs:y: np.ndarray, uint8, grayscale image (Y channel)cbcr_img: np.ndarray, uint8, (H, W, 2), Cb and Cr channelsReturns:rgb_img: np.ndarray, uint8, reconstructed RGB image"""y = y.astype(np.float32)cb = cbcr_img[:, :, 0].astype(np.float32) - 128.0cr = cbcr_img[:, :, 1].astype(np.float32) - 128.0# Inverse conversion from YCbCr to RGBr = y + 1.403 * crg = y - 0.344 * cb - 0.714 * crb = y + 1.773 * cb# Clip to valid [0, 255] ranger = np.clip(r, 0, 255).astype(np.uint8)g = np.clip(g, 0, 255).astype(np.uint8)b = np.clip(b, 0, 255).astype(np.uint8)rgb_img = np.dstack((r, g, b))return rgb_img