Использование SVD для сжатия изображения в MATLAB
Я новичок в MATLAB, но пытаюсь сделать код сжатия изображений для изображений в оттенках серого.
Вопросы
Как я могу использовать SVD для обрезания низкоценных собственных значений для восстановления сжатого изображения?
Работа/попытки до сих пор
Мой код до сих пор:
B=imread('images1.jpeg');
B=rgb2gray(B);
doubleB=double(B);
%read the image and store it as matrix B, convert the image to a grayscale
photo and convert the matrix to a class 'double' for values 0-255
[U,S,V]=svd(doubleB);
Это позволяет мне успешно разложить матрицу изображений с собственными значениями, хранящимися в переменной S.
Как усечь S (что 167x301, класс double)? Скажем, из 167 собственных значений я хочу взять только 100 лучших (или любой n действительно), как это сделать и восстановить сжатое изображение?
Обновленный код/мысли
Вместо того, чтобы помещать кучу кода в раздел комментариев, это текущий проект, который у меня есть. Я смог успешно создать сжатый образ, вручную изменив N, но я хотел бы сделать еще две вещи:
1- Показать панель изображений для различных сжатий (i/e, запустить цикл для N = 5,10,25 и т.д.)
2- Как-то вычислите разницу (ошибку) между каждым изображением и оригиналом и нарисуйте его.
Я ужасен с пониманием циклов и вывода, но это то, что я пробовал:
B=imread('images1.jpeg');
B=rgb2gray(B);
doubleB=im2double(B);%
%read the image and store it as matrix B, convert the image to a grayscale
%photo and convert the image to a class 'double'
[U,S,V]=svd(doubleB);
C=S;
for N=[5,10,25,50,100]
C(N+1:end,:)=0;
C(:,N+1:end)=0;
D=U*C*V';
%Use singular value decomposition on the image doubleB, create a new matrix
%C (for Compression diagonal) and zero out all entries above N, (which in
%this case is 100). Then construct a new image, D, by using the new
%diagonal matrix C.
imshow(D);
error=C-D;
end
Очевидно, что есть некоторые ошибки, потому что я не получаю несколько изображений или не умею "графа" матрицы ошибок
Ответы
Ответ 1
Чтобы начать, я предполагаю, что вы знаете, что SVD - это действительно не лучший инструмент для декорирования пикселей в одном изображении. Но это хорошая практика.
ОК, поэтому мы знаем, что B = U*S*V'
. И мы знаем, что S диагональна и отсортирована по величине. Таким образом, используя только верхние несколько значений S, вы получите приблизительное изображение. Скажем, C=U*S2*V'
, где S2 - ваш модифицированный S. Размеры U и V не изменились, поэтому на данный момент проще всего обнулить элементы S, которые вы не хотите использовать, и запустить реконструкции. (Самый простой способ сделать это: S2=S; S2(N+1:end, :) = 0; S2(:, N+1:end) = 0;
).
Теперь для части сжатия. U
заполнен, и поэтому V
, поэтому независимо от того, что происходит с S2
, ваш объем данных не изменяется. Но посмотрите, что происходит с U*S2
. (Настройте изображение). Если вы сохранили N особых значений в S2
, то только первые N строк из S2
отличны от нуля. Сжатие! Кроме того, вам все равно придется иметь дело с V
. Вы не можете использовать тот же трюк после того, как вы уже сделали (U*S2)
, так как больше U*S2
отличен от нуля, чем S2
. Как мы можем использовать S2 с обеих сторон? Ну, это диагональ, поэтому используйте D=sqrt(S2)
, а теперь C=U*D*D*V'
. Итак, теперь U*D
имеет только N ненулевых строк, а D*V'
имеет только N отличных от нуля столбцов. Передавайте только эти величины, и вы можете восстановить C, который примерно похож на B.
Ответ 2
Хотя этот вопрос устарел, он очень помог мне понять SVD. Я изменил код, который вы написали в своем вопросе, чтобы он работал.
Я считаю, что вы, возможно, решили проблему, однако только для будущих ссылок для тех, кто посещает эту страницу, я включаю полный код здесь с выходными изображениями и графиком.
Ниже приведен код:
close all
clear all
clc
%reading and converting the image
inImage=imread('fruits.jpg');
inImage=rgb2gray(inImage);
inImageD=double(inImage);
% decomposing the image using singular value decomposition
[U,S,V]=svd(inImageD);
% Using different number of singular values (diagonal of S) to compress and
% reconstruct the image
dispEr = [];
numSVals = [];
for N=5:25:300
% store the singular values in a temporary var
C = S;
% discard the diagonal values not required for compression
C(N+1:end,:)=0;
C(:,N+1:end)=0;
% Construct an Image using the selected singular values
D=U*C*V';
% display and compute error
figure;
buffer = sprintf('Image output using %d singular values', N)
imshow(uint8(D));
title(buffer);
error=sum(sum((inImageD-D).^2));
% store vals for display
dispEr = [dispEr; error];
numSVals = [numSVals; N];
end
% dislay the error graph
figure;
title('Error in compression');
plot(numSVals, dispEr);
grid on
xlabel('Number of Singular Values used');
ylabel('Error between compress and original image');
Применяя это к следующему изображению:
![Original Image]()
Дает следующий результат только с первыми 5 сингулярными значениями,
![First 5 Singular Values]()
с первыми 30 уникальными значениями,
![First 30 Singular Values]()
и первые 55 сингулярных значений,
![First 55 Singular Values]()
Изменение ошибки с увеличением числа сингулярных значений можно увидеть на графике ниже.
![Error graph]()
Здесь вы можете заметить, что график показывает, что использование приблизительно 200 первых сингулярных значений приводит к приблизительно нулевой ошибке.
Ответ 3
Например, здесь изображение 512 x 512 B & W Лена:
![Lena]()
Мы вычисляем СВД Лены. Выбирая сингулярные значения выше 1% от максимального сингулярного значения, мы оставляем только 53 сингулярных значения. Реконструируя Лену с этими сингулярными значениями и соответствующие (левые и правые) сингулярные векторы, получим низкое рандомизированное приближение Лены:
![введите описание изображения здесь]()
Вместо хранения значений 512 * 512 = 262144 (каждый из которых принимает 8 бит), мы можем сохранить 2 x (512 x 53) + 53 = 54325 значений, что приблизительно 20% от исходного размера. Это один пример того, как SVD можно использовать для сжатия изображений с потерями.
Здесь код MATLAB:
% open Lena image and convert from uint8 to double
Lena = double(imread('LenaBW.bmp'));
% perform SVD on Lena
[U,S,V] = svd(Lena);
% extract singular values
singvals = diag(S);
% find out where to truncate the U, S, V matrices
indices = find(singvals >= 0.01 * singvals(1));
% reduce SVD matrices
U_red = U(:,indices);
S_red = S(indices,indices);
V_red = V(:,indices);
% construct low-rank approximation of Lena
Lena_red = U_red * S_red * V_red';
% print results to command window
r = num2str(length(indices));
m = num2str(length(singvals));
disp(['Low-rank approximation used ',r,' of ',m,' singular values']);
% save reduced Lena
imwrite(uint8(Lena_red),'Reduced Lena.bmp');
Ответ 4
беря первое n максимальное число собственных значений, и их соответствующие собственные векторы могут решить вашу проблему. Для PCA исходные данные, умноженные на первые восходящие собственные векторы, построят ваше изображение на n x d, где d представляет число собственных векторов.