(本文所使用的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著,陶俊傑,陳小莉譯