第6章 Python 数字图像处理(DIP) - 彩色图像处理1 - RGB彩色模型,RGB to Gray,CMK和CMYK彩色模型,HSI彩色模型

数字图像处理(DIP)--Python 同时被 2 个专栏收录
57 篇文章 10 订阅

第6章主要讲的是彩色图像处理,一些彩色模型如RGB,CMK,CMYK,HSI等色彩模型;彩色模型的变换关系;还包含由灰度图像怎样处理成假彩色图像;使用彩色分割图像等。本章比较少理论还有变换的描述,主要以代码为主,如有需要,请自行查看书本。

import numpy as np
import cv2
import matplotlib 
import matplotlib.pyplot as plt
import PIL

print(f"Numpy version: {np.__version__}")
print(f"Opencv version: {cv2.__version__}")
print(f"Matplotlib version: {matplotlib.__version__}")
print(f"Pillow version: {PIL.__version__}")
Numpy version: 1.18.1
Opencv version: 4.2.0
Matplotlib version: 3.1.3
Pillow version: 7.0.0
def normalize(mask):
    return (mask - mask.min()) / (mask.max() - mask.min())

色彩基础

区别不同颜色的特性通常是亮度、色调和饱和度。亮度体现的发光强度的消色概念;色调是混合光波中与主波长书的人属性,表示被观察者感知的主导色;饱和度是指相对的纯度,或与一种色调混合的白光量。

色调与饱和度一起称为色度,因此一种颜色可由其亮度和色度来表征。形成任何一种特殊颜色的红色量、绿色量和蓝色量称为三色值,并分别表示为X,Y和Z。因此一种彩色就可由其三色系数来规定。

x = X X + Y + Z (6.1) x = \frac{X}{X + Y + Z} \tag{6.1} x=X+Y+ZX(6.1)
y = Y X + Y + Z (6.2) y = \frac{Y}{X + Y + Z} \tag{6.2} y=X+Y+ZY(6.2)
z = Z X + Y + Z (6.3) z = \frac{Z}{X + Y + Z} \tag{6.3} z=X+Y+ZZ(6.3)
x + y + z = 1 (6.4) x + y + z = 1 \tag{6.4} x+y+z=1(6.4)

彩色模型

彩色模型(也称彩色空间或彩色系统)的目的是以某种师傅微软方式来方便地规定颜色。彩色模型本质上规定:坐标系;坐标系内的子空间,模型内的每种颜色都可由子空间内包含的一个点来表示。

针对彩色显示器和彩色摄像机开发的RGB(Red, Green, Blue)模型;针对彩色打印开发的CMY(Cyan, Magenta, Yellow)模型和CMYK(K is Black);针对人们描述和解释颜色的方式开发的HSI(Hue, Saturation, Intensity)模型。

HSI能名解除图像中颜色和灰度级信息的联系,使其更适合灰度级处理技术。

RGB彩色模型

根根笛卡儿坐标系建立的。R,G和B的值都已经归一化在区间[0, 1]内。RGB原色可解释为发源于立方体原点的一个向量。

表示每个像素所用的比特数称为像素深度

RGB三个通道,每个通道像素深度为8比特图像,RGB彩色像素的深度为24比特。值域为[0, 255]

# RGB 彩色模型
img_bgr = cv2.imread('DIP_Figures/DIP3E_Original_Images_CH06/Fig0608(RGB-full-color-cube).tif')
img_rgb = img_bgr[:, :, ::-1]
plt.figure(figsize=(5, 5))
plt.imshow(img_rgb), plt.xticks([]), plt.yticks([])
plt.show()

在这里插入图片描述

# RGB 三个隐藏面,合成显示RGB,R = 127
temp = np.zeros([512, 512], np.uint8)

x = np.linspace(0, 1, temp.shape[0])
X = np.uint8(normalize(x) * 255)
X = np.tile(X, [512, 1])

y = np.linspace(0, 1, temp.shape[0])
Y = np.uint8(normalize(y) * 255)
Y = np.tile(Y, [512, 1])

R = temp + 127
G = X
B = np.rot90(Y)

