(本文所使用的Python库和版本号: Python 3.5, Numpy 1.14, scikit-learn 0.19, matplotlib 2.2 )git
前一篇文章咱们讲解了K-means算法的定义方法,并用K-means对数据集进行了简单的聚类分析。此处咱们讲解使用k-means对图片进行矢量量化操做。github
矢量量化(Vector Quantization, VQ)是一种很是重要的信号压缩方法,在图片处理,语音信号处理等领域占据十分重要的地位。算法
矢量量化是一种基于块编码规则的有损数据压缩方法,在图片压缩格式JPEG和视频压缩格式MPEG-4中都有矢量量化这一步,其基本思想是:将若干个标量数据组构成一个矢量,而后在矢量空间给以总体量化,从而达到压缩数据的同时但不损失多少信息。机器学习
矢量量化其实是一种逼近,其核心思想和“四舍五入”基本同样,就是用一个数来代替其余一个数或者一组数据,好比有不少数据(6.235,6.241,6.238,6.238954,6.24205.。。),这些数据若是用四舍五入的方式,均可以获得一个数据6.24,即用一个数据(6.24)来就能够表明不少个数据。函数
了解了这个基本思想,咱们能够看下面的一维矢量量化的例子:post
在这个数轴上,有不少数据,咱们能够用-3来表明全部小于-2的数据,用-1表明-2到0之间的数据,用1表明0到2之间的数据,用3表明大于2的数据,故而整个数轴上的无线多个数据,均可以用这四个数据(-3,-1,1,3)来表示,咱们能够对这四个数进行编码,只须要两个bit就能够,如(-3=00,-1=01,1=10,3=11),因此这就是1-dimensional, 2-bit VQ,其量化率rate=2bits/dimension.学习
下面看看稍微复杂一点的二维矢量量化的例子:ui
因为是二维,故而平面上的任意一个点均可以表示为(x,y)这种坐标形式,图中,咱们用蓝色实线将整个二维平面划分为16个区域,故而任意一个数据点都会落到这16个区域的某一个。咱们能够用平面上的某些点来表明这个平面区域,故而获得16个红点,这16个红点的坐标就表明了某一个区域内的全部二维点。编码
更进一步,咱们就用4bit二进制码来编码表示这16个数,故而这个问题是2-dimensional, 4-bit VQ, 其量化率也是rate=2bits/dimension.spa
此处图中显示的红星,也就是16个表明,被称为编码矢量(code vectors),而蓝色边界定的区域叫作编码区域(encoding regions),全部这些编码矢量的集合被称为码书(code book), 全部编码区域的集合称为空间的划分(partition of the space).
对于图像而言,能够认为图像中的每一个像素点就是一个数据,用k-means对这些数据进行聚类分析,好比将整幅图像聚为K类,那么会获得K个不一样的质心(关于质心的理解和直观感觉,能够参考个人上一篇文章【火炉炼AI】机器学习020-使用K-means算法对数据进行聚类分析),或者说通俗一点,能够获得K个不一样的数据表明,这些数据表明就能够表明整幅图像中的全部点的像素值,故而咱们只须要知道这K个数据表明就能够了(想一想人大表明就明白这个道理了),从而能够极大的减小图片的存储空间(好比一张bmp的图像可能有2-3M,而压缩成jpg后只有几百K的大小,固然压缩成jpg的过程还有其余压缩方式,不只仅是矢量量化,但大致意思相同),固然,这个表明的过程会形成必定的图像像素失真,失真的程度就是K的个数了。用图片能够表示为:
(以上内容部分来源于博客矢量量化(Vector Quantization))
根据上面第一部分对矢量量化的介绍,咱们能够对某一张图片进行矢量量化压缩,能够从图片中提取K个像素表明,而后用这些表明来表示一张图片。具体的代码为:
from sklearn.cluster import KMeans
# 构建一个函数来完成图像的矢量量化操做
def image_VQ(image,K_nums): # 貌似很花时间。。
# 构建一个KMeans对象
kmeans=KMeans(n_clusters=K_nums,n_init=4)
# 用这个KMeans对象来训练数据集,此处的数据集就是图像
img_data=image.reshape((-1,1))
kmeans.fit(img_data)
centroids=kmeans.cluster_centers_.squeeze() # 每个类别的质心
labels=kmeans.labels_ # 每个类别的标记
return np.choose(labels,centroids).reshape(image.shape)
复制代码
上面咱们先创建一个函数来完成图像的矢量量化压缩操做,这个操做首先创建一个Kmeans对象,而后用这个KMeans对象来训练图像数据,而后提起分类以后的每一个类别的质心和标记,并使用这些质心来直接替换原始图像像素,便可获得压缩以后的图像。
为了查看原始图像和压缩后图像,咱们将这两幅图都绘制到一行,绘制的函数为:
# 将原图和压缩图都绘制出来,方便对比查看效果
def plot_imgs(raw_img,VQ_img,compress_rate):
assert raw_img.ndim==2 and VQ_img.ndim==2, "only plot gray scale images"
plt.figure(12,figsize=(25,50))
plt.subplot(121)
plt.imshow(raw_img,cmap='gray')
plt.title('raw_img')
plt.subplot(122)
plt.imshow(VQ_img,cmap='gray')
plt.title('VQ_img compress_rate={:.2f}%'.format(compress_rate))
plt.show()
复制代码
为了使用方便,咱们能够直接将压缩图像函数和显示图像函数封装到一个更高级的函数中,方便咱们直接调用和运行,以下所示:
import cv2
def compress_plot_img(img_path,num_bits):
assert 1<=num_bits<=8, 'num_bits must be between 1 and 8'
K_nums=np.power(2,num_bits)
# 计算压缩率
compression_rate=round(100*(8-num_bits)/8,2)
# print('compression rate is {:.2f}%'.format(compression_rate))
image=cv2.imread(img_path,0) # 读取为灰度图
VQ_img=image_VQ(image,K_nums)
plot_imgs(image,VQ_img,compression_rate)
复制代码
准备好了各类操做函数以后,咱们就能够直接调用compress_plot_img()函数来压缩和显示图像,下面是采用三种不一样的比特位来压缩获得的图像,能够对比看看效果。
########################小**********结###############################
1, 对图像进行矢量量化压缩,其本质就是将图像数据划分为K个不一样类比,这种思想和K-means的思想一致,故而对图像进行矢量量化是K-means算法的一个重要应用。
2, 经过K-means算法获得图像的K个类别的质心后,就能够用着K个不一样质心来代替图像像素,进而获得压缩以后的,有少量失真的图像。
3, 从上述三幅图的比较能够看出,图像压缩率越大,图像失真的越厉害,最后的比特位为1时的图像能够说就是二值化图,其像素值非0即1,非1即0。
#################################################################
注:本部分代码已经所有上传到(个人github)上,欢迎下载。
参考资料:
1, Python机器学习经典实例,Prateek Joshi著,陶俊杰,陈小莉译