色彩空间类型---OpenCV-Python开发指南(7)

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了色彩空间类型---OpenCV-Python开发指南(7)相关的知识,希望对你有一定的参考价值。

参考技术A

在前面,我们只介绍了三种图像的类型,分别位二值图像、灰度图像以及RGB图像。但我们现在常用的图像肯定是RGB图像,不过它只是色彩空间的一种类型,在实际的图像中,还有许多其他的色彩空间,对于会PS的读者来说肯定不会陌生。

比如GRAY色彩空间(灰度图像),XYZ色彩空间,YCrCb色彩空间,HSV色彩空间,HLS色彩空间,CIEL a b 色彩空间,CIEL u v 色彩空间,Bayer色彩空间等。

每个图像都有其擅长处理的内容,因此我们要掌握这些色彩空间图像的转换,以便后续更方便的处理图像的问题。

GRAY就是我们前面介绍的灰度图像,通常指8位灰度图像,其具有256个灰度级,像素值范围位[0,255]。

RGB转换位GRAY的数学公式如下:

Gray=0.229 R+0.587 G+0.114*B

而图像有GRAY色彩空间转换为RGB色彩空间时,最终所有通道的值都是相同的,其处理方式如下:

R=Gray

G=Gray

B=Gray

XYZ色彩空间是由CIE(International Commission on Illumination)定义的,是一种更便于计算的色彩空间,它不像RGB转换位GRAY,只能单向转换,XYZ色彩空间与RGB转换不会丢失任何值。

将RGB色彩空间转换为XYZ色彩空间,其转换公式为:

将XYZ色彩空间转换为RGB色彩空间,其转换公式为:

人眼视觉系统对颜色的敏感度要低于对亮度的敏感度。在传统的RGB色彩空间内,RGB三原色具有相同的重要性,但是忽略了亮度的信息。所以,才有了YCrCb色彩空间。

在YCrCb色彩空间中,Y代表光源的亮度,色度信息保存在Cr和Cb中,其中,Cr表示红色分量信息,Cb表示蓝色分量信息。

亮度给出了颜色亮或暗的程度信息,该信息可以通过照明中强度成分的加权和来计算。在RGB光源中,绿色分量的影响最大,蓝色分量的影响最小。

从RGB色彩空间转换YCrCb色彩空间的数学公式如下:

Y=0.229 R+0.587 G+0.114*B

Cr=(R-Y)*0.713+delta

Cb=(B-Y)*0.564+delta

其中delta的值为:

从YCrCb色彩空间转RGB数学公式如下:

R=Y+1.403*(Cr-delta)

G=Y-0.714 (Cr-delta)-0.344 (Cb-delta)

B=Y+1.773*(Cb-delta)

RGB是从硬件的角度提出的颜色模型,在与人眼匹配的过程中可能存在一定的差异。而HSV色彩空间是一种面向视觉感知的颜色模型。HSV色彩空间是从心理学和视觉的角度出发,指出人眼色彩知觉主要包含3个要素:色调,饱和度,亮度。

说到这里,相信用过PS的都应该清楚HSV到底能干什么了吧?不过,我们还是介绍一些这3个要素,毕竟本篇博文就是专门将色彩空间理论知识的,不能有空缺。

色调(H):指光的颜色,色调与混合光谱中的主要光波长相关,例如“赤橙黄绿青蓝紫”分别表示不同的色调。如果从波长的角度考虑,不同的波长的光表现为不同的颜色,实际上它们体现的是色调的差异。

饱和度(S):指色彩的深浅层度,相对于纯净度,或一种颜色混合白光的数量。纯谱色是全饱和的,像深红色(红加白)和淡紫色(紫加白)这样的彩色是欠饱和的,饱和度与所加白光的数量成反比。

亮度(V):反映的是人眼感受到的光的明暗程度,该指标与物体的反射度有关。对于色彩来讲,如果在其中掺入的白色越多,则其亮度越高;如果在其掺入的黑色越多,则亮度越低。

在具体的实现上,我们将物理空间的颜色分布在圆周上,不同的角度代表不同的颜色。因此,通过调整色调值,我们就能选取不同的颜色,色调的取值范围为[0,360]。色调取值不同,颜色也不同,具体如下表所示:

饱和度的值为[0,1],饱和度的值为0时,只有灰度,饱和度越大,颜色值越丰富。至于亮度,其取值范围也是[0,1]。

例如,博主现在取色调=0,饱和度=1,亮度=1,就可以提取色彩深红色。

介绍完理论知识,HSV与上面的色彩空间一样,也需要与RGB进行转换,不过,我们这里转换之前,需要先将RGB色彩空间的值转换到[0,1]之间,然后在进行处理。具体处理如下:

V=max(R,G,B) 亮度

这里,H的计算结果可能小于0,如果出现这种情况,则需要对H进一步的处理计算。如下所示:

