基於奇異值分解(SVD)的圖片壓縮實踐

1. 前言

數字圖片在計算機中是以矩陣形式存儲的。所以可以通過矩陣理論和矩陣算法對數字圖像進行分析和處理。本文通過對圖片進行SVD壓縮,對不同的參數下的壓縮效果進行對比。

SVD概念可以參考:《統計學習方法》–奇異值分解(Singular Value Decomposition,SVD)

2. 原理簡介

彩色圖片有3個圖層,RGB(紅、綠、藍)也就是矩陣的一個位置上存儲了3個基色的數值,由3個基色混合成不同的色彩。

通過對3個圖層矩陣,分別進行SVD近似,SVD奇異值是唯一的,可以取前 k 個最大的奇異值進行近似表達,最後再將3個圖層的矩陣數據合併,用較少的數據去表達圖片。

2.1 SVD定義


基於奇異值分解(SVD)的圖片壓縮實踐


基於奇異值分解(SVD)的圖片壓縮實踐

3. 實踐代碼

<code># -*- 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/89401727
import numpy as np
import matplotlib.pyplot as plt
def zip_img_by_svd(img, plotId, rate=0.8):
    zip_img = np.zeros(img.shape)
    u_shape = 0
    sigma_shape = 0
    vT_shape = 0
    for chanel in range(3):  # 3個圖層
        u, sigma, v = np.linalg.svd(img[:, :, chanel])  # numpy svd函數
        sigma_i = 0
        temp = 0
        while (temp / np.sum(sigma)) < rate:  # 選取的奇異值和需要達到設定的權重
            temp += sigma[sigma_i]
            sigma_i += 1
        SigmaMat = 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].shape
        sigma_shape = SigmaMat.shape
        vT_shape = v[0:sigma_i, :].shape
    for 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),數據類型uint8
    plt.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()/<code>
基於奇異值分解(SVD)的圖片壓縮實踐

  • 可以看出在使用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、有的用戶也不會點擊,就避免了傳輸原圖,達到了壓縮的目的,節省流量)

總結難免有不足,大家可以評論區提出來一起討論。另外小編精心準備了2020最新python學習資料,大家可以關注公眾號:芝麻代理。回覆:資料分享。免費領取。


基於奇異值分解(SVD)的圖片壓縮實踐


基於奇異值分解(SVD)的圖片壓縮實踐


基於奇異值分解(SVD)的圖片壓縮實踐


分享到:


相關文章: