【火爐煉AI】機(jī)器學(xué)習(xí)021-使用K-means進(jìn)行圖片的矢量量化操作

【火爐煉AI】機(jī)器學(xué)習(xí)021-使用K-means進(jìn)行圖片的矢量量化操作

(本文所使用的Python庫和版本號: Python 3.5, Numpy 1.14, scikit-learn 0.19, matplotlib 2.2 )

前一篇文章我們講解了K-means算法的定義方法,并用K-means對數(shù)據(jù)集進(jìn)行了簡單的聚類分析。此處我們講解使用k-means對圖片進(jìn)行矢量量化操作。


1. 矢量量化簡介

矢量量化(Vector Quantization, VQ)是一種非常重要的信號壓縮方法,在圖片處理,語音信號處理等領(lǐng)域占據(jù)十分重要的地位。

矢量量化是一種基于塊編碼規(guī)則的有損數(shù)據(jù)壓縮方法,在圖片壓縮格式JPEG和視頻壓縮格式MPEG-4中都有矢量量化這一步,其基本思想是:將若干個標(biāo)量數(shù)據(jù)組構(gòu)成一個矢量,然后在矢量空間給以整體量化,從而達(dá)到壓縮數(shù)據(jù)的同時但不損失多少信息。

矢量量化實(shí)際上是一種逼近,其核心思想和“四舍五入”基本一樣,就是用一個數(shù)來代替其他一個數(shù)或者一組數(shù)據(jù),比如有很多數(shù)據(jù)(6.235,6.241,6.238,6.238954,6.24205.。。),這些數(shù)據(jù)如果用四舍五入的方式,都可以得到一個數(shù)據(jù)6.24,即用一個數(shù)據(jù)(6.24)來就可以代表很多個數(shù)據(jù)。

了解了這個基本思想,我們可以看下面的一維矢量量化的例子:

一維矢量量化舉例

在這個數(shù)軸上,有很多數(shù)據(jù),我們可以用-3來代表所有小于-2的數(shù)據(jù),用-1代表-2到0之間的數(shù)據(jù),用1代表0到2之間的數(shù)據(jù),用3代表大于2的數(shù)據(jù),故而整個數(shù)軸上的無線多個數(shù)據(jù),都可以用這四個數(shù)據(jù)(-3,-1,1,3)來表示,我們可以對這四個數(shù)進(jìn)行編碼,只需要兩個bit就可以,如(-3=00,-1=01,1=10,3=11),所以這就是1-dimensional, 2-bit VQ,其量化率rate=2bits/dimension.

下面看看稍微復(fù)雜一點(diǎn)的二維矢量量化的例子:

二維矢量量化的例子

由于是二維,故而平面上的任意一個點(diǎn)都可以表示為(x,y)這種坐標(biāo)形式,圖中,我們用藍(lán)色實(shí)線將整個二維平面劃分為16個區(qū)域,故而任意一個數(shù)據(jù)點(diǎn)都會落到這16個區(qū)域的某一個。我們可以用平面上的某些點(diǎn)來代表這個平面區(qū)域,故而得到16個紅點(diǎn),這16個紅點(diǎn)的坐標(biāo)就代表了某一個區(qū)域內(nèi)的所有二維點(diǎn)。

更進(jìn)一步,我們就用4bit二進(jìn)制碼來編碼表示這16個數(shù),故而這個問題是2-dimensional, 4-bit VQ, 其量化率也是rate=2bits/dimension.

此處圖中顯示的紅星,也就是16個代表,被稱為編碼矢量(code vectors),而藍(lán)色邊界定的區(qū)域叫做編碼區(qū)域(encoding regions),所有這些編碼矢量的集合被稱為碼書(code book), 所有編碼區(qū)域的集合稱為空間的劃分(partition of the space).

