03、K-means聚类实现步骤与基于K-means聚类的图像压缩(2)

news2025/1/4 7:21:46

03、K-means聚类实现步骤与基于K-means聚类的图像压缩(2)

工程下载:K-means聚类实现步骤与基于K-means聚类的图像压缩

其他:
03、K-means聚类实现步骤与基于K-means聚类的图像压缩(1)
03、K-means聚类实现步骤与基于K-means聚类的图像压缩(2)

K-means聚类的图像压缩

开始学习机器学习啦,已经把吴恩达的课全部刷完了,现在开始熟悉一下复现代码。对这个手写数字实部比较感兴趣,作为入门的素材非常合适。

1、K-means聚类图像压缩基本思路

我的想法是:这副图像存在许多很大一块区域的颜色相近,既然相近,我们就用一种颜色替代一大块区域中的各色。我们可以人为的用8、16、24、32种颜色表示整幅图像的颜色,也即说明聚类的个数为8、16、24、32。

上述说法是不准确的,准确的说法是:这副图像虽然很大,但是其有些部分颜色相近,这些颜色相近的部分不论是否出自同一位置,我们都可以用一种颜色进行替代。我们可以使用8、16、24、32种颜色来代替原来图中的所有颜色。

在使用K-means聚类进行图像压缩时,聚类的对象仅仅是颜色而已,和颜色的所在位置是否相近无关,也就是说这种压缩不改变像素的大小,只改变色彩的鲜艳程度而已。具体来讲,聚类是在三维坐标下进行的,三个坐标轴分别为R G B 的具体数值。

下面简单介绍这种压缩算法的压缩效果。对于一张RGB888的彩色图像,假设其大小为1920 * 1080,那么其存储所需的大小为1920*1080 * 24(因为RGB分别用8位来表示,因此每个像素点有24位来表示其颜色)。

此处有24位来表示颜色,可表示的颜色个数为2^24种,假设此处使用K=16的K-means聚类算法对其进行压缩,则代表压缩后的图像只包含K种颜色。

那么对于每个像素点而言,则需要log2(K)=4位来进行表示,此外还需要K*24的空间来存储对应的24位RGB颜色,因此压缩后的总空间为1920 * 1080 * log2(K)+ K * 24。

对于一张1920 * 1080 的图像,压缩比例和K的对应关系如下所示:
在这里插入图片描述

2、K-means聚类图像压缩底层实现

此处从K-means聚类的底层原理进行实现,不调用库函数:

import numpy as np
from matplotlib import pyplot as plt


# 随机初始化聚类初始优化点
def kMeans_init_centroids(X, K):
    # 随机重新排序样本的索引
    randidx = np.random.permutation(X.shape[0])
    # 取前K个样本作为聚类中心
    centroids = X[randidx[:K]]
    return centroids


def find_closest_centroids(X, centroids):
    # 获取聚类中心的数量,也即K值
    K = centroids.shape[0]

    # 初始化一个数组用于存储每个样本所属的聚类中心的索引
    idx = np.zeros(X.shape[0], dtype=int)
    # 遍历数据集中的每个样本
    for i in range(X.shape[0]):
        # 初始化一个列表用于存储当前样本到每个聚类中心的距离
        distance = []
        # 计算当前样本到每个聚类中心的距离
        for j in range(centroids.shape[0]):
            # 使用欧几里得距离公式计算样本i与聚类中心j之间的距离
            norm_ij = np.linalg.norm(X[i] - centroids[j])
            distance.append(norm_ij)
            # 找出距离列表中的最小值,该最小值对应的索引就是当前样本所属的聚类中心
        idx[i] = np.argmin(distance)
        # 返回每个样本所属的聚类中心的索引数组
    return idx


def compute_centroids(X, idx, K):
    # 获取数据集X的行数m和列数n
    # m表示样本数量,n表示每个样本的特征数量
    m, n = X.shape
    # 初始化一个K x n的零矩阵,用于存储K个聚类中心
    # K表示聚类数量,n表示特征数量
    centroids = np.zeros((K, n))
    # 遍历每个聚类中心
    for k in range(K):
        # 从数据集X中选择属于当前聚类k的所有样本
        # idx是一个长度为m的数组,存储了每个样本所属的聚类中心的索引
        points = X[idx == k]
        # 计算属于当前聚类k的所有样本的平均值,得到聚类中心
        # axis=0表示按列计算平均值
        centroids[k] = np.mean(points, axis=0)
        # 返回计算得到的K个聚类中心
    return centroids


