OpenCV-Python實戰(6)——OpenCV中的色彩空間和色彩映射(??含大量示例??,建議收藏)
- 0. 前言
- 1. 色彩空間
- 1.1 顯示色彩空間
- 1.2 不同色彩空間在皮膚分割中的不同效果
- 2. 色彩映射
- 2.1 OpenCV 中的色彩映射
- 2.2 自定義色彩映射
- 2.3 顯示自定義色彩映射圖例
- 小結
- 系列鏈接
0. 前言
為了更好的進行影像處理,我們有時會使用不同的色彩空間,色彩空間是一個抽象的數學模型概念,色彩是人的眼睛對于不同頻率的光線的不同感受,為了更好的表示色彩,人們建立了多種色彩模型以一維、二維、三維等坐標系來描述不同色彩,這種坐標系所能定義的色彩范圍即色彩空間,而色彩映射是將影像在一個色彩空間映射至另一色彩空間的操作,通常可以將灰度影像著色為等效的偽色彩影像,
1. 色彩空間
首先介紹流行 OpenCV 中的色彩空間的基礎知識—— RGB、CIE L*a*b*、HSL、HSV 以及 YCbCr,
OpenCV 提供了 150 多種色彩空間轉換方法來執行用戶所需的轉換,在以下示例中,將演示如何將以 RGB 色彩空間加載的影像轉換到其他色彩空間(例如,HSV、HLS 或 YCbCr),
1.1 顯示色彩空間
常用的色彩空間如下表所示:
| 色彩空間 | 簡介 |
|---|---|
| RGB | 加色空間,特定顏色由紅色、綠色和藍色的分量值表示,其作業方式與人類視覺類似的,因此該色彩空間非常適合用于計算機顯示影像圖形 |
| CIELAB | 也稱為 CIE Lab* 或簡稱為 LAB,將特定顏色表示為三個數值,其中 L* 表示亮度,a* 表示綠-紅分量,b* 表示藍色-黃色成分,通常用于一些影像處理演算法 |
| HSV | HSV 是RGB色彩空間的一種變形,特定顏色使用色相 (hue)、飽和度 (saturation)、明度 (value) 三個分量表示 |
| HSL | 也稱 HLS 或 HSI (I指intensity),與 HSV非常相似,區別在于其使用亮度 (lightness) 替代了明度 (brightness) |
| YCbCr | 視頻和數字攝影系統中使用的一系列色彩空間,根據色度分量 (Y) 和兩個色度分量( Cb 和 Cr )表示顏色,在影像分割中非常流行 |
在以下示例中,影像被加載到 BGR 色彩空間并轉換為上述色彩空間,在此腳本中,關鍵函式是 cv2.cvtColor(),它可以將一種色彩空間的輸入影像轉換為另一種色彩空間,
def show_with_matplotlib(color_img, title, pos):
img_RGB = color_img[:, :, ::-1]
ax = plt.subplot(3, 6, pos)
plt.imshow(img_RGB)
plt.title(title, fontsize=8)
plt.axis('off')
image = cv2.imread('example.png')
plt.figure(figsize=(12, 5))
plt.suptitle("Color spaces in OpenCV", fontsize=14, fontweight='bold')
gray_image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
(bgr_b, bgr_g, bgr_r) = cv2.split(image)
# 轉換為 HSV
hsv_image = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)
(hsv_h, hsv_s, hsv_v) = cv2.split(hsv_image)
# 轉換為 HLS
hls_image = cv2.cvtColor(image, cv2.COLOR_BGR2HLS)
(hls_h, hls_l, hls_s) = cv2.split(hls_image)
# 轉換為 YCrCb
ycrcb_image = cv2.cvtColor(image, cv2.COLOR_BGR2YCrCb)
(ycrcb_y, ycrcb_cr, ycrcb_cb) = cv2.split(ycrcb_image)
show_with_matplotlib(image, "BGR - image", 1)
# Show gray image:
show_with_matplotlib(cv2.cvtColor(gray_image, cv2.COLOR_GRAY2BGR), "gray image", 1 + 6)
# 顯示 RGB 分量通道
show_with_matplotlib(cv2.cvtColor(bgr_b, cv2.COLOR_GRAY2BGR), "BGR - B comp", 2)
show_with_matplotlib(cv2.cvtColor(bgr_g, cv2.COLOR_GRAY2BGR), "BGR - G comp", 2 + 6)
show_with_matplotlib(cv2.cvtColor(bgr_r, cv2.COLOR_GRAY2BGR), "BGR - R comp", 2 + 6 * 2)
# 展示其他色彩空間分量通道
# ...