對于圖像而言,可以認(rèn)為圖像中的每個像素點(diǎn)就是一個數(shù)據(jù),用k-means對這些數(shù)據(jù)進(jìn)行聚類分析,比如將整幅圖像聚為K類,那么會得到K個不同的質(zhì)心(關(guān)于質(zhì)心的理解和直觀感受,可以參考我的上一篇文章【火爐煉AI】機(jī)器學(xué)習(xí)020-使用K-means算法對數(shù)據(jù)進(jìn)行聚類分析),或者說通俗一點(diǎn),可以得到K個不同的數(shù)據(jù)代表,這些數(shù)據(jù)代表就可以代表整幅圖像中的所有點(diǎn)的像素值,故而我們只需要知道這K個數(shù)據(jù)代表就可以了(想想人大代表就明白這個道理了),從而可以極大的減少圖片的存儲空間(比如一張bmp的圖像可能有2-3M,而壓縮成jpg后只有幾百K的大小,當(dāng)然壓縮成jpg的過程還有其他壓縮方式,不僅僅是矢量量化,但大體意思相同),當(dāng)然,這個代表的過程會造成一定的圖像像素失真,失真的程度就是K的個數(shù)了。用圖片可以表示為:

矢量量化壓縮圖像的簡單說明

(以上內(nèi)容部分來源于博客矢量量化(Vector Quantization)


2. 使用K-means對圖像進(jìn)行矢量量化操作

根據(jù)上面第一部分對矢量量化的介紹,我們可以對某一張圖片進(jìn)行矢量量化壓縮,可以從圖片中提取K個像素代表,然后用這些代表來表示一張圖片。具體的代碼為:

from sklearn.cluster import KMeans
# 構(gòu)建一個函數(shù)來完成圖像的矢量量化操作
def image_VQ(image,K_nums): # 貌似很花時間。。
    # 構(gòu)建一個KMeans對象
    kmeans=KMeans(n_clusters=K_nums,n_init=4)
    # 用這個KMeans對象來訓(xùn)練數(shù)據(jù)集,此處的數(shù)據(jù)集就是圖像
    img_data=image.reshape((-1,1))
    kmeans.fit(img_data)
    centroids=kmeans.cluster_centers_.squeeze() # 每一個類別的質(zhì)心
    labels=kmeans.labels_ # 每一個類別的標(biāo)記
    return np.choose(labels,centroids).reshape(image.shape)

上面我們先建立一個函數(shù)來完成圖像的矢量量化壓縮操作,這個操作首先建立一個Kmeans對象,然后用這個KMeans對象來訓(xùn)練圖像數(shù)據(jù),然后提起分類之后的每個類別的質(zhì)心和標(biāo)記,并使用這些質(zhì)心來直接替換原始圖像像素,即可得到壓縮之后的圖像。

為了查看原始圖像和壓縮后圖像,我們將這兩幅圖都繪制到一行,繪制的函數(shù)為:

# 將原圖和壓縮圖都繪制出來,方便對比查看效果
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()

為了使用方便,我們可以直接將壓縮圖像函數(shù)和顯示圖像函數(shù)封裝到一個更高級的函數(shù)中,方便我們直接調(diào)用和運(yùn)行,如下所示:

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)
    
    # 計(jì)算壓縮率
    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)

準(zhǔn)備好了各種操作函數(shù)之后,我們就可以直接調(diào)用compress_plot_img()函數(shù)來壓縮和顯示圖像,下面是采用三種不同的比特位來壓縮得到的圖像,可以對比看看效果。

圖像壓縮率50%的效果對比圖
圖像壓縮率75%的效果對比圖
圖像壓縮率87.5%的效果對比圖

########################小**********結(jié)###############################

1, 對圖像進(jìn)行矢量量化壓縮,其本質(zhì)就是將圖像數(shù)據(jù)劃分為K個不同類比,這種思想和K-means的思想一致,故而對圖像進(jìn)行矢量量化是K-means算法的一個重要應(yīng)用。

2, 通過K-means算法得到圖像的K個類別的質(zhì)心后,就可以用著K個不同質(zhì)心來代替圖像像素,進(jìn)而得到壓縮之后的,有少許失真的圖像。

3, 從上述三幅圖的比較可以看出,圖像壓縮率越大,圖像失真的越厲害,最后的比特位為1時的圖像可以說就是二值化圖,其像素值非0即1,非1即0。

#################################################################


注:本部分代碼已經(jīng)全部上傳到(我的github)上,歡迎下載。

參考資料:

1, Python機(jī)器學(xué)習(xí)經(jīng)典實(shí)例,Prateek Joshi著,陶俊杰,陳小莉譯

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容