图像色彩量化是将图像的颜色数量减少到预设值的过程,常用于创建艺术效果(如卡通化)、压缩图像或适应有限的显示设备。在rgb图像中,每个像素由红、绿、蓝三个通道的值组成,通常每个通道有256个级别(0-255),这意味着一张图像理论上可以有 $256^3$ 种不同的颜色。
最初的量化尝试可能基于简单的分箱(binning)方法,例如:
import numpy as np import matplotlib.pyplot as plt from skimage import io # 假设 R2, G2, B2 是图像的R, G, B通道数据,范围0-255 # quant 是用于存储量化结果的数组 # n 是目标颜色数量的一个因子,例如 n=64 # 示例:创建一个模拟的图像数据 image_path = 'your_image.jpg' # 替换为你的图像路径 try: img = io.imread(image_path) except FileNotFoundError: print(f"警告:找不到图像文件 '{image_path}',使用模拟数据。") img = (np.random.rand(100, 100, 3) * 255).astype(np.uint8) # 创建一个100x100的随机图像 R2, G2, B2 = img[:,:,0], img[:,:,1], img[:,:,2] quant = np.zeros_like(img, dtype=np.uint8) n_bins_per_channel = 256 // (64 // 4) # 假设 n=64,每个通道分配16个bin quant[:,:,0] = np.floor_divide(R2, n_bins_per_channel) * n_bins_per_channel quant[:,:,1] = np.floor_divide(G2, n_bins_per_channel) * n_bins_per_channel quant[:,:,2] = np.floor_divide(G2, n_bins_per_channel) * n_bins_per_channel # plt.imshow(quant) # plt.title("简单分箱量化") # plt.show()
这种方法虽然能减少颜色数量,但其局限性在于无法精确控制最终的颜色总数n,也无法保证选择的颜色是最具代表性的。它只是将每个颜色通道的值均匀地分配到若干个区间,最终的颜色数量是每个通道区间数的乘积,而非我们指定的n。
2. 基于K-means聚类的精确色彩量化要实现精确的n种颜色量化,核心思想是识别图像中最具代表性的n种颜色,然后将每个像素替换为与其最接近的代表色。K-means聚类算法是解决此类问题的理想选择。
2.1 K-means聚类原理K-means算法是一种迭代的聚类方法,其基本步骤如下:
- 初始化:随机选择n个颜色作为初始聚类中心(centroids)。
- 分配:将图像中的每个像素颜色分配到与其最近的聚类中心。
- 更新:重新计算每个聚类的中心,即该聚类中所有像素颜色的平均值。
- 重复:重复步骤2和3,直到聚类中心不再显著变化或达到最大迭代次数。
完成聚类后,图像中的每个像素颜色就被替换为其所属聚类的中心颜色。
2.2 感知色彩空间的重要性在RGB空间中直接进行K-means聚类可能不是最优选择,因为RGB空间是非线性的,颜色之间的欧氏距离不一定能很好地反映人类视觉感知的差异。例如,在RGB空间中,红色和橙色之间的距离可能与蓝色和绿色之间的距离相同,但在人眼看来,它们的感知差异可能不同。
为了获得更符合人类视觉习惯的量化效果,推荐在感知色彩空间(如CIELAB)中进行聚类。CIELAB空间旨在使颜色之间的欧氏距离与人眼感知的差异成正比,从而使聚类结果更自然。
2.3 在受限库下实现K-means尽管sklearn库提供了方便的K-means实现,但在仅限于numpy、matplotlib.pyplot和skimage的环境下,我们可以手动实现K-means算法。

全面的AI聚合平台,一站式访问所有顶级AI模型


