# 低秩近似法與影像壓縮 ###### tags: `線性代數` `matlab` ###### 撰寫時間 : 2021/09/01 ## 大綱 我的報告內容為[SVD與資料分析](https://modular-course.science.ncku.edu.tw/course_info/7%20Lecture+Recitation%20-%E5%A5%87%E7%95%B0%E5%80%BC%E5%88%86%E8%A7%A3%E8%88%87%E8%B3%87%E6%96%99%E5%88%86%E6%9E%90.pdf)這門課上課內容的整理與延伸 - 低秩近似法。第一部分是數學理論,這是教授在第三天的上課內容;第二部分Matlab實踐是將第一部分的數學理論轉換為Matlab程式碼,在教授提供原文書程式碼的基礎之上,我另外加上奇異值大小分布作圖,相對誤差計算與作圖,與壓縮RGB影像 - 將R、G、B三色分離圖層個別做SVD分解並轉換為秩數較低的矩陣,最後再將合併R、G、B三色圖層即可得到壓縮後的RGB影像。 ## 數學理論 ### 矩陣的Frobenius norm與Spectral norm 在討論低秩近似法前,我們要找到如何代表兩矩陣"長得很像"的衡量標準?直覺來說就是找兩矩陣的"長度","長度"講為較專業的術語就是範數(norm),norm符合兩個性質,一是除了零矩陣的norm等於0,其他矩陣的norm必定大於0,二是符合三角不等式$\| \mathbf{A} + \mathbf{B} \leq \| \mathbf{A} \| + \| \mathbf{B} \|, \forall \mathbf{A}, \mathbf{B} \in \mathbb{R}^{m \times n}$,norm在定義上百百種,這邊只討論2種norm 1. Frobenius norm $$ \| \mathbf{A} \|_F = \sqrt{ \sum^m_{i = 1} \sum^n_{j = 1}|a_{ij}|^2 } $$ 將矩陣所有的entries平方相加最後開根號。 <br> 經推導,在原矩陣右邊或是左邊乘上正交矩陣,不改變Frobenius norm,因此Frobenius norm即為將$\mathbf{A}$做SVD分解中所有奇異值平方相加開根號。 $$ \| \mathbf{A} \|_F = \| \mathbf{U\Sigma V}^T \|_F = \| \mathbf{\Sigma } \|_F = \sqrt{\sigma_1^2 + \ldots + \sigma_n^2} $$ 2. Spectral norm / two norm $$ \| \mathbf{A} \|_2 = \max_{\mathbf{x} \neq 0} \frac{\| \mathbf{Ax}\|_2}{\| \mathbf{x}\|_2} $$ 線性變換前後向量長度的比例,取其中的最大值。另一種看法,可令$\| \mathbf{x} \| = 1$就是在單位圓上做線性轉換後得到橢圓等形狀,取其離原點最大的長度。 <br> 經推導,Spectral norm為將$\mathbf{A}$做SVD分解中最大的奇異值$\sigma_1$。 $$ \| \mathbf{A} \|_2 = \sigma_1 $$ ### SVD的外積展開式與低秩近似法 SVD分解$\mathbf{A} = \mathbf{U\Sigma V}^T$中$\mathbf{A}$數值只由$\mathbf{U}$前$r$項column vector,$\mathbf{\Sigma}$前$r \times r$的方陣決定,$\mathbf{V}^T$前$r$項row vector所決定。因此若資料以SVD形式儲存,可進一步簡化為**reduced SVD**。 $$ \mathbf{A}_r = \mathbf{U}_r \mathbf{\Sigma}_r \mathbf{V}_r^T\\ \mathbf{A}_{m \times n} = \mathbf{U}_{m \times r} \mathbf{\Sigma}_{r \times r} ({V}^T)_{r \times n} $$ 由第2天計算SVD步驟可知,欲求正交矩陣$\mathbf{U}, \mathbf{V}$,是把單位正交集合(set)擴展到單位正交基底(basis),實際上擴展哪些基底不重要,也不具有唯一性,只是為了要讓$\mathbf{U}, \mathbf{V}$的行向量單位正交而形成正交矩陣而已。 如此就可以定義SVD的前$k$項外積展開式,取前將奇異值一一取出展開 $$ \mathbf{A}_k = \sigma_1 \mathbf{u}_1 \mathbf{v}_1^T + \ldots + \sigma_k \mathbf{u}_k \mathbf{v}_k^T, i \leq k \leq r $$ 其秩數$\mathrm{rank}(\mathbf{A}_k) = k$,根據Eckart-Young Theorem,在同一秩數下$\mathrm{rank}(\mathbf{B}) = k$,最接近原本$\mathbf{A}$矩陣的距離就是前$k$項外積展開式,這就是**低秩近似法(low-rank approximation)最佳化的問題**。 $$ \text{For any } \mathbf{B} \in \mathbb{R}^{m \times n} \text{ with } \mathrm{rank}(\mathbf{B}) = k\\ \text{We have } \| \mathbf{A} - \mathbf{B} \|_F \geq \| \mathbf{A} - \mathbf{A}_k \|_F\\ \text{and } \| \mathbf{A} - \mathbf{B} \|_2 \geq \| \mathbf{A} - \mathbf{A}_k \|_2 $$ 因此在同一秩數$k$下,最靠近$\mathbf{A}$的矩陣就是前$k$項外積展開式$\mathbf{A}_k$。 最後就可計算相對誤差的最小值 $$ \begin{align*} &\frac{\| \mathbf{A} - \mathbf{B} \|_F}{\| \mathbf{A} \|_F} \geq\frac{\| \mathbf{A} - \mathbf{A}_k \|_F}{\| \mathbf{A} \|_F} = \frac{\sqrt{\sigma_{k + 1}^2 + \ldots + \sigma_r^2}}{\sqrt{\sigma_1^2 + \ldots + \sigma_r^2}}\\ &\frac{\| \mathbf{A} - \mathbf{B} \|_2}{\| \mathbf{A} \|_2} \geq\frac{\| \mathbf{A} - \mathbf{A}_k \|_2}{\| \mathbf{A} \|_2} = \frac{\sigma_{k + 1}}{\sigma_1} \end{align*} $$ ## Matlab程式實踐 給定輸入圖片`lycoris_recoil.jpg`。<br> ![](https://raw.githubusercontent.com/HsuChiChen/image_hosting_service/main/2023/05/20230503_221059.jpeg) ```matlab clc, clear, close all %% read file and change data type filename = 'lycoris_recoil.jpg'; imfinfo(filename) % image information rare_data = imread(filename); % data type : uint8 cal_data = double(rare_data); % data type : double size_A = size(cal_data); % size of A imshow(cal_data/255); % imshow(RGB) with RGB in [0,1] title(['original image with ',num2str(size_A(2)),'$\times$',num2str(size_A(1)),' pixels'],'interpreter','latex','FontSize',15); axis equal; % maintain aspect ratio of x,y axis off; % turn off axis ``` ![](https://raw.githubusercontent.com/HsuChiChen/image_hosting_service/main/2023/05/20230503_221111.jpeg)<br> 首先讀取圖檔,並用指令`imfinfo()`查看圖檔資訊為$1920 \times 1080$像素,位元深度(bit depth)為24位元,色型為true color,代表有3條通道(channel)分別為紅、綠、藍,每一條通道的位元深度為8位元。而圖片預設儲存的資料型態為無號(unsigned) 8位元整數(`uint8`),在計算機運算時,將其轉換為雙精度浮點數運算(`double`)。由於指令`imshow`要顯示true color影像,若是資料型態是浮點數則數值限制在$[0,1]$,因此除上$255$,將$[0, 255]$映射至$[0,1]$。 ```matlab %% decompose coloered images into RGB layers cal_data_1 = cal_data(:,:,1); cal_data_2 = cal_data(:,:,2); cal_data_3 = cal_data(:,:,3); subplot(2,2,1); imshow(cal_data/255); title('colored image $\mathbf{A}$','interpreter','latex','FontSize',15); subplot(2,2,2); imshow(cal_data_1,[0,255]); title('red layer of image $\mathbf{R}$','interpreter','latex','FontSize',15); colorbar subplot(2,2,3); imshow(cal_data_2,[0,255]); title('green layer of image $\mathbf{G}$','interpreter','latex','FontSize',15); colorbar subplot(2,2,4); imshow(cal_data_3,[0,255]); title('blue layer of image $\mathbf{B}$','interpreter','latex','FontSize',15); colorbar ``` ![](https://raw.githubusercontent.com/HsuChiChen/image_hosting_service/main/2023/05/20230503_221118.jpeg)<br> 將true color影像的R、G、B通道分解,也就是把3維矩陣降至3個2維矩陣 $$ \mathbf{A} \in \mathbb{R}^{1080 \times 1920 \times 3} \to \mathbf{R}, \mathbf{G}, \mathbf{B} \in \mathbb{R}^{1080 \times 1920} $$ 需要注意灰階影像顯示時,預設數值範圍是$[0,1]$,因此在指令`imshow`後面要給與要對應灰階的範圍更改為$[0,255]$。 ```matlab %% singular value decomposition [U1,S1,V1] = svd(cal_data_1); [U2,S2,V2] = svd(cal_data_2); [U3,S3,V3] = svd(cal_data_3); ``` 分別對矩陣$\mathbf{R, G, B}$做SVD分解為$\mathbf{U}\mathbf{\Sigma} \mathbf{V}^T$的形式。 ```matlab %% plot of sigular value sigular_value_number = min(size(S1)); x_axis = 1:sigular_value_number; sigular_value_1 = zeros(1, length(x_axis)); sigular_value_2 = zeros(1, length(x_axis)); sigular_value_3 = zeros(1, length(x_axis)); for i = 1:sigular_value_number % take diagonal entries sigular_value_1(1,i) = S1(i,i); sigular_value_2(1,i) = S2(i,i); sigular_value_3(1,i) = S3(i,i); end subplot(3,1,1); semilogy(x_axis, sigular_value_1, 'LineWidth',2); title(['sigualr value $\sigma_i$ of $\mathbf{R}$ for i = 1, 2, ...', num2str(sigular_value_number)],'interpreter','latex','FontSize',15); xlabel('i-axis','FontSize',10); ylabel('sigualr value \sigma_i','FontSize',10); grid on subplot(3,1,2); semilogy(x_axis, sigular_value_2, 'LineWidth',2); title(['sigualr value $\sigma_i$ of $\mathbf{G}$ for i = 1, 2, ...', num2str(sigular_value_number)],'interpreter','latex','FontSize',15); xlabel('i-axis','FontSize',10); ylabel('sigualr value \sigma_i','FontSize',10); grid on subplot(3,1,3); semilogy(x_axis, sigular_value_3, 'LineWidth',2); title(['sigualr value $\sigma_i$ of $\mathbf{B}$ for i = 1, 2, ...', num2str(sigular_value_number)],'interpreter','latex','FontSize',15); xlabel('i-axis','FontSize',10); ylabel('sigualr value \sigma_i','FontSize',10); grid on ``` ![](https://raw.githubusercontent.com/HsuChiChen/image_hosting_service/main/2023/05/20230503_221126.jpeg) $$ \mathbf{\Sigma} = \begin{bmatrix} \sigma_1 & \cdots & \mathbf{0} & \mathbf{0} & \cdots & \mathbf{0}\\ \vdots & \ddots & \vdots & \vdots & \ddots & \vdots\\ \mathbf{0} & \cdots & \sigma_{ 1080} & \mathbf{0} & \cdots & \mathbf{0}\\ \end{bmatrix}_{ 1080 \times 1920}, \text{ where } \sigma_1 \geq \sigma_2 \geq \cdots \geq \sigma_{1080} \geq 0 $$ 矩陣$\mathbf{R, G, B}$的奇異值$\sigma_i$數值對index $i$作圖,可以發現以此張圖來說,R、G、B圖層的奇異值$\sigma_i$分布類似;需要注意$\sigma_i$數值是對數$\log$作圖,因此奇異值大小變化量劇烈,代表取足夠前$k$項的SVD的外積展開式,就幾乎可以涵蓋整張圖的所有資訊 $$ \mathbf{A} = \underbrace{\sigma_1 \mathbf{u}_1 \mathbf{v}_1^T + \sigma_2 \mathbf{u}_2 \mathbf{v}_2^T + \ldots + \sigma_k \mathbf{u}_k \mathbf{v}_k^T}_{\text{encode almost image message}} + \ldots + \sigma_{1080}\mathbf{u}_{1080}\mathbf{v}_{1080}^T, k <1080 $$ ```matlab %% minimal relative rate of error subplot(3,1,1); error_1 = sigular_value_1(2:end) / S1(1,1); semilogy(x_axis(1:end-1), error_1, 'LineWidth',2); title('relative rate of error $\frac{\| \mathbf{R} - \mathbf{R}_k \|_2}{\| \mathbf{R} \|_2} = \frac{\sigma_{k + 1}}{\sigma_1}$','interpreter','latex','FontSize',15); xlabel('k-axis','FontSize',10); ylabel('A_k error','FontSize',10); grid on subplot(3,1,2); error_2 = sigular_value_2(2:end) / S2(1,1); semilogy(x_axis(1:end-1), error_2, 'LineWidth',2); title('relative rate of error $\frac{\| \mathbf{G} - \mathbf{G}_k \|_2}{\| \mathbf{G} \|_2} = \frac{\sigma_{k + 1}}{\sigma_1}$','interpreter','latex','FontSize',15); xlabel('k-axis','FontSize',10); ylabel('A_k error','FontSize',10); grid on subplot(3,1,3); error_3 = sigular_value_3(2:end) / S3(1,1); semilogy(x_axis(1:end-1), error_3, 'LineWidth',2); title('relative rate of error $\frac{\| \mathbf{B} - \mathbf{B}_k \|_2}{\| \mathbf{B} \|_2} = \frac{\sigma_{k + 1}}{\sigma_1}$','interpreter','latex','FontSize',15); xlabel('k-axis','FontSize',10); ylabel('A_k error','FontSize',10); grid on ``` ![](https://raw.githubusercontent.com/HsuChiChen/image_hosting_service/main/2023/05/20230503_221134.jpeg)<br> 至於這個$k$項要取多少?根據前面[數學理論](#數學理論)中矩陣之間的"距離"這個概念,在此使用norm的其中一種定義 - 2-norm來求得相對誤差值。 ```matlab %% compressed with factor of k k = 30; % reduced to number of singlular values S_re_1 = zeros(size_A(1),size_A(2)); S_re_2 = zeros(size_A(1),size_A(2)); S_re_3 = zeros(size_A(1),size_A(2)); for ir = 1:k S_re_1(ir,ir) = S1(ir,ir); % compress red layer of image S_re_2(ir,ir) = S2(ir,ir); % compress green layer of image S_re_3(ir,ir) = S3(ir,ir); % compress blue layer of image end A_re_1 = U1*S_re_1*V1'; A_re_2 = U2*S_re_2*V2'; A_re_3 = U3*S_re_3*V3'; ``` 我選擇把R、G、B的相對誤差都限制在$2\%$以內,因此選擇$k = 31$,此時R、G、B誤差為 $$ \begin{align*} \text{relative rate error of red layer} &= \frac{\| \mathbf{R} - \mathbf{R}_k \|_2}{\| \mathbf{R} \|_2} \approx 0.0138466 < 2\%\\ \text{relative rate error of green layer} &= \frac{\| \mathbf{G} - \mathbf{G}_k \|_2}{\| \mathbf{G} \|_2} \approx 0.0173546 < 2\%\\ \text{relative rate error of blue layer} &= \frac{\| \mathbf{B} - \mathbf{B}_k \|_2}{\| \mathbf{B} \|_2} \approx 0.0192512 < 2\% \end{align*} $$ ```matlab %% combine RGB layers into colored images figure; A_re = uint8(zeros(size_A(1), size_A(2), size_A(3))); A_re(:,:,1) = uint8(A_re_1); A_re(:,:,2) = uint8(A_re_2); A_re(:,:,3) = uint8(A_re_3); imshow(A_re); title('compressed colored image $\mathbf{R}_k, \mathbf{G}_k,\mathbf{B}_k \to \mathbf{A}_k$ with low rank approximation','interpreter','latex','FontSize',15); axis equal; axis off; ``` ![](https://raw.githubusercontent.com/HsuChiChen/image_hosting_service/main/2023/05/20230503_221141.jpeg)<br> 最後合併壓縮後的$\mathbf{R, G, B}$矩陣回原來的影像。可以發現原本原本$\mathrm{rank}(\mathbf{A}) = 1080$的矩陣,現在只需要取$k = 31$,使矩陣秩數只要$\mathrm{rank}(\mathbf{A}_k) = 31$,就可以看出原本影像大部分的特徵。 ```matlab %% each RGB layers after low rank approximation subplot(2,2,1); imshow(A_re); title('colored image $\mathbf{A}_k$','interpreter','latex','FontSize',15); subplot(2,2,2); imshow(uint8(A_re_1)); colorbar title('red layer of image $\mathbf{R}_k$','interpreter','latex','FontSize',15); subplot(2,2,3); imshow(uint8(A_re_2)); colorbar title('green layer of image $\mathbf{G}_k$','interpreter','latex','FontSize',15); subplot(2,2,4); imshow(uint8(A_re_3)); colorbar title('blue layer of image $\mathbf{B}_k$','interpreter','latex','FontSize',15); ``` ![](https://raw.githubusercontent.com/HsuChiChen/image_hosting_service/main/2023/05/20230503_221150.jpeg)<br> 把R、G、B壓縮影像印出來,看個別R、G、B圖層壓縮的效果。 ```matlab %% comparison of different rank k sgtitle('SVD application - low rank approximation $\mathbf{A}_k = \mathbf{U}_k\mathbf{\Sigma}_k\mathbf{V}_k^T$','interpreter','latex','FontSize',20); k = [5,10,50,100, 200]; % number of singlular values for i = 1:length(k) S_re_1 = zeros(size_A(1),size_A(2)); S_re_2 = zeros(size_A(1),size_A(2)); S_re_3 = zeros(size_A(1),size_A(2)); for ir = 1:k(i) S_re_1(ir,ir) = S1(ir,ir); % compress red layer of image S_re_2(ir,ir) = S2(ir,ir); % compress green layer of image S_re_3(ir,ir) = S3(ir,ir); % compress blue layer of image end A_re_1 = U1*S_re_1*V1'; A_re_2 = U2*S_re_2*V2'; A_re_3 = U3*S_re_3*V3'; A_re = uint8(zeros(size_A(1), size_A(2), size_A(3))); A_re(:,:,1) = uint8(A_re_1); A_re(:,:,2) = uint8(A_re_2); A_re(:,:,3) = uint8(A_re_3); subplot(2,3,i); imshow(A_re); title(['$\mathrm{rank}(\mathbf{A}_k) =$ ',num2str(k(i))],'interpreter','latex','FontSize',15); end subplot(2,3,6); imshow(cal_data/255); title(['original image with ',num2str(size_A(2)),'$\times$',num2str(size_A(1)),' pixels'],'interpreter','latex','FontSize',15); ``` ![](https://raw.githubusercontent.com/HsuChiChen/image_hosting_service/main/2023/05/20230503_221159.jpeg)<br> 最後寫一個`for loop`,觀察取不同$k$,也就是不同的秩數下所得到的效果,可以發現在$\mathrm{rank}(\mathbf{A}) = 100$,以目前這種顯示比例,跟原圖比較幾乎看不出有什麼區別,幾乎能保留影像大部分的特徵,而使用秩數較低的矩陣同時又能減少大量資料儲存,這就是low rank approximation在影像壓縮的應用與意義。