基于奇异值分解(SVD)的图片压缩实践
文章目錄
- 1. 前言
- 2. 原理簡介
- 2.1 SVD定義
 
- 3. 實踐代碼
- 4. 參考文獻
 
1. 前言
數字圖片在計算機中是以矩陣形式存儲的。所以可以通過矩陣理論和矩陣算法對數字圖像進行分析和處理。本文通過對圖片進行SVD壓縮,對不同的參數下的壓縮效果進行對比。
SVD概念可以參考:《統計學習方法》–奇異值分解(Singular Value Decomposition,SVD)
2. 原理簡介
彩色圖片有3個圖層,RGB(紅、綠、藍)也就是矩陣的一個位置上存儲了3個基色的數值,由3個基色混合成不同的色彩。
通過對3個圖層矩陣,分別進行SVD近似,SVD奇異值是唯一的,可以取前 k 個最大的奇異值進行近似表達,最后再將3個圖層的矩陣數據合并,用較少的數據去表達圖片。
2.1 SVD定義
Am×n=UΣVTUUT=ImVVT=InΣ=diag(σ1,σ2,...,σp)σ1≥σ2≥...≥σp≥0p=min?(m,n)A_{m \times n} = U \Sigma V^T\\ UU^T=I_m\\ VV^T=I_n\\ \Sigma=diag(\sigma_1,\sigma_2,...,\sigma_p) \\ \sigma_1\ge \sigma_2 \ge...\ge\sigma_p \ge0\\ p=\min(m,n)Am×n?=UΣVTUUT=Im?VVT=In?Σ=diag(σ1?,σ2?,...,σp?)σ1?≥σ2?≥...≥σp?≥0p=min(m,n)
- UΣVTU \Sigma V^TUΣVT 稱為矩陣 AAA 的奇異值分解(SVD),UUU 是 mmm 階正交矩陣, VVV 是 nnn 階正交矩陣,Σ\SigmaΣ 是 m×nm \times nm×n 的對角矩陣
- σi\sigma_iσi? 稱為矩陣 AAA 的奇異值
- UUU 的列向量,左奇異向量
- VVV 的列向量,右奇異向量
 Datam×n≈U[:,0:k]Σ[0:k,0:k]VT[0:k,:]Data_{m\times n} \approx U[ : , 0:k] \Sigma[0:k,0:k]V^T[0:k, :]Datam×n?≈U[:,0:k]Σ[0:k,0:k]VT[0:k,:]
3. 實踐代碼
# -*- coding:utf-8 -*- # @Python Version: 3.7 # @Time: 2020/4/21 23:38 # @Author: Michael Ming # @Website: https://michael.blog.csdn.net/ # @File: 15.svd_pic_compress.py # @Reference: https://blog.csdn.net/weixin_44344462/article/details/89401727import numpy as np import matplotlib.pyplot as pltdef zip_img_by_svd(img, plotId, rate=0.8):zip_img = np.zeros(img.shape)u_shape = 0sigma_shape = 0vT_shape = 0for chanel in range(3): # 3個圖層u, sigma, v = np.linalg.svd(img[:, :, chanel]) # numpy svd函數sigma_i = 0temp = 0while (temp / np.sum(sigma)) < rate: # 選取的奇異值和需要達到設定的權重temp += sigma[sigma_i]sigma_i += 1SigmaMat = np.zeros((sigma_i, sigma_i)) # 選取了sigma_i 最大的奇異值for i in range(sigma_i):SigmaMat[i, i] = sigma[i] # 將奇異值填充到Sigma對角矩陣zip_img[:, :, chanel] = u[:, 0:sigma_i].dot(SigmaMat).dot(v[0:sigma_i, :])# 將分解得到的3個矩陣相乘,得到壓縮后的近似矩陣u_shape = u[:, 0:sigma_i].shapesigma_shape = SigmaMat.shapevT_shape = v[0:sigma_i, :].shapefor i in range(3): # 對三個通道的矩陣數值進行歸一化處理MAX = np.max(zip_img[:, :, i])MIN = np.min(zip_img[:, :, i])zip_img[:, :, i] = (zip_img[:, :, i] - MIN) / (MAX - MIN)zip_img = np.round(zip_img * 255).astype("uint8")# 不乘255圖片是黑的(接近0,0,0),數據類型uint8plt.imsave("zip_svd_img.jpg", zip_img) # 保存壓縮后的圖片zip_rate = (img.size - 3 * (u_shape[0] * u_shape[1] + sigma_shape[0] * sigma_shape[1] + vT_shape[0] * vT_shape[1])) / (zip_img.size)f = plt.subplot(3, 3, plotId)f.imshow(zip_img)f.set_title("SVD壓縮率 %.4f,奇異值數量:%d" % (zip_rate, sigma_i))print("設置的壓縮率:", rate)print("使用的奇異值數量:", sigma_i)print("原始圖片大小:", img.shape)print("壓縮后用到的矩陣大小:3x({}+{}+{})".format(u_shape, sigma_shape, vT_shape))print("壓縮率為:", zip_rate)if __name__ == '__main__':imgfile = "svd_img.jpg"plt.figure(figsize=(12, 12))plt.rcParams['font.sans-serif'] = 'SimHei' # 消除中文亂碼img = plt.imread(imgfile)f1 = plt.subplot(331) # 繪制子圖,3行3列,3*3個子圖,現在畫第1幅f1.imshow(img)f1.set_title("原始圖片")for i in range(8): # 再畫8個子圖rate = (i + 1) / 10.0 # 壓縮率 10% - 80%zip_img_by_svd(img, i + 2, rate)plt.suptitle('圖片SVD效果對比', fontsize=17, y=0.02) # y偏移距離plt.show()- 可以看出在使用128個奇異值的SVD壓縮情況下,就可以得到跟原圖差不多效果的圖片
- 原圖是703x800的尺寸,SVD使用的矩陣 ((703, 128)+(128, 128)+(128, 800))=208768
- 可以少使用的矩陣數據比例為(703*800*3-208768*3)/(703*800*3)= 62.88%
- 可以只用37.12%的數據量去近似表達原始圖片,是不是很酷!!!
- 在網絡傳輸圖片的過程中,終端用戶可能點擊,也可能不點擊,那我都給他們發送SVD后的圖片矩陣數據(減少了當次傳輸數據量),然后在終端進行矩陣運算得到壓縮后的圖片,當用戶點擊圖片后,再進行傳輸原圖片(1、用戶點擊是分散的,可以降低統一發送原圖的網絡擁擠現象;2、有的用戶也不會點擊,就避免了傳輸原圖,達到了壓縮的目的,節省流量)
- 微信收到的圖片、小米手機云相冊的縮略圖等都可能用到類似的技術來節省空間
我是外行,自己想的結論,不對的地方,請大佬指點,感謝!
4. 參考文獻
本文參考了以下兩篇文章,對作者表示感謝!
- 利用SVD進行圖像壓縮(附Python代碼)
- 用SVD壓縮圖像
總結
以上是生活随笔為你收集整理的基于奇异值分解(SVD)的图片压缩实践的全部內容,希望文章能夠幫你解決所遇到的問題。
 
                            
                        - 上一篇: LeetCode 第 29 场双周赛(8
- 下一篇: LeetCode 531. 孤独像素 I