plt.figure(figsize=(20, 5))
plt.gray()
plt.subplot(143), plt.imshow(R, vmin=0, vmax=255), plt.title('Red Channel'), plt.xticks([]), plt.yticks([])
plt.subplot(142), plt.imshow(G, ), plt.title('Green Channel'), plt.xticks([]), plt.yticks([])
plt.subplot(141), plt.imshow(B, ), plt.title('Blue Channel'), plt.xticks([]), plt.yticks([])

img = np.dstack([R, G, B])
plt.subplot(144),plt.imshow(img), plt.xticks([]), plt.yticks([])
plt.tight_layout()
plt.show()

在这里插入图片描述

# RGB 三个隐藏面,R = 0
temp = np.zeros([512, 512], np.uint8)

x = np.linspace(0, 1, temp.shape[0])
X = np.uint8(normalize(x) * 255)
X = np.tile(X, [512, 1])

y = np.linspace(0, 1, temp.shape[0])
Y = np.uint8(normalize(y) * 255)
Y = np.tile(Y, [512, 1])

R = temp
G = X
B = np.rot90(Y)

plt.figure(figsize=(20, 5))
plt.gray()
plt.subplot(141), plt.imshow(R, vmin=0, vmax=255), plt.title('Red Channel'), plt.xticks([]), plt.yticks([])
plt.subplot(142), plt.imshow(G, ), plt.title('Green Channel'), plt.xticks([]), plt.yticks([])
plt.subplot(143), plt.imshow(B, ), plt.title('Blue Channel'), plt.xticks([]), plt.yticks([])

img = np.dstack([R, G, B])
plt.subplot(144),plt.imshow(img), plt.xticks([]), plt.yticks([])
plt.tight_layout()
plt.show()

在这里插入图片描述

# RGB 三个隐藏面,G = 0
temp = np.zeros([512, 512], np.uint8)

x = np.linspace(0, 1, temp.shape[0])
X = np.uint8(normalize(x) * 255)
X = np.tile(X, [512, 1])

y = np.linspace(0, 1, temp.shape[0])
Y = np.uint8(normalize(y) * 255)
Y = np.tile(Y, [512, 1])

G = temp
R = np.flip(X)
B = np.rot90(Y)

plt.figure(figsize=(20, 5))
plt.gray()
plt.subplot(141), plt.imshow(R, vmin=0, vmax=255), plt.title('Red Channel'), plt.xticks([]), plt.yticks([])
plt.subplot(142), plt.imshow(G, ), plt.title('Green Channel'), plt.xticks([]), plt.yticks([])
plt.subplot(143), plt.imshow(B, ), plt.title('Blue Channel'), plt.xticks([]), plt.yticks([])

img = np.dstack([R, G, B])
plt.subplot(144),plt.imshow(img), plt.xticks([]), plt.yticks([])
plt.tight_layout()
plt.show()

在这里插入图片描述

# RGB 三个隐藏面,B = 0
temp = np.zeros([512, 512], np.uint8)

x = np.linspace(0, 1, temp.shape[0])
X = np.uint8(normalize(x) * 255)
X = np.tile(X, [512, 1])

y = np.linspace(0, 1, temp.shape[0])
Y = np.uint8(normalize(y) * 255)
Y = np.tile(Y, [512, 1])

B = temp
G = X
R = np.rot90(np.fliplr(Y))

plt.figure(figsize=(20, 5))
plt.gray()
plt.subplot(141), plt.imshow(R, vmin=0, vmax=255), plt.title('Red Channel'), plt.xticks([]), plt.yticks([])
plt.subplot(142), plt.imshow(G, ), plt.title('Green Channel'), plt.xticks([]), plt.yticks([])
plt.subplot(143), plt.imshow(B, ), plt.title('Blue Channel'), plt.xticks([]), plt.yticks([])

img = np.dstack([R, G, B])
plt.subplot(144),plt.imshow(img), plt.xticks([]), plt.yticks([])
plt.tight_layout()
plt.show()

在这里插入图片描述

RGB to Gray

模式“L”:

模式“L”为灰色图像,它的每个像素用8个bit表示,0表示黑,255表示白,其他数字表示不同的灰度。在PIL中,从模式“RGB”转换为“L”模式是按照下面的公式转换的:

L = R * 299/1000 + G * 587/1000+ B * 114/1000