K-means算法实现步骤(伪代码/概念):
- 数据准备:将图像从 (height, width, 3) 转换为 (height * width, 3) 的像素列表,每个像素是一个三维向量(R, G, B或L, a, b)。
- 初始化聚类中心:从像素列表中随机选择n个像素作为初始聚类中心。
-
迭代:
- 计算每个像素到所有聚类中心的欧氏距离。
- 将每个像素分配给距离最近的聚类中心。
- 更新每个聚类中心为该聚类中所有像素的平均值。
- 重复直到收敛。
- 图像重建:将每个像素替换为其所属聚类的中心颜色,并将数据重塑回原始图像形状。
由于手动实现K-means涉及较多细节,且在性能上可能不如优化过的库函数,因此在允许使用skimage的情况下,可以考虑更高级的解决方案。
3. 利用skimage.segmentation.slic进行色彩量化skimage.segmentation.slic函数通常用于生成超像素,它在颜色空间和空间位置上进行聚类。然而,通过调整其参数,我们可以使其主要在颜色空间上进行聚类,从而实现精确的色彩量化。
slic函数的核心参数包括:
- n_segments:期望生成的超像素(或聚类)数量,这对应于我们所需的颜色数量n。
- compactness:控制颜色相似性和空间邻近性之间的权重。要实现纯粹的颜色量化,应将此值设置得非常低(例如0.01),以最小化空间因素的影响。
- convert2lab:布尔值,如果为True,则在CIELAB空间中执行聚类,提供更好的感知效果。
- enforce_connectivity:布尔值,如果为False,则不强制超像素的连通性,这有助于更好地分离纯颜色聚类。
以下是如何使用skimage.segmentation.slic进行精确色彩量化的示例:
from skimage import io, segmentation, color import numpy as np import matplotlib.pyplot as plt def quantize_image_slic(image_path, n_colors=64): """ 使用skimage.segmentation.slic对图像进行精确色彩量化。 Args: image_path (str): 输入图像的路径。 n_colors (int): 目标颜色数量。 Returns: numpy.ndarray: 量化后的图像。 """ try: # 读取图像,确保是浮点类型以便处理,并标准化到[0, 1] img_rgb = io.imread(image_path) if img_rgb.dtype == np.uint8: img_rgb = img_rgb / 255.0 except FileNotFoundError: print(f"错误:找不到图像文件 '{image_path}'。") return None except Exception as e: print(f"读取图像时发生错误: {e}") return None # 将图像从RGB转换为CIELAB,以便在感知均匀的颜色空间中进行聚类 # slic内部可以自动转换,但手动转换并传入会更明确 img_lab = color.rgb2lab(img_rgb) # 使用SLIC算法进行超像素分割,实际上是颜色聚类 # n_segments: 目标颜色数量 # compactness: 极低值,最小化空间距离影响,专注于颜色距离 # enforce_connectivity: False,不强制超像素连通性,进一步专注于颜色 # convert2lab: True,确保在LAB空间中处理(即使我们已经手动转换,此参数仍有作用) segments = segmentation.slic( img_lab, n_segments=n_colors, compactness=0.01, # 尽可能小,以优先考虑颜色相似性而非空间距离 enforce_connectivity=False, convert2lab=False, # 因为我们已经手动转换为LAB sigma=0 # 不进行高斯平滑 ) # 计算每个超像素(颜色聚类)的平均颜色 # 这将成为该聚类的代表色 quantized_img_lab = np.zeros_like(img_lab) for i in range(n_colors): mask = (segments == i) if np.any(mask): # 确保该segment存在像素 avg_color_lab = np.mean(img_lab[mask], axis=0) quantized_img_lab[mask] = avg_color_lab # 将量化后的图像从CIELAB转换回RGB quantized_img_rgb = color.lab2rgb(quantized_img_lab) # 将RGB值裁剪到[0, 1]范围并转换为uint8格式 quantized_img_rgb = np.clip(quantized_img_rgb, 0, 1) quantized_img_rgb = (quantized_img_rgb * 255).astype(np.uint8) return quantized_img_rgb # 示例使用 image_path = 'example.jpg' # 替换为你的图像文件路径 n_target_colors = 16 # 目标颜色数量 # 创建一个示例图像文件,如果不存在 try: with open(image_path, 'rb') as f: pass except FileNotFoundError: print(f"创建模拟图像文件 '{image_path}'...") sample_img = (np.random.rand(200, 300, 3) * 255).astype(np.uint8) io.imsave(image_path, sample_img) print("模拟图像创建完成。") quantized_image = quantize_image_slic(image_path, n_target_colors) if quantized_image is not None: plt.figure(figsize=(12, 6)) plt.subplot(1, 2, 1) plt.imshow(io.imread(image_path)) plt.title("原始图像") plt.axis('off') plt.subplot(1, 2, 2) plt.imshow(quantized_image) plt.title(f"量化图像 ({n_target_colors} 种颜色)") plt.axis('off') plt.show()3.2 注意事项
- compactness参数:将其设置为非常小的值(例如0.01或更小)至关重要。如果compactness值过高,slic会更多地考虑像素的空间位置,导致生成的是空间上连通的超像素,而非纯粹基于颜色的聚类。
- enforce_connectivity=False:此参数确保算法不会强制生成的区域在空间上是连通的。对于纯粹的颜色量化,我们不关心像素的物理位置,只关心其颜色值。
- convert2lab=True (或手动转换):在CIELAB颜色空间中进行聚类通常会产生更自然、更符合人眼感知的量化结果。slic函数内部可以处理RGB到LAB的转换,但为了代码清晰和控制,也可以像示例中那样手动转换。
- 图像数据类型:skimage函数通常期望浮点类型的图像数据,范围在[0, 1]之间。因此,在传入图像之前进行适当的归一化是良好的实践。
实现RGB图像的精确色彩量化,特别是当目标是固定数量的n种颜色时,简单的分箱法是不足的。K-means聚类算法提供了一个强大的框架来解决这个问题,通过识别图像中最具代表性的颜色并用它们替换原始像素。在受限于numpy, matplotlib.pyplot, skimage的环境下,skimage.segmentation.slic函数是一个高效且灵活的替代方案。通过精细调整slic的n_segments、compactness、enforce_connectivity和convert2lab参数,我们可以将其配置为主要进行颜色空间的聚类,从而实现高质量且精确的图像色彩量化效果。选择在感知色彩空间(如CIELAB)中进行操作,可以进一步提升量化结果的视觉质量。
以上就是RGB图像精确色彩量化:基于聚类与超像素分割的实现的详细内容,更多请关注知识资源分享宝库其它相关文章!
相关标签: igs numpy matplotlib 数据类型 算法 sklearn
发表评论:
◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。