Python+OpenCV实战之利用 K-Means 聚类进行色彩量化
作者:盼小辉丶 发布时间:2021-01-02 09:20:27
前言
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) 的结果图像,需要执行以下操作:
加载 BGR 图像:
img = cv2.imread('example.jpg')
使用 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() 函数外,由于其他代码并未修改,因此不再另外给出。
来源:https://blog.csdn.net/LOVEmy134611/article/details/120383994


猜你喜欢
- Python 正则表达式是什么学习 Python 正则表达式离不开 re 模块,所以本篇博客会配合 re 模块进行编写。re 库是 Pyth
- 假如你目前需要在Microsoft Access数据库中指示不存在数据,可以在“文本”或&l
- 假设有如下目录结构:-- dir0| file1.py| file2.py| dir3| file3.py| dir4| file4.pyd
- 问题:在Jupyter Notebook中使用args传递参数时出现错误:原始代码:args = parser.parse_args()us
- 本文实例讲述了php替换字符串中间字符为省略号的方法。分享给大家供大家参考。具体分析如下:对于一个长字符串,如果你只希望用户看到头尾的部分内
- time库是python中处理时间的标准库。计算机时间的表达:time()、ctime()、gmtime();提供获取系统时间并格式化输出功
- 1,建路由;2,写方法;def fanyi(request): import requests import jso
- 触发器是一种特殊类型的存储过程,它不同于之前的我们介绍的存储过程。触发器主要是通过事件进行触发被自动调用执行的。而存储过程可以通过存储过程的
- 使用python实现单向循环链表,供大家参考,具体内容如下单向循环链表将所有的链接在一起,每一个节点分为数据存储区和链接区,数据区存储数据,
- difflib模块提供的类和方法用来进行序列的差异化比较,它能够比对文件并生成差异结果文本或者html格式的差异化比较页面,如果需要比较目录
- 一、身份验证配置在sqlserver服务端电脑打开SqlServer Managerment Studio管理工具,首先通过Windows身
- 今天要聊聊用 PyTorch 进行 C++ 扩展。在正式开始前,我们需要了解 PyTorch 如何自定义module。这其中,最常见的就是在
- 一、vscode插件介绍在我们演示插值表达式之前,我们先安装这一个VScode给我们提供的插件,它可以将我们书写好的网页通过服务端口的方式进
- 然后给脚本文件运行权限,方法(1)chmod +x ./*.py方法(2)chmod 755 ./*.py (777也无所谓啦)这个命令不去
- 使用MS数据库的朋友就会知道一个问题,C盘的空间很容易被占满了。因为本来系统盘就分区比较小,这是由于数据库在使用过程中会使日志文件不断增加,
- docker-compose.yal文件中: redis: image: redis container_name:
- 链接字典chainMap是逻辑上合并两个字典为一个逻辑单元,合并后的结构实际上是一个列表,只是逻辑上是仍然为一个字典(并未生成新的),对此列
- 现在流行的静态博客/网站生成工具有很多,比如 Jekyll, Pelican, Middleman, Hyde 等等,StaticGen 列
- 他们都可以把 x 变成一个布尔类型的值:>>> x = 123>>> not not xTrue>
- 一、Python pass语句在实际开发中,有时候我们会先搭建起程序的整体逻辑结构,但是暂时不去实现某些细节,而是在这些地方加一些注释,方面