img_ori = cv2.imread('DIP_Figures/DIP3E_Original_Images_CH06/Fig0646(a)(lenna_original_RGB).tif')

img_rgb = img_ori[:, :, ::-1] # 这个下面实现是一样的效果[0, 1, 2], 反转为[2, 1, 0]
# img_rgb = img_ori[..., ::-1]

plt.figure(figsize=(20, 5))

img_b = img_ori[:, :, 0]
img_g = img_ori[:, :, 1]
img_r = img_ori[:, :, 2]

plt.subplot(141), plt.imshow(img_rgb), plt.title('Original'), plt.xticks([]), plt.yticks([])

plt.gray()
plt.subplot(142), plt.imshow(img_b, ), plt.title('Blue Channel'), plt.xticks([]), plt.yticks([])
plt.subplot(143), plt.imshow(img_g, ), plt.title('Green Channel'), plt.xticks([]), plt.yticks([])
plt.subplot(144), plt.imshow(img_r, ), plt.title('Red Channel'), plt.xticks([]), plt.yticks([])

plt.tight_layout()
plt.show()

在这里插入图片描述

def rgb2gray(img):
    """
    convert RGB image to gray image
    param: img: input RGB 3 channels image
    return: grayscale image
    """
    img_gray = np.zeros((img.shape[:2]))
    
    img_r = img[:, :, 0].astype(np.float32)
    img_g = img[:, :, 1].astype(np.float32)
    img_b = img[:, :, 2].astype(np.float32)
    
    img_gray = (img_r * 299 + img_g * 587 + img_b * 114) / 1000
    img_gray = (normalize(img_gray) * 255).astype(np.uint8)
    
    return img_gray

def bgr2gray(img):
    """
    convert RGB image to gray image
    param: img: input RGB 3 channels image
    return: grayscale image
    """
    img_gray = np.zeros((img.shape[:2]))
    
    img_r = img[:, :, 2].astype(np.float32)
    img_g = img[:, :, 1].astype(np.float32)
    img_b = img[:, :, 0].astype(np.float32)
    
    img_gray = (img_r * 299 + img_g * 587 + img_b * 114) / 1000
    img_gray = (normalize(img_gray) * 255).astype(np.uint8)
    
    return img_gray
# RGB2GRAY
img_ori = cv2.imread('DIP_Figures/DIP3E_Original_Images_CH06/Fig0646(a)(lenna_original_RGB).tif') # BGR
img_rgb = img_ori[:, :, ::-1] # 这个下面实现是一样的效果[0, 1, 2], 反转为[2, 1, 0]
img_gray = rgb2gray(img_rgb)
bgr_gray = bgr2gray(img_ori)

plt.figure(figsize=(20, 5))
plt.subplot(141), plt.imshow(img_rgb), plt.title('Original'), plt.xticks([]), plt.yticks([])

plt.gray()
plt.subplot(142), plt.imshow(img_gray, ), plt.title('RGB to Gray'), plt.xticks([]), plt.yticks([])
plt.subplot(143), plt.imshow(bgr_gray, ), plt.title('BGR to Gray'), plt.xticks([]), plt.yticks([])
# plt.subplot(144), plt.imshow(img_r, ), plt.title('Red Channel'), plt.xticks([]), plt.yticks([])
plt.tight_layout()
plt.show()

在这里插入图片描述

CMK和CMYK 彩色模型

RGB 归一化到【0, 1】

[ C M Y ] = [ 1 1 1 ] − [ R G B ] \begin{bmatrix} C \\ M \\ Y\end{bmatrix} = \begin{bmatrix} 1 \\ 1 \\ 1\end{bmatrix} - \begin{bmatrix} R \\ G \\ B\end{bmatrix} CMY=111RGB

从CMY到CMYK的转换如下:
K = m i n ( C , M , Y ) K= min(C,M,Y) K=min(C,M,Y)
K = 1 K=1 K=1,则产生无颜色贡献的纯黑色,由此得出 C = 0 , M = 0 , Y = 0 C = 0, M=0, Y=0 C=0,M=0,Y=0,否则:
C = ( C − K ) / ( 1 − K ) C = (C - K)/(1 - K) C=(CK)/(1K)
M = ( M − K ) / ( 1 − K ) M = (M - K)/(1 - K) M=(MK)/(1K)
Y = ( Y − k ) / ( 1 − K ) Y = (Y - k)/(1 - K) Y=(Yk)/(1K)