def run_kMeans(X, initial_centroids, max_iters=10):
    # 获取数据集X的行数m和列数n
    # m表示样本数量,n表示每个样本的特征数量
    m, n = X.shape
    # 获取初始聚类中心的数量K
    K = initial_centroids.shape[0]
    # 将初始聚类中心赋值给centroids变量
    centroids = initial_centroids
    # 将初始聚类中心复制给previous_centroids变量,用于后续比较聚类中心是否发生变化
    previous_centroids = centroids
    # 初始化一个长度为m的零数组,用于存储每个样本所属的聚类中心的索引
    idx = np.zeros(m)
    # 开始运行K-means算法,最多迭代max_iters次
    for i in range(max_iters):
        # 输出当前迭代进度
        print("K-Means iteration %d/%d" % (i, max_iters - 1))
        # 调用find_closest_centroids函数,为数据集X中的每个样本找到最近的聚类中心,并返回索引数组
        idx = find_closest_centroids(X, centroids)
        # 调用compute_centroids函数,根据每个样本所属的聚类中心和索引数组,计算新的聚类中心
        centroids = compute_centroids(X, idx, K)
        # 返回最终的聚类中心和每个样本所属的聚类中心的索引
    return centroids, idx

# Load an image of a bird
original_img = plt.imread('K_means_data/bird_small.png')
# Visualizing the image
plt.imshow(original_img)
plt.show()
print("Shape of original_img is:", original_img.shape)

# Divide by 255 so that all values are in the range 0 - 1
# RGB各8位,将其归一化至0-1
original_img = original_img / 255
# Reshape the image into an m x 3 matrix where m = number of pixels
# 数组的内容是图像各个点的颜色,m x 3
X_img = np.reshape(original_img, (original_img.shape[0] * original_img.shape[1], 3))

# K就是要使用几种颜色进行表达
K = 8
max_iters = 10

# Using the function you have implemented above. 初始化的是rgb的数值,因此是包含三个元素的数组
initial_centroids = kMeans_init_centroids(X_img, K)

# Run K-Means - this takes a couple of minutes
centroids, idx = run_kMeans(X_img, initial_centroids, max_iters)

# Represent image in terms of indices
X_recovered = centroids[idx, :]

# Reshape recovered image into proper dimensions
X_recovered = np.reshape(X_recovered, original_img.shape)

# Display original image
fig, ax = plt.subplots(1, 2, figsize=(8, 8))
plt.axis('off')

ax[0].imshow(original_img * 255)
ax[0].set_title('Original')
ax[0].set_axis_off()

# Display compressed image
ax[1].imshow(X_recovered * 255)
ax[1].set_title('Compressed with %d colours' % K)
ax[1].set_axis_off()
plt.show()

3、K-means聚类图像压缩库函数实现

此处直接使用from sklearn.cluster import KMeans来进行K-means聚类,代码更加简洁了:

import matplotlib.pyplot as plt  # plt 用于显示图片
import matplotlib.image as mpimg  # mpimg 用于读取图片
from sklearn.cluster import KMeans
import numpy as np

original_pixel = mpimg.imread('K_means_data/bird_small.png')
pixel = original_pixel.reshape((128 * 128, 3))

kmeans = KMeans(n_clusters=8, random_state=0).fit(pixel)

newPixel = []
for i in kmeans.labels_:
    newPixel.append(list(kmeans.cluster_centers_[i, :]))

newPixel = np.array(newPixel)
newPixel = newPixel.reshape((128, 128, 3))


# Display original image
fig, ax = plt.subplots(1, 2, figsize=(8, 8))
plt.axis('off')
ax[0].imshow(original_pixel)
ax[0].set_title('Original')
ax[0].set_axis_off()

# Display compressed image
ax[1].imshow(newPixel)
ax[1].set_title('Compressed with %d colours' % kmeans.n_clusters)
ax[1].set_axis_off()
plt.show()

4、小结

