Color Models (RGB, CMY, HSI)

Gonzalez R. C. and Woods R. E. Digital Image Processing (Forth Edition)

除了我们熟悉的RGB模式来表示图片, 还有其他很多种图片表示方式. 其实我现在很想要知道的一点是, 神经网络会对不同的表示会有不同的反应吗?

定义

RGB

Color Models (RGB, CMY, HSI)

普通的彩色图片, 每个像素点是一个三维的值((x, y, z) in [0, 1]^3),

CMY

RGB是以Red, Green, Blue为坐标轴的坐标系, 而CMY是以Cyan, Magenta, Yellow为坐标轴(这些颜色成为second colors), 以White为原点的坐标系.

CMYK

CMY因为不是以black为原点, 所以在实际中难以表示true black? 所以特意引入第四个颜色K表示黑色.

HSI

Color Models (RGB, CMY, HSI)

HSI, 即hue, saturation, identisity.

Hue: 即表示某种颜色, 正如上图所示, 以H, 点与红色(标准)之间的角度(相当于指定某种颜色).

Saturation: 饱和度, 顾名思义, 表示该颜色是有多少的纯色和白色混合在一起的.

Identisity: 灰度值, 将一个图像变成一个灰度图就是根据这个指标, 其相当于((x, y, z))在black-white这个轴上的投影.

注: HSI的值也是归一化为([0, 1])之间.

相互的转换

RGB <=> CMY

[left [ egin{array}{l} C \ M \ Y \ end{array} ight ] = left [ egin{array}{l} 1 \ 1 \ 1 \ end{array} ight ] - left [ egin{array}{l} R \ G \ B end{array} ight ] . ]

CMY <=> CMYK

CMY > CMYK

[K = min (C, M, Y), \ C = (C - K) / (1 - K), \ M = (M - K) / (1 - K), \ Y = (Y - K) / (1 - K). \ ]

CMYK > CMY

[C = C * (1 - K) + K, \ M = M * (1 - K) + K, \ Y = Y * (1 - K) + K. \ ]

RGB <=> HSI

hsi-to-rgb-conversion.pdf (tau.ac.il)

RGB > HSI

注意RGB > HSI并非直接的坐标变换, 具体的推导可以看上面的参考文献.

[ heta = cos^{-1} { frac{frac{1}{2}[(R-G) + (R-B)]}{[(R-G)^2 + (R-B)(G-B)]^{1/2}} }, \ H = left { egin{array}{ll} heta / 2pi & ext{if} : B le G, \ 1 - heta/2pi & ext{if} : B > G. end{array} ight . \ S = 1 - frac{3}{R + G + B}[min (R, G, B)]. \ I = frac{1}{3}(R + G + B). ]

注: 原文使用的是角度制, 这里我改成弧度制, 另外我这里直接将(H)归一化了.

HSI > RGB

HSI > RGB 需要根据(H)的不同改变策略:

首先令(H = H cdot 2pi in [0, 2pi)).

  1. (H in [0, frac{2}{3}pi]):

[B = I(1-S), \ R = I[1 + frac{Scos H}{cos (frac{1}{3}pi - H)}], \ G = 3I - (R + B). ]

  1. (H in [frac{2}{3}pi, frac{4}{3}pi)):

[H = H - frac{2}{3}pi, \ R = I(1-S), \ G = I[1 + frac{Scos H}{cos (frac{1}{3}pi - H)}], \ B = 3I - (R + G). ]

  1. (H in [frac{4}{3}pi, 2pi)):

[H = H - frac{4}{3}pi, \ G = I(1-S), \ B = I[1 + frac{Scos H}{cos (frac{1}{3}pi - H)}], \ R = 3I - (G + B). ]

代码示例

import cv2
import matplotlib.pyplot as plt
import numpy as np
from PIL import Image
img = np.random.randint(0, 256, (10, 10, 3), dtype=np.uint8)
# rgb -> hsi
def calcH(v):
    r, g, b = v
    val = (2 * r - g - b) / np.sqrt((r - g) ** 2 + (r - b) * (g - b))
    val /= 2
    theta = np.arccos(val)
    h = theta / (2 * np.pi) if b <= g else (1 - theta / (2 * np.pi))
    return h

def calcS(v):
    return 1 - 3 * v.min() / v.sum()

def calcI(v):
    return v.mean()

def rgb2hsi(img: np.ndarray) -> np.ndarray:
    img = img.astype(np.float) / 255.
    def unit(v):
        h = calcH(v)
        s = calcS(v)
        i = calcI(v)
        return np.array([h, s, i])
    img = np.apply_along_axis(unit, 2, img)
    return img
# hsi -> rgb

def unit(v):
    h, s, i = v
    idx = np.arange(3)
    h = h * np.pi * 2
    if h >= 2 * np.pi / 3:
        if h >= 4 * np.pi / 3:
            h = h - 4 * np.pi / 3
            idx = (idx - 2) % 3
        else:
            h = h - 2 * np.pi / 3
            idx = (idx - 1) % 3
    b = i * (1 - s)
    r = i * (1 + s * np.cos(h) / np.cos(np.pi / 3 - h))
    g = 3 * i - b - r
    v = np.array([r, g, b])
    return v[idx]



def hsi2rgb(img: np.ndarray):
    img = img.astype(np.float) / 255.
    img = np.apply_along_axis(unit, 2, img) * 255
    return img.astype(np.uint8)

在opencv库中, 可以通过如下手段变化:

rgb2hsi_cv = cv2.cvtColor(img[..., [2, 1, 0]], cv2.COLOR_BGR2HLS)
hsi2rgb_cv = cv2.cvtColor(rgb2hsi_cv, cv2.COLOR_HLS2BGR)[..., [2, 1, 0]]

opencv采用BGR格式是由于历史原因.

另外, 其出来的结果和我所实现的结果并不一致, 其I值也并非(frac{1}{3}(R + G + B))​, 所以我怀疑可能用的是另外一套变换公式(虽然网上说, HLS就是HSI).

在PIL库中, 我没找到RGB2HSI的函数, 不过PIL一般采用如下方式进行变换:

rgb2what = Image.fromarray(img).convert('RGB').convert('...')

注: '...'部分表示mode, 具体参考: Concepts — Pillow (PIL Fork) 8.3.1 documentation