上述公式计算的结果肯定与前面说的色调,亮度,饱和度的范围一致。至于HSV转RGB,感兴趣的可以参考开发文档。

HLS与HSV色彩空间类似,都具有3要素。只是HLS色彩空间就L与V不同,其中HLS色彩空间的L(光亮度/明度)替换了亮度。

那么什么是光亮度/明度呢?

其实,光亮度/明度是用来控制色彩的明暗变换,它的取值范围同样也是[0,1]。我们在程序中,可以通过光亮度/明度的大小来衡量有多少光线从物体表面反射出来。光亮度/明度对于眼睛感知颜色很重要,因为当一个具有色彩的物体处于光线太强或者太暗的地方时,眼睛是无法准确获取物体颜色的。

说实话,编辑公式有点费劲,感兴趣的自己查询开发文档,后续在python中开发,我们都是使用cv2.cvtColor()进行转换的。使用起来,你只需要了解其到底做什么的,并不需要知道其内部如何实现,但内部实现,就是上面的这些数学公式。

CIEL a b*色彩空间是均匀色彩空间模型,它是面向视觉感知的颜色模型。从视觉感知均匀的角度来讲,人所感知到的两种颜色的区别程度,应该与这两种颜色在色彩空间中的距离成正比。在某个色彩空间中如果人所观察的两种颜色的区别程度,与这两种颜色在该色彩空间中对应的点之间的欧式距离成正比,则称该色彩空间为均匀色彩空间。

CIEL a b 色彩空间中的L分量用于表示像素的亮度,取值范围为[0,100],表示从纯黑到纯白;a 分量表示从红色到绿色的范围,取值范围为[-127,127];b*分量表示从黄色到蓝色的范围,取值范围为[-127.127]。

由于CIEL a b 是在CIE的XYZ色彩空间上发展起来的,所以转换的时候,需要先将RGB转换为XYZ色彩空间,然后在转换为CIEL a b 。具体的数学公式感兴趣的查询开发文档。

CIEL u v 色彩空间同CIEL a b 色彩空间一样,是均匀的颜色模型。CIEL u v*色彩空间与设备无关,适用于显示器显示和根据加色原理进行组合的场合,该模型中比较强调对红色的表示,即对红色的变化比较敏感,但对蓝色的变化不太敏感。

同样的,CIEL u v 色彩空间也需要先将RGB转换为XYZ色彩空间,然后在转换为CIEL u v ,具体公式感兴趣的可以查询开发文档。

Bayer色彩空间被广泛的应用在CCD和CMOS相机中。

色彩空间的理论知识,到这里基本就讲解完成了,感兴趣的可以自己扩展最后几个数学公式。

OpenCV-Python实战(番外篇)——利用 K-Means 聚类进行色彩量化

OpenCV-Python实战(番外篇)——利用 K-Means 聚类进行色彩量化

前言

K-Means 聚类算法的目标是将 n 个样本划分(聚类)为 K 个簇,在博文《OpenCV与机器学习的碰撞》中,我们已经学习利用 OpenCV 提供了 cv2.kmeans() 函数实现 K-Means 聚类算法,该算法通过找到簇的中心并将输入样本分组到簇周围,同时通过简单的示例了解了 K-Means 算法的用法。在本文中,我们将学习如何利用 K-Means 聚类进行色彩量化,以减少图像中颜色数量。

利用 K-Means 聚类进行色彩量化

色彩量化问题可以定义为减少图像中颜色数量的过程。色彩量化对于某些设备显示图像非常关键,这些设备可能由于内存限制等原因只能显示有限颜色,因此,在这些设备上显示色彩通常需要在准确性和减少颜色数量之间进行权衡,在利用 K-Means 聚类进行色彩量化时,权衡两者是通过正确设置 K 参数来进行的。
利用 K-Means 聚类算法来执行色彩量化,簇中心数据由 3 个特征组成,它们对应于图像每个像素的 B、G 和 R 值。因此,关键是将图像转换为数据:

data = np.float32(image).reshape((-1, 3))

为了观察如何权衡准确性和颜色数,我们使用不同 K 值 (3 、 5 、 10 、 20 和 40) 执行聚类过程,以查看生成的图像如何变化,如果我们想要只有 3 种颜色 (K = 3) 的结果图像,需要执行以下操作:

  1. 加载 BGR 图像:
    img = cv2.imread('example.jpg')
    
  2. 使用 color_quantization() 函数执行色彩量化:
    def color_quantization(image, k):
        # 将图像转换为数据
        data = np.float32(image).reshape((-1, 3))
        # 算法终止条件
        criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 20, 1.0)
        # K-Means 聚类
        ret, label, center = cv2.kmeans(data, k, None, criteria, 10, cv2.KMEANS_RANDOM_CENTERS)
        # 簇中心
        center = np.uint8(center)
        # 将具有 k 颜色中心的图像转换为 uint8
        result = center[label.flatten()]
        result = result.reshape(img.shape)
        return result
    color_3 = color_quantization(img, 3)
    

