目录
在图像处理中,彩色的运用主要受两个因素的推动。
- 彩色是一个强有力的描绘子,它常常可简化从场景中提取和识别目标;
- 人可以辨别几千种彩色色调和亮度,但相比之下只能辨别几十种灰度色调。
第二点因素在人工图像分析中特别重要。
彩色图像处理可分为两个主要邻域:全彩色处理和伪彩色处理。
6.1 彩色基础
人类感知物体颜色是由物体反射光的性质决定的。可见光由电磁波谱中相对较窄的频段组成。物体反射的光若在所有可见光波长范围内是平衡的,那么对观察者来说显示为白色。然而,一个物体反射有限的可见光谱时,则呈现某种颜色。
描述彩色光源质量的3个基本量是辐射、光强和亮度。
- 辐射:从光源流出的能量的总量,通常用瓦特(W)来度量。
- 光强:用流明来度量,它给出观察者从光源感知的能量总和的度量。
- 亮度:是一个主观描绘子。实际上是不可度量的。体现了无色的强度的概念,并且是描述色彩感觉的一个关键因素。
三原色:红(R)绿(G)蓝(B)。人类对红光最敏感,对蓝光最不敏感。光合颜料的原色及二次下如下图:
通常用以区别不同颜色特性的是亮度、色调和饱和度。亮度表达了无色的强度概念;色调是光波混合中与主波长有关的属性,表示观察者感知的主要颜色 ;饱和度指的是相对纯净度,或一个色调混合的白光亮。色调与饱和度一起称为色度。
形成任何特殊彩色的红、绿、蓝的数量称为三色值,并分别表示为X,Y和Z。这样,一种颜色就可由其三色值系数定义为
可得:x+y+z=1
6.2 彩色模型
彩色模型的目的是,在某些标准下用通常可以接受的方式方便地对彩色加以说明。
6.2.1 RGB彩色模型
在RGB模型中,每种颜色出现在红、绿、蓝的原色光谱成分中。该模型基于笛卡尔坐标系。
import cv2
import matplotlib.pyplot as plt
import numpy as np
plt.figure()
image = cv2.imread(r'G:\4.png')
plt.subplot(2,2,1)
plt.imshow(image)
plt.axis('off')
R, G, B = cv2.split(image)
# 创建一个跟图片一样大小的全为0的矩阵
z = np.zeros(image.shape[:2], dtype=np.uint8)
red = cv2.merge([R, z, z]) # 第一个通道为红色,其它通道全为0
green = cv2.merge([z, G, z])
blue = cv2.merge([z, z, B])
plt.subplot(2,2,2)
plt.title('red')
plt.imshow(red)
plt.axis('off')
plt.subplot(2,2,3)
plt.title('green')
plt.imshow(green)
plt.axis('off')
plt.subplot(2,2,4)
plt.title('blue')
plt.imshow(blue)
plt.axis('off')
plt.show()
可以观察到图像的RGB通道图像
6.2.2 CMY和CMYK彩色模型
大多数在纸上沉积彩色颜料的设备,如彩色打印机和复印机,要求输入CMY数据或在内部进行RGB到CMY的转换。该转换是使用下面这个简单的操作执行的:
假设所有的彩色值都归一化到了区间[0,1]内。如上式表明涂有青色颜料的表面所反射的光中不包含红色(即公式中C=1-R)。类似地,纯深红色不反射绿色,纯黄色不反射蓝色。
实际上,为打印目的组合的颜色所产生的的黑色是不纯的。因此,为了生成真正的黑色,加入了第四种颜色——黑色,提出了CMYK模型。
将RGB图像转化为CMY:
'''-----------------RGB → CMY------------------------'''
import cv2
import imutils
def rgb_cmy(img):
r, g, b = cv2.split(img) # split the channels
# normalization [0,1]
r = r / 255.0
g = g / 255.0
b = b / 255.0
c = 1 - r
m = 1 - g
y = 1 - b
result = cv2.merge((c, m, y)) # merge the channels
return result
if __name__ == '__main__':
img = cv2.imread("G:/7.jpg")
img_CMY = rgb_cmy(img)
img_NEW = img_CMY * 255
#cv2.imwrite('F:/img_CMY.PNG', img_NEW)
cv2.imshow("CMY image", imutils.resize(img_CMY, 666))
cv2.imshow("original image", imutils.resize(img, 666))
cv2.waitKey(0)
cv2.destroyAllWindows()
6.2.3 HSI彩色模型
观察彩色物体时,我们用其色调、饱和度、亮度来描述这个物体。这三者的定义在之前已经进行过讨论。
HSI彩色空间的重要分量是垂直强度轴、到一个彩色点的向量长度和该向量与红轴的夹角。色度平面以何种形状出现是无关紧要的,因为这些形状中的任何一个都可以通过几何变换转换为其他形状。
给定一幅RGB彩色格式的图像,每个RGB像素的H分量可用下式得到:
饱和度分量由下式给出:
强度分量由下式给出:
RGB→HSI图像转换:
'''-----------------RGB → HSI------------------------'''
import cv2
import math
import imutils
import numpy as np
def rgb_hsi(rgb_Img):
img_rows = int(rgb_Img.shape[0])
img_cols = int(rgb_Img.shape[1])
b, g, r = cv2.split(rgb_Img)
# normalization[0,1]
r = r / 255.0
g = g / 255.0
b = b / 255.0
hsi_Img = rgb_Img.copy()
H, S, I = cv2.split(hsi_Img)
for i in range(img_rows):
for j in range(img_cols):
num = 0.5 * ((r[i, j]-g[i, j])+(r[i, j]-b[i, j]))
den = np.sqrt((r[i, j]-g[i, j])**2+(r[i, j]-b[i, j])*(g[i, j]-b[i, j]))
theta = float(np.arccos(num/den))
if den == 0:
H = 0
elif b[i, j] <= g[i, j]:
H = theta
else:
H = math.pi - theta
min_RGB = min(min(b[i, j], g[i, j]), r[i, j])
sum = b[i, j]+g[i, j]+r[i, j]
if sum == 0:
S = 0
else:
S = 1 - 3*min_RGB/sum
H = H/(math.pi)
I = sum/3.0
# 输出HSI图像,扩充到255以方便显示,一般H分量在[0,2pi]之间,S和I在[0,1]之间
hsi_Img[i, j, 0] = H*255
hsi_Img[i, j, 1] = S*255
hsi_Img[i, j, 2] = I*255
return hsi_Img
if __name__ == '__main__':
rgb_Img = cv2.imread("G:/6.jpg")
hsi_Img = rgb_hsi(rgb_Img)
cv2.imshow('original image', imutils.resize(rgb_Img, 600))
cv2.imshow('HSI image', imutils.resize(hsi_Img, 600))
key = cv2.waitKey(0) & 0xFF
if key == ord('q'):
cv2.destroyAllWindows()
HSI→RGB图像转换
'''-----------------HSI → RGB------------------------'''
import cv2
import math
import imutils
def hsi_rgb(hsi_img):
img_rows = int(hsi_img.shape[0])
img_cols = int(hsi_img.shape[1])
H, S, I = cv2.split(hsi_img)
# normalization[0,1]
H = H / 255.0
S = S / 255.0
I = I / 255.0
bgr_img = hsi_img.copy()
B, G, R = cv2.split(bgr_img)
for i in range(img_rows):
for j in range(img_cols):
if S[i, j] < 1e-6:
R = I[i, j]
G = I[i, j]
B = I[i, j]
else:
H[i, j] *= 360
if H[i, j] > 0 and H[i, j] <= 120:
B = I[i, j] * (1 - S[i, j])
R = I[i, j] * (1 + (S[i, j] * math.cos(H[i, j] * math.pi / 180)) / math.cos((60 - H[i, j]) * math.pi / 180))
G = 3 * I[i, j] - (R + B)
elif H[i, j] > 120 and H[i, j] <= 240:
H[i, j] = H[i, j] - 120
R = I[i, j] * (1 - S[i, j])
G = I[i, j] * (1 + (S[i, j] * math.cos(H[i, j] * math.pi / 180)) / math.cos((60 - H[i, j]) * math.pi / 180))
B = 3 * I[i, j] - (R + G)
elif H[i, j] > 240 and H[i, j] <= 360:
H[i, j] = H[i, j] - 240
G = I[i, j] * (1 - S[i, j])
B = I[i, j] * (1 + (S[i, j] * math.cos(H[i, j] * math.pi / 180)) / math.cos((60 - H[i, j]) * math.pi / 180))
R = 3 * I[i, j] - (G + B)
bgr_img[i, j, 0] = B * 255
bgr_img[i, j, 1] = G * 255
bgr_img[i, j, 2] = R * 255
return bgr_img
if __name__ == '__main__':
hsi_Img = cv2.imread("G:/HSI.jpg")
rgb_Img = hsi_rgb(hsi_Img)
cv2.imshow('original image', imutils.resize(rgb_Img, 600))
cv2.imshow('RGB image', imutils.resize(hsi_Img, 600))
key = cv2.waitKey(0) & 0xFF
if key == ord('q'):
cv2.destroyAllWindows()
可以观察到最后得到的RGB图像与原图像有差异。
6.3 伪彩色图像处理
伪彩色(也称为假彩色)图像处理包含根据特定标准对灰色值赋予颜色,主要应用是对于单幅图像或序列图像中灰度级事件的人类可视化和解释。
6.3.1灰度分层
如果一幅图像倍描述为三维函数,则分层方法可视为放置一些平行于该图像的坐标平面的平面,然后,每个平面在相交的区域中“切割”图像函数。
图中li表示灰度图像的某一个灰度级,li将灰度图像分成两个部分,并给两部分分别赋予不同的颜色。
当用更多的灰度级时,映射函数采用一种阶梯形式。
6.3.2 灰度到彩色的变换
对任何输入像素的灰度执行3个独立的变换,然后将3个变换结果分别送入彩色电视监视器的红、绿、蓝通道。这种方法产生一幅合成图像,合成图像的彩色内容由变换函数的特性调制。这些是在图像亮度值上进行的变换,而不是位置的函数。
一幅单色图像:
多幅单色图像:
6.4 全彩色图像处理基础
全彩色图像处理方法分为两大类:
- 分别处理每一幅分量图像,然后由分别处理过的分量图像来形成一幅处理过的合成彩色图像。
- 直接处理彩色像素。因为全彩色图像至少有3个分量,所以彩色像素实际上是向量。
令c代表RGB彩色空间中的一个任意向量:
彩色分量是坐标(x,y)的函数,表示为
每种彩色分量的处理要等同于基于向量的处理,必须满足两个条件:
- 处理必须对向量和标量都可用。
- 对向量的每个分量的操作对于其他分量必须是独立的。
6.5 彩色变换
彩色变换主要涉及在单一彩色模型内处理彩色图像的分量,而不是这些分量在不同模型间的转换。
6.5.1 公式
与灰度变换技术类似,我们用下式表达的彩色变换作为模型:
式中f(x,y)是彩色输入图像,g(x,y)是变换后或处理过的彩色输出图像,T是在(x,y)的空间邻域上对f操作的一个算子。这里的像素值是从彩色空间选择的、用来表示图像的彩色空间的3元组或4元组(即3个值或4个值的组合)。
ri和si是f(x,y)和g(x,y)在任何点处彩色分量的变量,n是彩色分量数,{T1,T2...,Tn}是对ri操作产生si的一组变换或彩色映射函数。
6.5.2 补色
在如图所示的彩色环上,与色调直接相对的另一端被称为补色。补色对于增强嵌在彩色图像暗区的细节很有用——特别是区域在大小上占优势时。
6.5.3 彩色分层
突出图像中某个特定彩色区域对从其周围分离出目标物体很有用。
思路有两种:
- 显示感兴趣的颜色以便从背景中突出它们。
- 想模板那样使用由彩色定义的区域,以便进一步处理
对彩色图像进行分层的简单方法之一是,把某些感兴趣区域之外的彩色映射为不突出的中性颜色。如果感兴趣的颜色由宽为W、中吸你在原型(即平均)颜色点并具有分量(a1,a2,...,an)的立方体(或超立方体,此时n>3)所包围,则必要的一组变换为
如果使用一个圆球体来指定感兴趣的颜色,则上式变为
6.5.4 色调和彩色校正
L*a*b*彩色分量由如下公式给出:
式中,
其中XW,YW和ZW是参考白色三激励值,即CIE标准D65照度下的一种完美的漫反射白色。
类似于HSI系统,L*a*b*系统是一种优秀的亮度和彩色分离器,它在图像操作和图像压缩方面很有用。
校准成像系统的主要意义是,它允许交互地和独立地校正色调与色彩的不平衡,即按两个顺序操作。
6.5.5直方图处理
直方图均衡会自动地确定一种变换,该变换试图产生具有均匀灰度值的直方图的图像。
由于彩色图像是由多个分量组成的,所以必须考虑适应多个分量和/或直方图的灰度级技术。单独对彩色图像的分量进行直方图均衡通常是不明智的,这将产生不正确的彩色。一种符合逻辑的方法是,均匀地展开这种彩色灰度,而保持彩色本身(即色调)不变。
6.6 平滑和锐化
6.6.1 彩色图像平滑
灰度级图像平滑可视为一种空间滤波操作,在这种操作中,滤波模板的系数具有相同的值。当模板滑过将被平滑的图像时,每个像素被由该模板定义的邻域中的像素的平均值代替。
在一幅RGB彩色图像中,令Sxy表示中心位于(x,y)的邻域定义的一组坐标。在该邻域中RGB分量的向量平均值为
我们将该向量的分量视为几幅标量图像,这些标量图像可通过传统的灰度级邻域处理,单独地平滑原RGB图像的每个平面来得到。
这样,我们就可得出结论:邻域平均平滑可以在每个彩色平面的基础上执行,其结果与使用RGB彩色向量执行平均是相同的。
import cv2
import numpy as np
import matplotlib.pyplot as plt
#读取图片
img = cv2.imread(r'G:\8.jpg')
source = cv2.cvtColor(img,cv2.COLOR_BGR2RGB)
#均值滤波
result = cv2.blur(source, (5,5))
#显示图形
titles = ['Source Image', 'Blur Image']
images = [source, result]
for i in range(2):
plt.subplot(1,2,i+1), plt.imshow(images[i], 'gray')
plt.title(titles[i])
plt.xticks([]),plt.yticks([])
plt.show()
观察到当设定卷积核为5*5时,选择图片处理效果不明显。
下图为10*10,明显观察到图像变得更加模糊。
6.6.2 彩色图像锐化
使用拉普拉斯变换方法进行图像(尖)锐化处理。
在RGB彩色系统中,向量c的拉普拉斯变换为
它告诉我们可通过分别计算每幅分量图像的拉普拉斯变换来计算全彩色图像的拉普拉斯变换。
6.7 基于彩色的图像分割
分割是把一幅图像分成多个区域的处理。
6.7.1 HSI彩色空间的分割
在HSI空间中,可利用色调不同进行分割,把饱和度作为一个模板。彩色图像分割中不常使用亮度图像,因为它不携带彩色信息。
6.7.2 RGB向量空间中的分割
虽然在HSI空间的工作更直观,但分割却是用RGB彩色向量能得到更好结果的邻域。
假定我们的目的是在RGB图像中分割一个特定染色区域的物体。给定一组感兴趣的有代表性彩色的彩色样点集,可得到我们希望分割的颜色的“平均”估计。用RGB向量a来表示这个平均彩色。分割的目的是将给定图像中的每个RGB像素按照是否具有指定范围内的颜色来划分。最简单的相似性度量是欧式距离。
令z表示RGB空间中的任意一点。如果他们之间的距离小于特定的预知D0,则称z与a是相似的。z和a间的欧氏距离由下式给出。
其中下表R,G,B表示向量a和z的RGB分量。满足D(z,a)≤D0的点的轨迹是半径为D0的实心球体。
上式的一种有用推广是形如下式的距离度量:
其中C表示我们希望分割的有代表性颜色的样本的协方差矩阵。
import cv2
import numpy as np
import matplotlib.pyplot as plt
img = 'strawberry_color.bmp'
f_bgr = cv2.imread(r'G:\4.png', cv2.IMREAD_COLOR)
f_rgb = cv2.cvtColor(f_bgr, cv2.COLOR_BGR2RGB)
bndbox = {'xmin': 89,
'ymin': 60,
'xmax': 320,
'ymax': 280}
f_rec = cv2.rectangle(f_rgb.copy(),
(bndbox['xmin'], bndbox['ymin']),
(bndbox['xmax'], bndbox['ymax']),
color=(255, 255, 0), thickness=5)
plt.imshow(f_rec)
plt.show()
def Euclid(f, box, d0):
"""
Calculate Euclid distance and return binarized image
:param f: img
:param box: (xmin, ymin, xmax, ymax) # VOC format
:param d0: condition
:return: binarized image according to condition
"""
H, W, C = f.shape
a = np.zeros(C, dtype='float')
b = np.zeros((H, W), dtype='int')
for c in range(C):
a[c] = np.mean(f[box[0]:box[2], box[1]:box[3] ,c])
a = a.reshape(C, 1)
for w in range(W):
for h in range(H):
z = f[h, w, :].reshape(C, 1)
d = z - a
DE = np.dot(d.T, d)
if DE.sum() <= d0:
b[h, w] = 1
return b
def binary_mix(f, b):
"""
mix input image and binarized image
:param f: input image
:param b: its binarized image with only two values 0 and 1
:return: g
"""
g = f.copy()
H, W, C = g.shape
for c in range(C):
g[:, :, c] = np.multiply(g[:, :, c], b)
return g
# Euclid distance
D0_0 = 5000
g_u_b_0 = Euclid(f_rgb, list(bndbox.values()), d0=D0_0)
D0_1 = 10000
g_u_b_1 = Euclid(f_rgb, list(bndbox.values()), d0=D0_1)
fig, axs = plt.subplots(2, 2, figsize=(10, 10))
axs[0][0].set_title('g_u_b_0: D0={}'.format(D0_0))
axs[0][0].imshow(g_u_b_0, cmap='gray')
axs[0][1].set_title('binary_mix(f_rgb, g_u_b_0): D0={}'.format(D0_0))
axs[0][1].imshow(binary_mix(f_rgb, g_u_b_0))
axs[1][0].set_title('g_u_b_1: D0={}'.format(D0_1))
axs[1][0].imshow(g_u_b_1, cmap='gray')
axs[1][1].set_title('binary_mix(f_rgb, g_u_b_1): D0={}'.format(D0_1))
axs[1][1].imshow(binary_mix(f_rgb, g_u_b_1))
plt.suptitle('Euclid distance')
plt.show()
def single_Manhattan(f, box, eta, channel=0):
"""
Calculate single channel Manhattan distance and return binarized image
:param f: img
:param box: (xmin, ymin, xmax, ymax) # VOC format
:param eta: condition
:param channel: int, channel number
:return: binarized image according to condition
"""
H, W, C = f.shape
b = np.zeros((H, W), dtype='int')
c = f[box[0]:box[2], box[1]:box[3] , channel]
a = np.mean(c)
sigma = np.std(c)
for w in range(W):
for h in range(H):
z = f[h, w, channel]
if np.abs(z - a) <= eta * sigma:
b[h, w] = 1
return b
# single red channel Manhattan distance
eta0 = 1.25
channel0 = 0
g_m_b_0 = single_Manhattan(f_rgb, list(bndbox.values()), eta=eta0, channel=channel0)
fig, axs = plt.subplots(2, 2, figsize=(10, 10))
axs[0][0].set_title('origin')
axs[0][0].imshow(f_rgb)
axs[0][1].set_title('origin with single channel: channel={}'.format(channel0))
axs[0][1].imshow(f_rgb[:, :, channel0], cmap='Reds')
axs[1][0].set_title(r'g_m_b_0: \eta={}'.format(eta0))
axs[1][0].imshow(g_m_b_0, cmap='gray')
axs[1][1].set_title(r'binary_mix(f_rgb, g_m_b_0): \eta={}'.format(eta0))
axs[1][1].imshow(binary_mix(f_rgb, g_m_b_0))
plt.suptitle('single red channel Manhattan distance')
plt.show()
# multi-channel average Manhattan distance
def multi_avg_Manhattan(f, box, eta):
"""
Calculate multi-channel average Manhattan distance and return binarized image
:param f: img
:param box: (xmin, ymin, xmax, ymax) # VOC format
:param eta: condition
:return: binarized image according to condition
"""
H, W, C = f.shape
a = np.zeros(C, dtype='float')
sigma = np.zeros(C, dtype='float')
b = np.zeros((H, W), dtype='int')
for c in range(C):
sam = f[box[0]:box[2], box[1]:box[3] ,c]
a[c] = np.mean(sam)
sigma[c] = np.std(sam)
sigmaL1 = np.sum(sigma)
a = a.reshape(C, 1)
for w in range(W):
for h in range(H):
z = f[h, w, :].reshape(C, 1)
d = z - a
DM = np.sum(np.abs(d))
if DM <= eta * sigmaL1:
b[h, w] = 1
return b
eta1 = 1.1
g_m_b_1 = multi_avg_Manhattan(f_rgb, list(bndbox.values()), eta=eta1)
fig, axs = plt.subplots(1, 2, figsize=(10, 5))
axs[0].set_title(r'g_m_b_1: \eta={}'.format(eta1))
axs[0].imshow(g_m_b_1, cmap='gray')
axs[1].set_title(r'binary_mix(f_rgb, g_m_b_1): \eta={}'.format(eta1))
axs[1].imshow(binary_mix(f_rgb, g_m_b_1))
plt.suptitle('multi-channel average Manhattan distance')
plt.show()
6.7.3 彩色边缘检测
边缘检测对图像分割来说是一个重要的工具。在本节中,我们的兴趣在于以单一图像为基础计算边缘,而不是直接在彩色向量空间中计算边缘的问题。
对于标量函数f(x,y),梯度是坐标(x,y)处指向f的最大变化理念方向的向量。
令r、g、b是沿RGB彩色空间的R,G,B轴的单位向量,并定义向量
和
令gxx,gyy和gxy表示这些向量的点积,如下所示:
R,G,B以及由此而来的g项是x和y的函数。使用这种表示法,可以证明c(x,y)的最大变化率方向可以由角度
给出,且在角度方向上点(x,y)处的变化率的值由下式给出:
6.8 彩色图像中的噪声
通常,彩色图像的噪声内容在每个彩色通道中具有相同的特性,但噪声对不同彩色通道所造成的影响不同。一种可能是个别通道的电子学故障。然而,不同的噪声水平更可能是由每个彩色通道的相对照射强度的差异造成的。
6.9 彩色图像压缩
因为描述彩色所要求的的比特数比描述灰度级所要求的比特数大3~4倍,所以数据压缩在存储和传输彩色图像中起着核心的作用。