虽说写了那么多,但是实际上还是没有输出压缩后的图片文件。压缩后的图片的大小是1920 * 1080 * log2(K)的,此外还需要K * 24位来存储颜色表,或许没办法用普通png来表示了?那应该怎么生成文件嘞?
之后一定填坑。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1267698.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

day66

今日回顾内容 web框架 django 路由控制 视图层 web框架 一、什么是web框架 Web框架(Web framework)是一种开发框架,用来支持动态网站、网络应用和网络服务的开发。这大多数的web框架提供了一套开发和部署网站的方式,也为web行…

从0开始学习JavaScript--JavaScript中的解构赋值及使用场景

在现代JavaScript中,解构赋值是一种强大而灵活的语法特性,它允许从数组或对象中提取值并赋给变量。这种语法不仅使代码更简洁,而且提高了可读性。在本篇文章中,将深入探讨JavaScript中解构赋值的基本概念、语法规则以及丰富的使用…

element-plus 使用密码输入框的自定义图标

<el-inputv-model"ruleFormPassword.newPassword"placeholder"请输入新密码":type"showPassword ? text : password":style"{ width: 360px }"><template #suffix><span class"input_icon" click"swit…

JavaScript基础—函数、参数、返回值、作用域、变量、匿名函数、综合案例—转换时间,逻辑中断,转换为Boolean型

版本说明 当前版本号[20231129]。 版本修改说明20231126初版20231129完善部分内容 目录 文章目录 版本说明目录JavaScript 基础 - 第4天笔记函数声明和调用声明&#xff08;定义&#xff09;调用细节补充 参数形参和实参函数默认值 返回值作用域全局作用域局部作用域 变量全…

【论文阅读】基于隐蔽带宽的汽车控制网络鲁棒认证(三)

文章目录 第六章 通过认证帧定时实现VulCAN的非once同步6.1 问题陈述6.2 方法概述6.3 动机和缺点6.3.1 认证帧定时隐蔽通信6.3.2 VulCAN的梵蒂冈后端Nonce同步的应用 6.4 设计与实现6.4.1发送方6.4.2 接收方6.4.3 设计参数配置6.4.4 实现 6.5 安全注意事项6.5.1 系统模型6.5.2攻…

提升认知|为什么比尔盖茨在地上发现100美元也会捡?

哈喽呀&#xff0c;大家好&#xff0c;我是雷工&#xff01; 大概在高中时代&#xff0c;听到过这么一个段子&#xff0c;“说如果地上有100美元&#xff0c;比尔盖茨是不会去捡的&#xff0c;因为他弯腰去捡100美元浪费的时间足够其创造1000美元以上的价值。” 当时听完也觉得…

场景应用丨厦门水环境综合治理监测系统效果和作用

厦门&#xff0c;这座美丽的海滨城市&#xff0c;其水资源的状况与水环境的治理对于城市的生存与发展至关重要。近年来&#xff0c;厦门致力于打造生态宜居的城市环境&#xff0c;对城市生命线——水资源的保护与治理给予了极大的关注。其中&#xff0c;水资源综合治理监测系统…

贝锐向日葵与华为达成合作,启动鸿蒙原生应用开发

2023年11月24日&#xff0c;贝锐与华为携手举办鸿蒙原生应用开发启动仪式。贝锐创始人之一兼首席技术官张小峰先生、贝锐事业部总经理张海英女士共同出席了此次活动&#xff0c;并达成重大合作。贝锐旗下国民级远程控制品牌贝锐向日葵将以原生方式适配鸿蒙系统&#xff0c;成为…

《软件工程原理与实践》复习总结与习题——软件工程

软件生命周期 软件生命周期分为三个时期、八个阶段 软件定义时期&#xff1a; 1&#xff09;问题定义阶段&#xff1a;要解决什么问题 2&#xff09;可行性研究阶段&#xff1a;确定软件开发可行 3&#xff09;需求分析阶段&#xff1a;系统做什么 软件开发时期&#xff1a;…

查理·芒格之死对伯克希尔·哈撒韦公司意味着什么?