从CMYK到CMY的转换是:
C = C ( 1 − K ) + K C = C(1 - K) + K C=C(1K)+K
M = M ( 1 − K ) + K M = M(1 - K)+ K M=M(1K)+K
Y = Y ( 1 − K ) + K Y = Y(1 - K) + K Y=Y(1K)+K

def rgb_2_cmy(img):
    
    """
    RGB image convert to CMY
    param: img: input RGB image
    return CMY image
    """
    
    img_norm = normalize(img).astype(np.float32)
    img_cmy = 1 - img_norm
#     img_cmy = np.uint8(img_cmy * 255)
    
    return img_cmy
def rgb_2_cmyk(img):
    """
    RGB image convert to CMYK
    param: img: input RGB image
    return CMYK image
    """
    height, width, channel = img.shape

    img_cmy = 1 - normalize(img).astype(np.float32)

    img_c = np.zeros((height, width), dtype=np.float32)
    img_m = np.zeros((height, width), dtype=np.float32)
    img_y = np.zeros((height, width), dtype=np.float32)
    img_k = np.zeros((height, width), dtype=np.float32)

    for h in range(height):
        for w in range(width):
            temp = img[h, w]
            k = min(temp[0], temp[1], temp[2])
            c, m, y = img_cmy[h, w]
            if k == 1:
                img_c[h, w] = 0
                img_m[h, w] = 0
                img_y[h, w] = 0
                img_k[h, w] = 1
            else:
                img_c[h, w] = (c - k) / (1 - k)
                img_m[h, w] = (m - k) / (1 - k)
                img_y[h, w] = (y - k) / (1 - k)
                img_k[h, w] = k

    img_cmyk = np.dstack((img_c, img_m, img_y, img_k))
    img_dst = normalize(img_cmyk)
#     img_dst = np.uint8(normalize(img_cmyk) * 255)
    
    return img_dst
def rgb_2_cmyk_2(img):
    """There still have some problem"""
    height, width, channel = img.shape
    img_cmy = 1 - normalize(img)

    k = np.min(img_cmy, axis=2)

    img_c = img_cmy[:, :, 0]
    img_m = img_cmy[:, :, 1]
    img_y = img_cmy[:, :, 2]
    
    # 当 K != 1 时
    img_c = np.where(k == 1, img_c, (img_c - k) / (1 - k + 1e-5))
    img_m = np.where(k == 1, img_m, (img_m - k) / (1 - k + 1e-5))
    img_y = np.where(k == 1, img_y, (img_y - k) / (1 - k + 1e-5))

    # 当 K = 1  时
    img_c = np.where(k != 1, img_c, 0)
    img_m = np.where(k != 1, img_m, 0)
    img_y = np.where(k != 1, img_y, 0)

    img_cmyk = np.dstack((img_c, img_m, img_y))
    img_cmyk = normalize(img_cmyk)
    
    return img_cmyk
# RGB 2 CMYK
img_ori = cv2.imread('DIP_Figures/DIP3E_Original_Images_CH06/Fig0646(a)(lenna_original_RGB).tif')

# 这里需要先转为float,不然会出现意想不到的结果
img_ori_norm = normalize(img_ori).astype(np.float32)

img_rgb = img_ori_norm[:, :, ::-1] # 这个下面实现是一样的效果[0, 1, 2], 反转为[2, 1, 0]
# img_rgb = img_ori[..., ::-1]
print(img_rgb[0, 0])

plt.figure(figsize=(20, 5))

img_b = img_ori_norm[:, :, 0]
img_g = img_ori_norm[:, :, 1]
img_r = img_ori_norm[:, :, 2]

img_cmy = rgb_2_cmy(img_rgb)
print(img_cmy[0, 0])

img_cmyk = rgb_2_cmyk(img_rgb)
print(img_cmyk[0, 0])

