opencv-python实战——opencv中的色彩空间和色彩映射(❤️含大量实例,建议收藏❤️)(代码片段)

盼小辉丶 盼小辉丶     2023-01-04     342

关键词:

0. 前言

为了更好的进行图像处理,我们有时会使用不同的色彩空间。色彩空间是一个抽象的数学模型概念,色彩是人的眼睛对于不同频率的光线的不同感受,为了更好的表示色彩,人们建立了多种色彩模型以一维、二维、三维等坐标系来描述不同色彩,这种坐标系所能定义的色彩范围即色彩空间。而色彩映射是将图像在一个色彩空间映射至另一色彩空间的操作,通常可以将灰度图像着色为等效的伪色彩图像。

1. 色彩空间

首先介绍流行 OpenCV 中的色彩空间的基础知识—— RGBCIE L*a*b*HSLHSV 以及 YCbCr
OpenCV 提供了 150 多种色彩空间转换方法来执行用户所需的转换。在以下示例中,将演示如何将以 RGB 色彩空间加载的图像转换到其他色彩空间(例如,HSVHLSYCbCr)。

1.1 显示色彩空间

常用的色彩空间如下表所示:

色彩空间简介
RGB加色空间,特定颜色由红色、绿色和蓝色的分量值表示,其工作方式与人类视觉类似的,因此该色彩空间非常适合用于计算机显示图像图形
CIELAB也称为 CIE Lab* 或简称为 LAB,将特定颜色表示为三个数值,其中 L* 表示亮度,a* 表示绿-红分量,b* 表示蓝色-黄色成分,通常用于一些图像处理算法
HSVHSV 是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)

# 展示其他色彩空间分量通道
# ...


在进行色彩空间转换时,应明确指定通道的顺序( BGRRGB ):

# 将图像加载到 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_AUTUMN0
COLORMAP_BONE1
COLORMAP_JET2
COLORMAP_WINTER3
COLORMAP_RAINBOW4
COLORMAP_OCEAN5
COLORMAP_SUMMER6
COLORMAP_SPRING7
COLORMAP_COOL8
COLORMAP_HSV9
COLORMAP_HOT11
COLORMAP_PINK10
COLORMAP_PARULA12

我们可以将把所有的颜色映射应用到同一个灰度图像上,并将它们绘制在同一个图形中:

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 自定义色彩映射

可以使用多种方法将自定义颜色映射应用于图像。
第一种方法是定义一个色彩映射,将 0255 个灰度值映射到 256 种颜色。这可以通过创建大小为 256 x 18 位彩色图像来完成,以便存储所有创建的颜色。之后,可以以下方法通过查找表将图像的灰度强度映射到定义的颜色:

  1. 使用 cv2.LUT() 函数
  2. 使用 cv2.applyColorMap() 函数
    需要注意的是,在创建大小为 256 x 18 位彩色图像用于存储图像时,如果打算使用 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, 3opencv-python实战(16)——人脸追踪详解(代码片段)

OpenCV-Python实战(16)——人脸追踪详解0.前言1.人脸追踪技术简介2.使用基于dlibDCF的跟踪器进行人脸跟踪2.1完整代码3.使用基于dlibDCF的跟踪器进行对象跟踪3.2完整代码小结系列链接0.前言人脸处理是人工智能中的一个热门... 查看详情

opencv-python实战——opencv用于图像分割的阈值技术(含大量示例,建议收藏)(代码片段)

OpenCV-Python实战(9)——OpenCV用于图像分割的阈值技术(含大量示例,建议收藏)0.前言1.阈值技术简介2.简单的阈值技术2.1阈值类型2.2简单阈值技术的实际应用3.自适应阈值算法4.Otsu阈值算法5.Triangle阈值算法6.... 查看详情

opencv-python实战——opencv简介与图像处理基础(万字总结,️建议收藏️)(代码片段)

OpenCV-Python实战(1)——OpenCV简介与图像处理基础(内含大量示例,📕建议收藏📕)OpenCV介绍Python安装OpenCVOpenCV主要模块OpenCV应用场景OpenCV图像处理基础图像基础图像处理中的主要问题图像处理流程像... 查看详情

opencv-python实战(番外篇)——opencv实现图像卡通化(代码片段)