在進行色彩空間轉換時,應明確指定通道的順序( BGR 或 RGB ):
# 將影像加載到 BGR 色彩空間中
image = cv2.imread('color_spaces.png')
# 將其轉換為 HSV 色彩空間
hsv_image = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)
可以看到,此處使用了 cv2.COLOR_BGR2HSV 而不是 cv2.COLOR_RGB2HSV,
1.2 不同色彩空間在皮膚分割中的不同效果
上述色彩空間可用于不同的影像處理任務和技術,以皮膚分割為例,我們在不同的色彩空間內查看不同的演算法執行皮膚分割的效果,
在這個示例中的關鍵函式除了上述 cv2.cvtColor() 函式外,還包括 cv2.inRange(),它用于檢查陣列中包含的元素是否位于接受的兩個陣列引數的元素之間(下邊界陣列和上邊界陣列),
因此,我們使用 cv2.inRange() 函式來檢測與皮膚對應的顏色,為這兩個陣列(下邊界和上邊界)定義的值在分割演算法的性能中起著至關重要的作用,可以通過修改上、下邊界陣列進行實驗,以找到最合適的值,
import numpy as np
import cv2
import matplotlib.pyplot as plt
import os
# Name and path of the images to load:
image_names = ['1.png', '2.png', '3.png', '4.png', '5.png', '6.png']
path = 'skin_test_imgs'
# Load all test images building the relative path using 'os.path.join'
def load_all_test_images():
"""Loads all the test images and returns the created array containing the loaded images"""
skin_images = []
for index_image, name_image in enumerate(image_names):
# Build the relative path where the current image is:
image_path = os.path.join(path, name_image)
# print("image_path: '{}'".format(image_path))
# Read the image and add it (append) to the structure 'skin_images'
img = cv2.imread(image_path)
skin_images.append(img)
# Return all the loaded test images:
return skin_images
# 可視化
def show_images(array_img, title, pos):
for index_image, image in enumerate(array_img):
show_with_matplotlib(image, title + "_" + str(index_image + 1), pos + index_image)
#
def show_with_matplotlib(color_img, title, pos):
# 將 BGR 影像轉化為 RGB
img_RGB = color_img[:, :, ::-1]
ax = plt.subplot(5, 6, pos)
plt.imshow(img_RGB)
plt.title(title, fontsize=8)
plt.axis('off')
# 上下界陣列
lower_hsv = np.array([0, 48, 80], dtype='uint8')
upper_hsv = np.array([20, 255, 255], dtype='uint8')
# 基于 HSV 顏色空間的皮膚檢測
def skin_detector_hsv(bgr_image):
hsv_image = cv2.cvtColor(bgr_image, cv2.COLOR_BGR2HSV)
# 在 HSV 色彩空間中查找具有膚色的區域
skin_region = cv2.inRange(hsv_image, lower_hsv, upper_hsv)
return skin_region
lower_hsv_2 = np.array([0, 50, 0], dtype="uint8")
upper_hsv_2 = np.array([120, 150, 255], dtype="uint8")
# 基于 HSV 顏色空間的皮膚檢測
def skin_detector_hsv_2(bgr_image):
# 將圖片從 BRG 色彩空間轉換到 HSV 空間
hsv_image = cv2.cvtColor(bgr_image, cv2.COLOR_BGR2HSV)
# 在 HSV 色彩空間中查找具有膚色的區域
skin_region = cv2.inRange(hsv_image, lower_hsv_2, upper_hsv_2)
return skin_region
lower_ycrcb = np.array([0, 133, 77], dtype="uint8")
upper_ycrcb = np.array([255, 173, 127], dtype="uint8")
# 基于 YCrCb 顏色空間的皮膚檢測
def skin_detector_ycrcb(bgr_image):
ycrcb_image = cv2.cvtColor(bgr_image, cv2.COLOR_BGR2YCR_CB)
skin_region = cv2.inRange(ycrcb_image, lower_ycrcb, upper_ycrcb)
return skin_region
# 基于 bgr 顏色空間的皮膚檢測的閾值設定
def bgr_skin(b, g, r):
# 值基于論文《RGB-H-CbCr Skin Colour Model for Human Face Detection》
e1 = bool((r > 95) and (g > 40) and (b > 20) and ((max(r, max(g, b)) - min(r, min(g, b))) > 15) and (abs(int(r) - int(g)) > 15) and (r > g) and (r > b))
e2 = bool((r > 220) and (g > 210) and (b > 170) and (abs(int(r) - int(g)) <= 15) and (r > b) and (g > b))
return e1 or e2
# 基于 bgr 顏色空間的皮膚檢測
def skin_detector_bgr(bgr_image):
h = bgr_image.shape[0]
w = bgr_image.shape[1]
res = np.zeros((h, w, 1), dtype='uint8')
for y in range(0, h):
for x in range(0, w):
(b, g, r) = bgr_image[y, x]
if bgr_skin(b, g, r):
res[y, x] = 255
return res
skin_detectors = {
'ycrcb': skin_detector_ycrcb,
'hsv': skin_detector_hsv,
'hsv_2': skin_detector_hsv_2,
'bgr': skin_detector_bgr
}
def apply_skin_detector(array_img, skin_detector):
skin_detector_result = []
for index_image, image in enumerate(array_img):
detected_skin = skin_detectors[skin_detector](image)
bgr = cv2.cvtColor(detected_skin, cv2.COLOR_GRAY2BGR)
skin_detector_result.append(bgr)
return skin_detector_result
plt.figure(figsize=(15, 8))
plt.suptitle("Skin segmentation using different color spaces", fontsize=14, fontweight='bold')
# 加載影像
test_images = load_all_test_images()
# 繪制原始影像
show_images(test_images, "test img", 1)
# 對于每個影像應用皮膚檢測函式
for i, key in enumerate(skin_detectors.keys()):
show_images(apply_skin_detector(test_images, key), key, 7 + i * 6)
plt.show()
構建 skin_detectors 字典將所有皮膚分割演算法應用于測驗影像,上例中定義了四個皮膚檢測器,可以使用以下方法呼叫皮膚分割檢測函式(例如 skin_detector_ycrcb ):
detected_skin = skin_detectors['ycrcb'](image)
程式的分割結果如下所示:

可以使用多個測驗影像來查看應用不同皮膚分割演算法的效果,以了解這些演算法在不同條件下的作業方式,
2. 色彩映射
在一些計算機視覺應用程式中,演算法的輸出結果是灰度影像,但是,人眼在感知彩色影像的變化時更加敏感,而不擅長觀察灰度影像的變化,因此我們常常需要將灰度影像重新著色轉換為等效的偽彩色影像,
2.1 OpenCV 中的色彩映射
為了執行這種色彩轉換,OpenCV 包含多種色彩映射來增強可視化效果,cv2.applyColorMap() 函式在給定的影像上應用色彩映射,例如應用 cv2.COLORMAP_HSV 色彩映射:
img_COLORMAP_HSV = cv2.applyColorMap(gray_img, cv2.COLORMAP_HSV)
OpenCV定義的色彩映射如下(可以直接利用編號作為引數來呼叫相應色彩映射,類似別名):
| 色彩映射名 | 編號 |
|---|---|
| COLORMAP_AUTUMN | 0 |
| COLORMAP_BONE | 1 |
| COLORMAP_JET | 2 |
| COLORMAP_WINTER | 3 |
| COLORMAP_RAINBOW | 4 |
| COLORMAP_OCEAN | 5 |
| COLORMAP_SUMMER | 6 |
| COLORMAP_SPRING | 7 |
| COLORMAP_COOL | 8 |
| COLORMAP_HSV | 9 |
| COLORMAP_HOT | 11 |
| COLORMAP_PINK | 10 |
| COLORMAP_PARULA | 12 |
我們可以將把所有的顏色映射應用到同一個灰度影像上,并將它們繪制在同一個圖形中:
def show_with_matplotlib(color_img, title, pos):
img_RGB = color_img[:, :, ::-1]
ax = plt.subplot(2, 7, pos)
plt.imshow(img_RGB)
plt.title(title, fontsize=8)
plt.axis('off')
# 加載影像并轉化為灰度影像
gray_img = cv2.imread('12.png', cv2.IMREAD_GRAYSCALE)
# 色彩映射串列
colormaps = ["AUTUMN", "BONE", "JET", "WINTER", "RAINBOW", "OCEAN", "SUMMER", "SPRING", "COOL", "HSV", "HOT", "PINK", "PARULA"]
plt.figure(figsize=(12, 5))
plt.suptitle("Colormaps", fontsize=14, fontweight='bold')
show_with_matplotlib(cv2.cvtColor(gray_img, cv2.COLOR_GRAY2BGR), "GRAY", 1)
# 應用色彩映射
for idx, val in enumerate(colormaps):
show_with_matplotlib(cv2.applyColorMap(gray_img, idx), val, idx + 2)
plt.show()
程式的運行結果如下圖所示:

在上圖中,可以看到將所有預定義的顏色映射應用于灰度影像以增強可視化的效果,
2.2 自定義色彩映射
可以使用多種方法將自定義顏色映射應用于影像,
第一種方法是定義一個色彩映射,將 0 到 255 個灰度值映射到 256 種顏色,這可以通過創建大小為 256 x 1 的 8 位彩色影像來完成,以便存盤所有創建的顏色,之后,可以以下方法通過查找表將影像的灰度強度映射到定義的顏色:
- 使用
cv2.LUT()函式 - 使用
cv2.applyColorMap()函式
需要注意的是,在創建大小為256 x 1的8位彩色影像用于存盤影像時,如果打算使用cv2.LUT(),則應按如下方式創建影像:
lut = np.zeros((256, 3), dtype=np.uint8)
如果打算使用 cv2.cv2.applyColorMap(),則應使用:
lut = np.zeros((256, 1, 3), dtype=np.uint8)
完整的代碼如下:
import cv2
import numpy as np
import matplotlib.pyplot as plt
def apply_rand_custom_colormap_values(im_gray):
lut = np.random.randint(255, size=(256, 1, 3), dtype=np.uint8)
im_color = cv2.applyColorMap(im_gray, lut)
return im_color
def apply_rand_custom_colormap_values2(im_gray):
# 創建隨機 LUT
lut = np.random.randint(255, size=(256, 3), dtype=np.uint8)
# 使用 cv2.LUT() 應用自定義色彩映射
s0, s1 = im_gray.shape
im_color = np.empty(shape=(s0, s1, 3), dtype=np.uint8)
for i in range(3):
im_color[..., i] = cv2.LUT(im_gray, lut[:, i])
return im_color
def show_with_matplotlib(color_img, title, pos):
img_RGB = color_img[:, :, ::-1]
ax = plt.subplot(1, 5, pos)
plt.imshow(img_RGB)
plt.title(title, fontsize=8)
plt.axis('off')
# 讀取影像并轉化為灰度影像
gray_img = cv2.cvtColor(cv2.imread('8.png'), cv2.COLOR_BGR2GRAY)
plt.figure(figsize=(12, 2))
plt.suptitle("Custom colormaps providing all values", fontsize=14, fontweight='bold')
show_with_matplotlib(cv2.cvtColor(gray_img, cv2.COLOR_GRAY2BGR), "gray", 1)
# 應用色彩映射
custom_rand_1 = apply_rand_custom_colormap_values(gray_img)
custom_rand_2 = apply_rand_custom_colormap_values2(gray_img)
# 可以自行創建固定值色彩映射并應用,與隨機創建類似,在此不再贅述
# custom_values_1 = apply_custom_colormap_values(gray_img)
# custom_values_2 = apply_custom_colormap_values2(gray_img)
# 可視化
show_with_matplotlib(custom_rand_1, "cv2.applyColorMap()", 2)
show_with_matplotlib(custom_rand_2, "cv2.LUT()", 3)
plt.show()