plt.subplot(141), plt.imshow(img_rgb, ), plt.title('Original'), plt.xticks([]), plt.yticks([])
plt.subplot(142), plt.imshow(img_cmy, ), plt.title('CMY'), plt.xticks([]), plt.yticks([])
plt.subplot(143), plt.imshow(img_cmyk, ), plt.title('CMYK'), plt.xticks([]), plt.yticks([])
plt.subplot(144), plt.imshow(img_r, ), plt.title('Red Channel'), plt.xticks([]), plt.yticks([])

plt.tight_layout()
plt.show()
[0.7882353  0.29411766 0.00392157]
[0.2117647  0.7058823  0.99607843]
[0.78587306 0.9201019  0.9989347  0.7304729 ]

在这里插入图片描述

# Pillow CMYK
img_ori = PIL.Image.open('DIP_Figures/DIP3E_Original_Images_CH06/Fig0646(a)(lenna_original_RGB).tif')

img_cmyk = img_ori.convert("CMYK")
print(f"Mode: {img_cmyk.mode}, shape: {np.array(img_cmyk).shape}")

print(f'Pixel value: RGB: {img_ori.getpixel((0, 0))}, CMYK: {img_cmyk.getpixel((0, 0))}')

plt.figure(figsize=(20, 5))
plt.subplot(141), plt.imshow(img_ori, ), plt.title('Original')
plt.subplot(142), plt.imshow(img_cmyk, ), plt.title('CMYK')
plt.tight_layout()
plt.show()
Mode: CMYK, shape: (512, 512, 4)
Pixel value: RGB: (201, 75, 1), CMYK: (54, 180, 254, 0)

在这里插入图片描述

HSI