OpenCV-Python实战(番外篇)——OpenCV实现图像卡通化前言图像卡通化完整代码更多卡通化效果展示相关链接前言在博文《OpenCV-Python实战(4)——OpenCV常见图像处理技术(❤️万字长文,含大量示例❤️࿰... 查看详情

opencv-python实战(番外篇)——opencv中绘制模拟时钟显示当前时间(代码片段)

OpenCV-Python实战(番外篇)——OpenCV中绘制模拟时钟显示当前时间前言模拟时钟外观计算时针刻度hours_orig和hours_dest数组使用模拟时钟显示当前时间clock_appearance.py完整代码相关链接前言我们已经在《OpenCV-Python实战(3&#x... 查看详情

opencv-python实战(番外篇)——opencv中利用鼠标事件动态绘制图形(代码片段)

OpenCV-Python实战(番外篇)——OpenCV中利用鼠标事件动态绘制图形使用鼠标事件动态绘制动态绘制图形动态绘制图形和文本相关链接使用鼠标事件动态绘制我们已经在《OpenCV-Python实战(3)——OpenCV中绘制图形与文... 查看详情

opencv-python实战(番外篇)——opencvnumpy和matplotlib直方图比较(代码片段)

OpenCV-Python实战(番外篇)——OpenCV、NumPy和Matplotlib直方图比较前言OpenCV、NumPy和Matplotlib灰度直方图比较OpenCV、NumPy和Matplotlib颜色直方图比较相关链接前言在《OpenCV-Python实战(7)——直方图详解(❤️万字长... 查看详情

opencv-python实战(10)——详解opencv轮廓检测(代码片段)

OpenCV-Python实战(10)——详解OpenCV轮廓检测0.前言1.轮廓介绍2.轮廓检测3.轮廓压缩4.图像矩4.1一些基于矩的对象特征4.2Hu不变矩小结系列链接0.前言在计算机视觉领域,轮廓通常指图像中对象边界的一系列点。因此,... 查看详情

opencv-python实战(11)——opencv轮廓检测相关应用(代码片段)

OpenCV-Python实战(11)——OpenCV轮廓检测相关应用0.前言1.轮廓绘制2.轮廓筛选3.轮廓识别4.轮廓匹配小结系列链接0.前言在计算机视觉领域,轮廓通常指图像中对象边界的一系列点。因此,轮廓通常描述了对象边界的... 查看详情

opencv-python实战(13)——opencv与机器学习的碰撞(代码片段)

OpenCV-Python实战(13)——OpenCV与机器学习的碰撞0.前言1.机器学习简介1.1监督学习1.2无监督学习1.3半监督学习2.K均值(K-Means)聚类2.1K-Means聚类示例3.K最近邻3.1K最近邻示例4.支持向量机4.1支持向量机示例小结系列链接0.前言机... 查看详情

opencv-python实战——直方图均衡化(含大量示例,建议收藏)(代码片段)

OpenCV-Python实战(8)——直方图均衡化(含大量示例,建议收藏)0.前言1.灰度直方图均衡化2.颜色直方图均衡化3.对比度受限的自适应直方图均衡化4.比较CLAHE和直方图均衡化5.直方图的比较小结系列链接0.前言... 查看详情

《nuitka打包实战指南》实战打包opencv-python(代码片段)

实战打包OpenCV-Python打包时解决掉的问题:ModuleNotFoundError:NoModulenamedcv2ImportError:numpy.core.multiarrayfailedtoimport打包示例源码:请看文章末尾版本信息:opencv-python==4.5.1.48numpy==1.23.2Nuitka==0.6.19.1打包系统: Windows1064位打包前我们需要... 查看详情

opencv-python实战(12)——一文详解ar增强现实(代码片段)

OpenCV-Python实战(12)——一文详解AR增强现实0.前言1.增强现实简介2.基于无标记的增强现实2.1特征检测2.2特征匹配2.3利用特征匹配和单应性计算以查找对象3.基于标记的增强现实3.1创建标记和字典3.2检测标记3.3相机校准3.4... 查看详情

opencv-python实战——opencv图像运算(❤️万字长文,含大量示例❤️)(代码片段)

OpenCV-Python实战(5)——OpenCV图像运算(❤️万字长文,含大量示例❤️)0.前言1.饱和运算2.图像加减法与图像混合3.按位运算4.形态变换4.1膨胀运算与腐蚀运算4.2开运算与闭运算4.3形态梯度运算4.4顶帽运算与... 查看详情

opencv-python实战(15)——面部特征点检测详解(仅需5行代码学会3种面部特征点检测方法)(代码片段)

OpenCV-Python实战(15)——面部特征点检测详解(仅需5行代码学会3种面部特征点检测方法)0.前言1.面部特征点简介2.使用OpenCV检测面部特征点3.用dlib检测面部特征点4.使用face_recognition检测面部特征点小结系列链接0.... 查看详情

opencv-python实战——opencv中绘制图形与文本(万字总结,️❤️建议收藏️❤️)(代码片段)

OpenCV-Python实战(3)——OpenCV中绘制图形与文本(万字总结,️📕建议收藏📕)0.前言1.OpenCV绘图基础2.OpenCV绘制图形2.1基本图形的绘制2.1.1直线2.1.2矩形2.1.3圆形2.2高级图形的绘制2.2.1剪裁线2.2.2箭头2.2.3... 查看详情

opencv-python实战——opencv常见图像处理技术(❤️万字长文,含大量示例❤️)(代码片段)

OpenCV-Python实战(4)——OpenCV常见图像处理技术(❤️万字长文,含大量示例❤️)0.前言1.拆分与合并通道2.图像的几何变换2.1缩放图像2.2平移图像2.3旋转图像2.4图像的仿射变换2.5图像的透视变换2.6裁剪图像3.... 查看详情

opencv-python实战——图像与视频文件的处理(两万字详解,️建议收藏️)(代码片段)

OpenCV-Python实战(2)——图像与视频文件的处理(两万字详解,️📕建议收藏📕)0.前言1.图像与视频文件处理基础2.图像的读取与写入2.1在OpenCV中读取图像2.2使用OpenCV写入图像2.3计算机视觉项目处理流... 查看详情