查看全课大纲(第 13 / 93 节)
- 1.计算机视觉概述
- 2.计算机视觉应用场景
- 3.困难与挑战
- 4.开源库介绍
- 5.图像显示与存储
- 6.CLAHE
- 7.腐蚀膨胀与开闭运算
- 8.滤波与边缘填充
- 9.各种滤波
- 10.图像金字塔
- 11.傅里叶变换
- 12.短时傅里叶变换与小波变换
- 13.颜色特征与颜色直方图
- 14.几何特征_边缘
- 15.几何特征-角点与斑点
- 16.局部特征-SIFT特征描述子
- 17.局部特征-SURF特征描述子
- 18.局部特征-ORB特征描述子
- 19.其他特征描述子
- 20.传统图像分割技术
- 21.GraphCut
- 22.GrabCut
- 23.人脸检测的传统方法
- 24.行人检测的传统方法HOG+SVM
- 25.行人检测DPM
- 26.人工神经网络的架构
- 27.目标函数与梯度下降
- 28.误差反向传播
- 29.可手算的神经网络
- 30.改进目标函数与克服梯度消散
- 31.神经网络之传统神经网络与深度网络的差异
- 32.各种梯度下降算法
- 33.如何选择合适的梯度下降算法
- 34.避免过适应
- 35.CNN的架构
- 36.CNN的误差反向传播_卷积层
- 37.CNN的误差反向传播_池化层
- 38.分类所用的数据集
- 39.Alexnet详解
- 40.VGG详解
- 41.GoogLeNet_v1
- 42.BatchNormalization
- 43.GoogLeNet_v3v4
- 44.ResNet
- 45.ResNeXt
- 46.CNN设计准则
- 47.图像检索与推土机距离
- 48.基于颜色相似性
- 49.基于形状相似性
- 50.基于纹理相似性
- 51.基于局部特征相似性
- 52.DL的二值哈希编码
- 53.高维空间中检索KDTree
- 54.高维空间中检索LSH
- 55.目标识别任务综述
- 56.RCNN
- 57.SPP-Net
- 58.Fast R-CNN
- 59.Faster R-CNN
- 60.R-CNN系列模型回顾
- 61.R-FCN的设计思路
- 62.R-FCN的实现
- 63.YOLOv1
- 64.YOLOv2_9000
- 65.YOLOv3
- 66.图像分割应用场景与数据集
- 67.上采样的实现方式
- 68.FCN网络架构的由来
- 69.FCN构建细节
- 70.DeepLabv1
- 71.CRF
- 72.DeepLabv2
- 73.DeepLabv3
- 74.特定的医学影像分割任务
- 75.U-Net
- 76.3D U-Net
- 77.V-Net
- 78.DenseNet
- 79.病理切片分析任务及数据集
- 80.病理切片分析
- 81.图说任务及其传统处理方法
- 82.深度语言模型与RNN
- 83.LSTM及其变种
- 84.NIC模型
- 85.SAT模型
- 86.图说的性能指标
- 87.判别模型与生成模型
- 88.KL散度与JS散度
- 89.VAE
- 90.GAN的基本原理
- 91.DCGAN与CGAN
- 92.Wasserstein GAN
- 93.SRGAN
颜色特征与颜色直方图
小象实战讲义 · 基于深度学习的计算机视觉
在计算机视觉中,如何让机器“看懂”一张图片?第一步往往是提取图像的特征。颜色,作为人眼感知图像最直观的属性,自然也成为机器理解图像的首要特征。本节我们将学习如何用数学工具——颜色直方图,来量化、表达和比较图像的颜色特征,这是图像检索、分类等任务的基础。
💡 核心导读
- 颜色特征的地位:颜色是图像最显著的特征之一,尤其在彩色图像中。
- 两种直方图构建方法:量化颜色直方图适用于颜色分布有限的RGB/HSV空间;聚类颜色直方图则适用于颜色分布极广的Lab空间。
- 直方图相似性度量的挑战:简单的L1或欧氏距离无法考虑相邻颜色之间的相似性。
- 解决方案:引入二次式距离或对直方图进行平滑过滤,以更合理地衡量图像间的颜色相似度。
颜色特征:图像的“第一印象”
当我们观察一幅图像时,颜色往往是最先被注意到的属性。蓝天、绿草、红色的苹果——这些颜色信息构成了我们对图像内容最初步的理解。在计算机视觉中,颜色特征正是这种直观感知的数字化表达。它计算简单,对图像的旋转、缩放等几何变换不敏感,因此在许多视觉任务中作为基础特征被广泛应用。
那么,如何用一种简洁、有效的数学形式来描述一幅图像的颜色分布呢?答案就是颜色直方图。
量化颜色直方图
量化颜色直方图是最直观的构建方法。其核心思想是:先将整个颜色空间(如RGB或HSV)划分为若干个小的单元,称为bin。每个bin用一个代表性的颜色(通常是该单元的中心颜色)来指代。然后,遍历图像中的所有像素,判断每个像素的颜色落在哪个bin中,并对该bin的计数器加一。最终,统计每个bin中像素的数量,就形成了颜色直方图。
工作原理与示例
以HSV颜色空间为例,它是一个圆锥体模型。我们可以沿着色相(H)、饱和度(S)、明度(V)三个维度进行均匀划分。例如,将色相分为若干份,饱和度从中心(灰色)到边缘(纯色)分为几层,明度从黑到白也分为几层。这样,整个HSV空间就被划分成了许多小格子。
import numpy as np
import cv2
# 为了演示,我们手动构造一个简单的“图像”,用HSV值表示
# 假设图像只有4个像素,每个像素用(H, S, V)表示,H范围[0,179], S,V范围[0,255]
pixels_hsv = np.array([
[10, 200, 230], # 偏红高饱和
[12, 190, 220], # 偏红高饱和(与第一个很接近)
[100, 210, 150], # 偏蓝(H=100 对应约 200° 色角)
[30, 50, 200] # 偏黄低饱和
], dtype=np.uint8)
# 1. 量化:这里我们简化演示,仅对色相(H)进行粗量化
# 将0-179的色相范围划分为6个bin,每个bin范围30
def quantize_hue(hue_value):
bin_idx = hue_value // 30 # 0,1,2,3,4,5 共6个bin
return bin_idx
# 2. 统计直方图
hist_bins = 6
hist = np.zeros(hist_bins, dtype=int)
for pixel in pixels_hsv:
h_bin = quantize_hue(pixel[0])
hist[h_bin] += 1
print("量化后的色相bin索引(每个像素):", [int(quantize_hue(p[0])) for p in pixels_hsv])
print("颜色直方图(统计每个bin的像素数):", hist)量化后的色相bin索引(每个像素): [0, 0, 3, 1]
颜色直方图(统计每个bin的像素数): [2 1 0 1 0 0]从输出可以看出,前两个偏红的像素(色相10和12)都被划分到了第0个bin(色相0-29度),因此该bin的计数值为2。这就是量化颜色直方图的生成过程。
优势与不足
优势:
- 计算高效:bin是预先定义好的,只需要简单的查找和计数操作,速度很快。
不足:
- 量化问题:如何划分bin(划分的粗细)是个难题。划分太细,直方图会非常稀疏(很多bin是0);划分太粗,又会把视觉上差异明显的颜色混为一谈,丢失细节。
- 稀疏性:对于一张具体的图片,其颜色只占据整个颜色空间的很小一部分,导致直方图中大部分bin的值为0,如上例中的bin 2,4,5。
- 相似性度量缺陷:假设两幅图的颜色直方图几乎一样,只是整体颜色稍微偏了一点点(例如所有像素的色相都增加了5度),使大量像素落入与原来相邻的bin(当bin划分较细、宽度不超过5°时尤其明显)。如果用L1距离或欧氏距离计算,它们会被判定为非常不相似,但这显然不符合人眼的感知。
聚类颜色直方图
当颜色空间本身非常庞大时(例如Lab颜色空间),量化直方图的稀疏性问题会变得极其严重。Lab空间是为了数字化描述人眼视觉感应而设计的,它包含了正常人眼能看到的所有颜色,且与显示设备无关。
- L分量:亮度,范围0(纯黑)到100(纯白)。
- a分量:从品红色(+127)到深绿色(-128)的范围。
- b分量:从黄色(+127)到蓝色(-128)的范围。
对于Lab这样广阔的颜色空间,预先划分出所有可能的bin是不现实的。聚类颜色直方图提供了另一种思路:让数据自己告诉我们应该有哪些bin。
工作原理
- 聚类:将图像中所有像素的颜色值(在Lab空间或其他空间)视为数据点,使用聚类算法(如K-Means)将这些点聚合成若干类(例如K类)。
- 定义bin:每个聚类中心就代表一个bin。
- 统计:统计属于每个聚类的像素数量,形成直方图。
这样做的好处是,直方图的bin(即聚类中心)是自适应于当前图像颜色分布的,避免了在颜色稀疏区域无效地设置大量bin,使得直方图更紧凑、信息密度更高。
# 演示聚类思想。我们使用一个更简单的2D例子来模拟颜色聚类。
import numpy as np
np.random.seed(42) # 固定随机种子以保证结果可复现
# 模拟两类颜色点:红色调(集中在x=2附近)和蓝色调(集中在x=8附近)
red_points = np.random.randn(30, 2) * 0.5 + [2, 5]
blue_points = np.random.randn(20, 2) * 0.5 + [8, 5]
all_points = np.vstack([red_points, blue_points]) # 共50个点,模拟50个像素的颜色
# 简化演示:我们“事先知道”有两类,手动指定两个初始中心
centers = np.array([[1, 5], [9, 5]]) # 初始聚类中心
print("初始聚类中心:\n", centers)
# 执行一轮简单的K-Means分配(仅演示分配步骤,不迭代更新中心)
def assign_points(points, centers):
distances = np.linalg.norm(points[:, np.newaxis, :] - centers[np.newaxis, :, :], axis=2)
labels = np.argmin(distances, axis=1)
return labels
labels = assign_points(all_points, centers)
hist_cluster = np.bincount(labels, minlength=len(centers))
print("像素点聚类标签(前10个):", labels[:10])
print("聚类颜色直方图:", hist_cluster)初始聚类中心:
[[1 5]
[9 5]]
像素点聚类标签(前10个): [0 0 0 0 0 0 0 0 0 0]
聚类颜色直方图: [30 20]输出显示,50个像素点被分配到了两个聚类中,分别有30个和20个点,形成了两个bin的直方图。在实际应用中,我们会使用更严谨的K-Means算法迭代优化中心点。
如何比较颜色直方图?
构建直方图的最终目的是为了比较,例如判断两幅图像的颜色是否相似。最简单的方法是计算两个直方图向量之间的L1距离(曼哈顿距离) 或L2距离(欧氏距离)。
# 假设有两幅图的量化颜色直方图(6个bin)
import numpy as np
hist1 = np.array([20, 5, 0, 3, 0, 0]) # 图像1的直方图
hist2 = np.array([0, 22, 3, 4, 0, 0]) # 图像2的直方图,整体颜色“偏移”了一个bin
# 计算L1和L2距离
l1_dist = np.sum(np.abs(hist1 - hist2))
l2_dist = np.sqrt(np.sum((hist1 - hist2) ** 2))
print("直方图1:", hist1)
print("直方图2:", hist2)
print("L1距离:", l1_dist)
print("L2距离:", l2_dist)直方图1: [20 5 0 3 0 0]
直方图2: [ 0 22 3 4 0 0]
L1距离: 41
L2距离: 26.43860813280457计算出的距离值很大,意味着这两幅图在简单的距离度量下非常不相似。但设想一个场景:图像1主要是深红色,图像2主要是稍浅一点的红色。由于量化误差,深红色落在了bin1,浅红色落在了bin2。在人眼看来,这两幅图颜色是相似的,但直方图距离却给出了相反的判断。
改进方法:考虑颜色相似性
为了解决这个问题,我们需要在计算距离时,考虑不同bin所代表颜色之间的相似度。两种常见方法是:
二次式距离 (Quadratic Form Distance): 它不仅计算直方图对应位置的差异,还通过一个颜色相似度矩阵A,将不同位置(不同颜色)的差异关联起来。公式为: 其中矩阵A的元素 表示第i个bin的颜色与第j个bin的颜色之间的相似度(例如用 定义, 是两个bin代表色在颜色空间中的距离, 为该空间中的最大距离)。相似的颜色之间 值大,即使直方图峰值错开一个bin,也能通过矩阵A的“平滑”作用得到较小的距离。
直方图平滑过滤 (Histogram Smoothing): 在统计直方图之前或之后,对每个像素的“贡献”进行平滑。一个像素不仅对其所属的bin有贡献,也对其相邻(颜色相似)的bin有部分贡献。这相当于在统计直方图时使用了“软分配”,而不是“硬分配”,生成的直方图本身就带有平滑效果,再用常规距离计算就会更合理。
# 伪代码:二次式距离计算思路
1. 计算颜色相似度矩阵A(例如,基于Lab空间颜色距离的指数衰减函数)。
2. 计算直方图差值向量 d = H1 - H2。
3. 计算二次式距离 distance = sqrt( d.T @ A @ d )。
# 伪代码:直方图平滑思路
1. 对于每个像素的颜色c,找到K个最近的颜色bin中心。
2. 根据c到这些bin中心的距离,按一定权重(如高斯权重)将像素计数分配到多个bin中。
3. 生成平滑后的直方图。📝 动手练一练
- 概念辨析:假设你有一张主要在Lab颜色空间分布的风景照片,天空是渐变的蓝色,草地是渐变的绿色。如果你想用一个128维的向量来表达它的颜色特征,你会选择量化颜色直方图还是聚类颜色直方图?为什么?
👉 点击查看参考答案
参考答案: 应该选择聚类颜色直方图。
- 原因:Lab颜色空间非常广阔,包含的颜色数量极多。如果使用量化直方图,为了覆盖所有可能颜色,需要将三维的L、a、b通道都划分得很细,这会导致bin的数量爆炸(远超过128个),而实际图像用到的颜色只是其中很小一部分,生成的128维量化直方图会极度稀疏(大部分是0),无法有效表达特征。
- 聚类直方图的优势:通过聚类,我们可以直接将特征维度设置为128(即聚成128类)。算法会自适应地在图像颜色实际分布的区域(蓝色渐变区和绿色渐变区)找到有代表性的128个颜色中心作为bin。这样生成的128维直方图是稠密的,每个bin都有一定的像素贡献,能更紧凑、更有代表性地表达这张特定图像的颜色分布。
- 手算理解:有两幅单通道灰度图像(像素值0-255),我们将其量化为4个bin:bin1:[0-63], bin2:[64-127], bin3:[128-191], bin4:[192-255]。图像A的直方图为
H_A = [10, 40, 30, 20],图像B的直方图为H_B = [5, 45, 35, 15]。请计算它们之间的L1距离和L2距离。如果图像C的直方图为H_C = [40, 10, 20, 30],你觉得哪两幅图像看起来颜色分布更相似?这反映了简单距离度量的什么局限?
👉 点击查看参考答案
计算过程:
- A与B的L1距离:
|10-5| + |40-45| + |30-35| + |20-15| = 5+5+5+5 = 20 - A与B的L2距离:
sqrt((5)^2 + (5)^2 + (5)^2 + (5)^2) = sqrt(100) = 10 - A与C的L1距离:
|10-40| + |40-10| + |30-20| + |20-30| = 30+30+10+10 = 80 - A与C的L2距离:
sqrt((30)^2 + (30)^2 + (10)^2 + (10)^2) = sqrt(900+900+100+100)= sqrt(2000) ≈ 44.72
分析: 从数值上看,A与B的距离(L1=20, L2=10)远小于A与C的距离(L1=80, L2≈44.72)。因此,简单距离度量认为A和B更相似。
- 观察直方图:A和B的形状非常接近,都是中间高(bin2, bin3像素多),两头低(bin1, bin4像素少),只是整体计数有些许浮动。这对应了两幅图像整体亮度和对比度非常接近。
- A和C的形状则完全相反,A是中间高,C是两头高。
- 人眼感知:如果A是一张正常曝光的照片,B可能是其亮度稍微调整后的版本,两者内容看起来会非常相似。而C可能是一张严重过曝或欠曝、或者负片效果的图片,与A差异巨大。
- 局限的反映:这个例子说明,L1/L2距离能够捕捉直方图形状的差异。对于颜色分布形状相似但整体亮度有偏移的图像,它们能给出“相似”的判断。这正是我们想要的。然而,本节正文中提到的“错开一个bin”的问题,是形状相似但峰值位置有平移的情况,那是简单距离无法很好处理的另一种局限。
本章小结
本节我们深入探讨了图像最基础的特征——颜色特征,及其核心表达工具颜色直方图。我们学习了两种构建直方图的方法:适用于颜色分布有限空间的量化颜色直方图,以及适用于广阔颜色空间的聚类颜色直方图。更重要的是,我们认识到,将直方图用于图像相似性比较时,必须考虑颜色本身的相似性,为此引入了二次式距离和直方图平滑等改进方法。颜色直方图是许多视觉任务的起点,理解其原理与局限,是构建更复杂视觉系统的基石。需要说明的是,手工设计的颜色直方图在今天更多用作快速基线、传统特征拼接和颜色敏感场景的初筛;在图像分类、检索等任务上,端到端卷积网络学到的特征通常更强。
📋 行动清单
- 在脑海中复现量化颜色直方图的构建过程:划分颜色空间 -> 像素归类 -> 统计计数。
- 理解聚类颜色直方图“数据驱动生成bin”的核心思想,并与量化方法进行对比。
- 思考:如果让你设计一个简单的图像颜色检索系统,仅基于颜色直方图,你会如何选择直方图类型和相似度度量方法?
—— 小象教研组
配套资料 · 直接下载
课件、代码、数据集等配套资料全部免费,点击即可直接下载,无需分享或解锁。