color_quantization() 函数中,关键点是利用 cv2.kmeans() 方法。最后,可以用 k 种颜色来构建图像,用它们对应的中心值替换每个像素值,程序的运行结果如下所示:


完整代码

利用 K-Means 聚类进行色彩量化的完整代码如下所示:

import numpy as np
import cv2
from matplotlib import pyplot as plt


def show_img_with_matplotlib(color_img, title, pos):
    img_RGB = color_img[:, :, ::-1]

    ax = plt.subplot(2, 4, pos)
    plt.imshow(img_RGB)
    plt.title(title, fontsize=8)
    plt.axis('off')

def color_quantization(image, k):
    # 将图像转换为数据
    data = np.float32(image).reshape((-1, 3))
    # 算法终止条件
    criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 20, 1.0)
    # K-Means 聚类
    ret, label, center = cv2.kmeans(data, k, None, criteria, 10, cv2.KMEANS_RANDOM_CENTERS)
    # 簇中心
    center = np.uint8(center)
    # 将具有 k 颜色中心的图像转换为 uint8
    result = center[label.flatten()]
    result = result.reshape(img.shape)
    return result

fig = plt.figure(figsize=(16, 8))
plt.suptitle("Color quantization using K-means clustering algorithm", fontsize=14, fontweight='bold')
# 图片加载
img = cv2.imread('example.png')
show_img_with_matplotlib(img, "original image", 1)
# 使用不同 K 值进行色彩量化
for i in range(7):
    color = color_quantization(img, (i+1) * 10)
    show_img_with_matplotlib(color, "color quantization (k = )".format((i+1) * 10), i+2)

plt.show()

显示色彩量化后的色彩分布

可以扩展以上程序使其显示色彩量化后的色彩分布,该色彩分布显示了分配给每个聚类中心的像素数。只需扩展 color_quantization() 函数已被修改为包含所需功能:

import collections
def color_quantization(image, k):
    # 将图像转换为数据
    data = np.float32(image).reshape((-1, 3))
    # 算法终止条件
    criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 20, 1.0)
    # K-Means 聚类
    ret, label, center = cv2.kmeans(data, k, None, criteria, 10, cv2.KMEANS_RANDOM_CENTERS)
    # 簇中心
    center = np.uint8(center)
    # 将具有 k 颜色中心的图像转换为 uint8
    result = center[label.flatten()]
    result = result.reshape(img.shape)
    # 统计分配给每个聚类中心的像素数
    counter = collections.Counter(label.flatten())
    print(counter)
    # 计算输入图像的总像素数
    total = img.shape[0] * img.shape[1]
    # 为色彩分布图像指定宽度和高度:
    desired_width = img.shape[1]
    
    desired_height = 70
    desired_height_colors = 50
    # 初始化色彩分布图像
    color_distribution = np.ones((desired_height, desired_width, 3), dtype='uint8') * 255
    start = 0

    for key, value in counter.items():
        # 归一化
        value_normalized = value / total * desired_width
        end = start + value_normalized
        # 绘制与当前颜色对应的矩形
        cv2.rectangle(color_distribution, (int(start), 0), (int(end), desired_height_colors), center[key].tolist(), -1)
        start = end

    return np.vstack((color_distribution, result))

上述代码中,使用 collections.Counter() 来统计分配给每个聚类中心的像素数:

counter = collections.Counter(label.flatten())

例如,如果 K = 10,则可以得到如下结果:

Counter(7: 37199, 3: 36302, 0: 29299, 5: 23987, 6: 23895, 1: 20077, 9: 19814, 8: 18427, 4: 16221, 2: 14779)

构建色彩分布图像后,将其与色彩量化后的图像连接在一起:

np.vstack((color_distribution, result))

程序的输出如下所示:

从上图可以看出,使用 K-Means 聚类算法应用色彩量化后改变参数 k (10、20、30、40、50、60 和 70) 的结果,k 值越大产生的图像越逼真。

Note:除了 color_quantization() 函数外,由于其他代码并未修改,因此不再另外给出。

相关链接

OpenCV-Python实战(13)——OpenCV与机器学习的碰撞

以上是关于色彩空间类型---OpenCV-Python开发指南(7)的主要内容,如果未能解决你的问题,请参考以下文章

opencv系列之基于NVIDIA显卡的opencv-python硬解方案

opencv系列之基于NVIDIA显卡的opencv-python硬解方案

OpenCV-Python教程:颜色图(applyColorMap)[只需几行代码生成22种风格各异的彩色图]

OpenCV-Python实战(番外篇)——利用 K-Means 聚类进行色彩量化

OpenCV-Python计算机视觉函数

opencv-python常用知识速查