来源&#xff1a;猛兽财经 作者&#xff1a;猛兽财经 虽然查理芒格是伯克希尔哈撒韦公司首席执行官沃伦巴菲特的重要合作伙伴&#xff0c;但猛兽财经认为查理芒格的去世并不会对伯克希尔哈撒韦的正常运营产生太大的影响&#xff0c;因为该公司长期以来一直是由巴菲特主导的。 …

YouTube宣布要求披露AI生成的内容并添加标签

不知道大家在逛YouTube的时候有没有刷到过一些画面和人物看起来不太自然的视频。 没错&#xff0c;这些视频里面的画面和人物可能都是由AI生成的。 近日&#xff0c;YouTube 产品管理副总裁在官方博客文章上表示&#xff1a;生成式 AI 有潜力在 YouTube 上激发创造力&#xff…

vue2常见的语法糖

Vue.js 2 提供了一些语法糖&#xff08;syntactic sugar&#xff09;来简化常见的操作。以下是一些 Vue.js 2 中常用的语法糖&#xff1a; v-bind 简写&#xff1a; <!-- 完整语法 --> <a v-bind:href"url">Link</a><!-- 简写 --> <a :hr…

解决:ValueError: must have exactly one of create/read/write/append mode

解决&#xff1a;ValueError: must have exactly one of create/read/write/append mode 文章目录 解决&#xff1a;ValueError: must have exactly one of create/read/write/append mode背景报错问题报错翻译报错位置代码报错原因解决方法今天的分享就到此结束了 背景 在使用…

初学者如何理解​session、cookie、token的区别与联系?

session、cookie、token。 相信学过接口的朋友都特别熟悉了。 但是对我一个刚接触接口测试的小白来说&#xff0c;属实有点分不清楚。 下文就是我通过查阅各种资料总结出来的一点理解&#xff0c;不准确的地方还请各位指正。 &#xff08;文末送洗浴中心流程指南&#xff09…

如何在代码中启动与关闭ROS节点

在ROS开发中&#xff0c;节点的管理是很重要的一部分&#xff0c;其中有一些节点大部分时候用不到&#xff0c;只会在特定情况下被启动&#xff08;比如建图节点&#xff09;同时这些节点在使用完后还需要被关闭&#xff0c;因此我们就需要在程序中对这些节点进行启动与关闭的管…

“GIF转PNG轻松转换,图片批量处理神器,提升你的图像管理效率!“

你是否曾经为转换GIF格式到PNG格式而感到困扰&#xff1f;或者为处理大量图片而感到烦恼&#xff1f;现在&#xff0c;我们为你推荐一款全新的GIF到PNG转换工具&#xff0c;以及一款图片批量处理工具&#xff0c;让你的图像管理工作变得轻松愉快&#xff01; 首先&#xff0c;…

通过分析波形,透彻理解 UART 通信

UART是一种异步全双工串行通信协议&#xff0c;由 Tx 和 Rx 两根数据线组成&#xff0c;因为没有参考时钟信号&#xff0c;所以通信的双方必须约定串口波特率、数据位宽、奇偶校验位、停止位等配置参数&#xff0c;从而按照相同的速率进行通信。 异步通信以一个字符为传输单位…

「媒体邀约」三农,农业类媒体资源有哪些?

传媒如春雨&#xff0c;润物细无声&#xff0c;大家好&#xff0c;我是51媒体网胡老师。 农业在我国国民经济中的地位是基础&#xff0c;农业是国民经济建设和发展的基础产业&#xff0c;因此围绕三农发展有很多的公司和企业&#xff0c;每年全国都有大大小小关于农业的展览&a…

C/C++内存管理(含C++中new和delete的使用)

文章目录 C/C内存管理&#xff08;含C中new和delete的使用&#xff09;1、C/C内存分布2、C语言中动态内存管理方式&#xff1a;malloc/calloc/realloc/free3、C动态内存管理3.1、new/delete操作内置类型3.2、new/delete操作自定义类型 4、operator new与operator delete函数5、…

天眼销:B端销冠的私藏宝藏!

在B端销售的业务场景下&#xff0c;获取客户的联系方式是绕不开的一个话题&#xff0c;并且也有很多销售有自己的经验。 怎么去获取企业客户呢&#xff1f;你肯定想我得找到企业基本的信息还有联系方式&#xff0c;这时候你可能会想到去知名的查查平台。然后你会发现&#xff…