自定義色彩映射的第二種方法是僅提供一些關鍵顏色,然后對這些值進行插值,以獲得構建查找表所需的所有顏色,
撰寫 build_lut() 函式根據這些關鍵顏色構建查找表:基于 5 個預先定義的色點,呼叫 np.linespace() 在預定義的每個色點區間內計算均勻間隔的顏色:
def build_lut(cmap):
lut = np.empty(shape=(256, 3), dtype=np.uint8)
max = 256
# 構建查找表
lastval, lastcol = cmap[0]
for step, col in cmap[1:]:
val = int(step * max)
for i in range(3): lastval, val - lastval))
lut[lastval:val, i] = np.linspace(lastcol[i], col[i], val - lastval)
lastcol = col
lastval = val
return lut
然后應用自定義顏色映射:
def show_with_matplotlib(color_img, title, pos):
"""Shows an image using matplotlib capabilities"""
# Convert BGR image to RGB
img_RGB = color_img[:, :, ::-1]
ax = plt.subplot(2, 3, pos)
plt.imshow(img_RGB)
plt.title(title, fontsize=8)
plt.axis('off')
# Read grayscale image:
gray_img = cv2.imread('shades.png', cv2.IMREAD_GRAYSCALE)
plt.figure(figsize=(14, 3))
plt.suptitle("Custom color maps based on key colors", fontsize=14, fontweight='bold')
# 讀取影像并轉化為灰度影像
gray_img = cv2.cvtColor(cv2.imread('8.png'), cv2.COLOR_BGR2GRAY)
# 應用色彩映射
custom_1 = apply_color_map_1(gray_img, ((0, (255, 0, 255)), (0.25, (255, 0, 180)), (0.5, (255, 0, 120)),
(0.75, (255, 0, 60)), (1.0, (255, 0, 0))))
custom_2 = apply_color_map_1(gray_img, ((0, (0, 255, 128)), (0.25, (128, 184, 64)), (0.5, (255, 128, 0)),
(0.75, (64, 128, 224)), (1.0, (0, 128, 255))))
custom_3 = apply_color_map_2(gray_img, ((0, (255, 0, 255)), (0.25, (255, 0, 180)), (0.5, (255, 0, 120)),
(0.75, (255, 0, 60)), (1.0, (255, 0, 0))))
custom_4 = apply_color_map_2(gray_img, ((0, (0, 255, 128)), (0.25, (128, 184, 64)), (0.5, (255, 128, 0)),
(0.75, (64, 128, 224)), (1.0, (0, 128, 255))))
# 可視化
show_with_matplotlib(custom_1, "custom 1 using cv2.LUT()", 2)
show_with_matplotlib(custom_2, "custom 2 using cv2.LUT()", 3)
show_with_matplotlib(custom_3, "custom 3 using cv2.applyColorMap()", 5)
show_with_matplotlib(custom_4, "custom 4 using using cv2.applyColorMap()", 6)

在上圖中,可以看到將兩個自定義色彩映射應用于灰度影像的效果,
2.3 顯示自定義色彩映射圖例
最后,我們也可以在顯示自定義顏色映射時提供圖例,為了構建色彩映射圖例,撰寫 build_lut_image():
def build_lut_image(cmap, height):
lut = build_lut(cmap)
image = np.repeat(lut[np.newaxis, ...], height, axis=0)
return image
其首先呼叫 build_lut() 函式以獲取查找表,然后,呼叫 np.repeat() 以多次復制此查找表( height 次),這是由于查找表的形狀是 (256, 3),而輸出影像的形狀為 (height, 256, 3) ,為了增加新維度,我們還需要將 np.repeat()與 np.newaxis() 一起使用:
image = np.repeat(lut[np.newaxis, ...], height, axis=0)
運行結果如下圖所示:

在上圖中,可以看到將兩個自定義顏色映射應用于灰度影像并顯示每個顏色映射的圖例的效果,
小結
為了更好的表示色彩,建立了多種色彩模型以一維、二維、三維等坐標系來描述不同色彩,這種坐標系所能定義的色彩范圍即色彩空間,,函式 cv2.cvtColor() 可以將一種色彩空間的輸入影像轉換為另一種色彩空;而色彩映射是將影像在一個色彩空間映射至另一色彩空間的操作,cv2.applyColorMap() 或 cv2.LUT() 函式在給定的影像上應用色彩映射,通常可以將灰度影像著色為等效的偽色彩影像,同時還介紹了 用于獲取限定上下界的 cv2.inRange() 函式,
系列鏈接
OpenCV-Python實戰(1)——OpenCV簡介與影像處理基礎(內含大量示例,📕建議收藏📕)
OpenCV-Python實戰(2)——影像與視頻檔案的處理(兩萬字詳解,?📕建議收藏📕)
OpenCV-Python實戰(3)——OpenCV中繪制圖形與文本(萬字總結,?📕建議收藏📕)
OpenCV-Python實戰(4)——OpenCV常見影像處理技術(??萬字長文,含大量示例??)
OpenCV-Python實戰(5)——OpenCV影像運算(??萬字長文,含大量示例??)
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/301093.html
標籤:其他