H = { θ , B ≤ G 360 − θ , B > G H = \begin{cases} \theta, & B \leq G \\ 360 - \theta, & B > G \end{cases} H={θ,360θ,BGB>G
θ = a r c c o s [ 1 2 [ ( R − G ) + ( R − B ) ] [ ( R − G ) 2 + ( R − B ) ( G − B ) ] 1 / 2 ] \theta = arccos\bigg[\frac{\frac{1}{2}[(R-G) + (R-B)]}{[(R-G)^2 + (R-B)(G-B)]^{1/2}} \bigg] θ=arccos[[(RG)2+(RB)(GB)]1/221[(RG)+(RB)]]
S = 1 − 3 ( R + G + B ) [ m i n ( R , G , B ) ] S = 1 - \frac{3}{(R + G + B)}[min(R, G, B)] S=1(R+G+B)3[min(R,G,B)]
I = 1 3 ( R + G + B ) I = \frac{1}{3}(R + G + B) I=31(R+G+B)

RGB 值已被归一化到区间【0,1】,并且角度 θ \theta θ是相对于HSI空间的红色轴来测量的,将得到的所有值除以360,可将色调归一化到区间【0, 1】。

def rgb2hsi(img):
    
    """
    RGB image convert to CMYK
    param: img: input RGB image
    return CMYK image
    """
    img_rgb = img.copy()
    H = np.zeros(img_rgb.shape[:2])
    S = np.zeros(img_rgb.shape[:2])
    I = np.zeros(img_rgb.shape[:2])

    height, width = img_rgb.shape[:2]
    for h in range(height):
        for w in range(width):
            temp = img_rgb[h, w]
            R = temp[0]
            G = temp[1]
            B = temp[2]
            numerator = ((R - G) + (R - B)) / 2
            denominator = np.power((R - G)**2 + (R - B) * (G - B), 1/2)
            theta = np.arccos(numerator / (denominator + 1e-5))
            print(theta)
            if B <= G:
                H[h, w] = theta / 360
            else:
                H[h, w] = (360 - theta) / 360

            S[h, w] = 1 - ((3 * min(R, G, B))/ (R + G + B + 1e-5))

            I[h, w] = (R + G + B + 1e-5) / 3

    img_HSI = np.dstack((H, S, I))
    img_HSI = np.uint8(normalize(img_HSI) * 255)
    
    return img_HSI
# RGB 2 HSI
img_ori = cv2.imread('DIP_Figures/DIP3E_Original_Images_CH06/Fig0646(a)(lenna_original_RGB).tif')

img_ori_norm = normalize(img_ori)

img_rgb = img_ori_norm[:, :, ::-1] # 这个下面实现是一样的效果[0, 1, 2], 反转为[2, 1, 0]
# img_rgb = img_ori[..., ::-1]
print(img_rgb[0, 0])

plt.figure(figsize=(20, 5))

# 这里需要先转为float,不然会出现意想不到的结果
img_b = img_ori_norm[:, :, 0].astype(np.float32)
img_g = img_ori_norm[:, :, 1].astype(np.float32)
img_r = img_ori_norm[:, :, 2].astype(np.float32)

H = np.zeros(img_rgb.shape[:2])
S = np.zeros(img_rgb.shape[:2])
I = np.zeros(img_rgb.shape[:2])


height, width = img_rgb.shape[:2]
for h in range(height):
    for w in range(width):
        R = img_r[h, w]
        G = img_g[h, w]
        B = img_b[h, w]
        numerator = ((R - G) + (R - B))
        denominator = 2 * np.sqrt((R - G)**2 + (R - B) * (G - B))
        theta = np.arccos(numerator / (denominator + 1e-5))
        degree = np.rad2deg(theta)
#         print(degree)
        if B <= G:
            H[h, w] = degree
        else:
            H[h, w] = (360 - degree)
            
        S[h, w] = 1 - ((3 * min(R, G, B))/ (R + G + B + 1e-5))
        
        I[h, w] = (R + G + B) / 3

H = normalize(H)
H = H.reshape(H.shape[0], H.shape[1], 1)
S = S.reshape(S.shape[0], S.shape[1], 1)
I = I.reshape(H.shape[0], H.shape[1], 1)
S = normalize(S)
I = normalize(I)
img_HSI = np.concatenate((H, S, I), axis=2)
# img_HSI = img_HSI.reshape(img_ori.shape[0], img_ori.shape[1], 3)
# img_HSI = img_HSI.transpose((1, 2, 0))
print(img_HSI.shape)
# img_HSI = np.uint8(normalize(img_HSI) * 255)
        
plt.subplot(141), plt.imshow(img_rgb, ), plt.title('Original')

plt.subplot(142), plt.imshow(img_HSI, ), plt.title('HSI')
# plt.subplot(143), plt.imshow(img_cmyk, ), plt.title('CMYK')
# plt.subplot(144), plt.imshow(img_r, ), plt.title('Red Channel')

plt.tight_layout()
plt.show()
[0.78823529 0.29411765 0.00392157]
(512, 512, 3)

在这里插入图片描述

# RGB 2 HSI
img_ori = cv2.imread('DIP_Figures/DIP3E_Original_Images_CH06/Fig0646(a)(lenna_original_RGB).tif')

img_hsi = cv2.cvtColor(img_ori, cv2.COLOR_BGR2HSV_FULL)      

plt.figure(figsize=(20, 5))
plt.subplot(141), plt.imshow(img_rgb, ), plt.title('Original')

plt.subplot(142), plt.imshow(img_hsi, ), plt.title('HSI')
# plt.subplot(143), plt.imshow(img_cmyk, ), plt.title('CMYK')
# plt.subplot(144), plt.imshow(img_r, ), plt.title('Red Channel')

plt.tight_layout()
plt.show()

在这里插入图片描述

img_ori = PIL.Image.open('DIP_Figures/DIP3E_Original_Images_CH06/Fig0646(a)(lenna_original_RGB).tif')

img_HSI = img_ori.convert("HSV")
print(f"Mode: {img_HSI.mode}, shape: {np.array(img_HSI).shape}")

print(f'Pixel value: RGB: {img_ori.getpixel((0, 0))}, HSI: {img_HSI.getpixel((0, 0))}')

plt.figure(figsize=(20, 5))
plt.subplot(141), plt.imshow(img_ori, ), plt.title('Original')
plt.subplot(142), plt.imshow(img_HSI, ), plt.title('HSI')
plt.tight_layout()
plt.show()
Mode: HSV, shape: (512, 512, 3)
Pixel value: RGB: (201, 75, 1), HSI: (15, 253, 201)

在这里插入图片描述

  • 0
    点赞
  • 2
    评论
  • 1
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

相关推荐
©️2020 CSDN 皮肤主题: 深蓝海洋 设计师:CSDN官方博客 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值