基于R语言SVD的图像压缩方法

图片的基本构成

假设我们有一个灰度图像(128×128,即128×128像素)。我们可以使用矩阵来表示这个图像。如果我们有彩色图像,用R中的readJEPG函数读取图片就可以发现它是由一个三维数组构成(我们常说的三原色:Red, Green, Blue )它有三个矩阵,大小与图像相同, 每个矩阵表示包括RGB色标的颜色值,每个像素由0-255的整数表示。接下来,我们可以通过SVD分解矩阵,再通过消除小的奇异值,我们可以近似矩阵,选择k的值进行矩阵近似,也就是压缩原有矩阵,得到一个保留了原始矩阵大多数信息的压缩矩阵。注意:绘制奇异值图像可能有助于确定重要性显着下降的位置

假设我们选择了k的值,k = 我们希望保留的奇异值的数量,我们可以通过扩展A来生成新的图像矩阵一个使用SVD(第一个k,仅限奇异值)。如果要使用彩色图像,请分别对R,G,B矩阵进行重新组装。
如果你想要得到一个基于原有的彩色图片的灰度图片的话,可以采用: N e w . i m g = 0.299 ∗ r + 0.587 ∗ g + 0.114 ∗ b New.img = 0.299 * r + 0.587 * g + 0.114 * b New.img=0.299r+0.587g+0.114b的公式处理。

如果对SVD(奇异值分解)不了解的话,可以参考我另外一篇有关SVD原理的方面的文章:SVD及PCA的详细原理以及联系

R代码

library(RSpectra)
library(jpeg)
library(animation)

##灰度图片生成
png("new_pic%03d.png")
r <- 0.299
g <- 0.587
b <- 0.114
pic <- readJPEG(sprintf("sam_%03d.jpg", i))
R <- pic[,,1]
G <- pic[,,2]
B <- pic[,,3]
#通过灰度进行图像转化
new_pic <- r*R + g*G + b*B   
#将转化后的灰度图导出为.jpg
writeJPEG(new_pic, sprintf("new_pic%03d.jpg", i))   
dev.off()

##图像压缩
#设置读取路径
setwd("/Users/tyc_219/Desktop/tex")
#读取图片
wyz <- readJPEG("wyz.jpg")
#可以看出图片结构为三维矩阵
str(wyz)
#设置压缩向量
k <- seq(10, 200, 10)
png("Compresspics%03d.png")

compress_jpg <- function(pic, k, plots = TRUE)
{
  #如果压缩向量k里面的最小值小于等于1,报错
  if(min(k) <= 1) stop("The minimum of the vector must larger than 1!")
  #svds()函数在给定的mxn矩阵A的前提下,可以找到其最大的k奇异值和相应的奇异向量
  svd_message <- function(jpg, i)
  {
    r <- svds(jpg[,,1], i)
    g <- svds(jpg[,,2], i)
    b <- svds(jpg[,,3], i)
    return(list(r = r, g = g, b = b))
  }
  
  pic <- svd_message(pic, 200)
  if(plots == TRUE){
    sigma <- pic$r$d
    plot(1:length(sigma), sigma, xlab="i-th sigma", ylab="sigma", main="Singular Values")
    plot(1:length(sigma), cumsum(sigma)/sum(sigma), main="Cumulative Percent of Total Sigmas")
  }
  #返回在不同取值下的压缩后图片,取值越小图片约模糊
  for(m in k)
  {
    r <- pic$r$u[, 1:m] %*% diag(pic$r$d[1:m]) %*% t(pic$r$v[,1:m])
    g <- pic$g$u[, 1:m] %*% diag(pic$g$d[1:m]) %*% t(pic$g$v[,1:m])
    b <- pic$b$u[, 1:m] %*% diag(pic$b$d[1:m]) %*% t(pic$b$v[,1:m])
    pics <- array(0, c(nrow(r), ncol(r), 3))
    pics[,,1] <- r
    pics[,,2] <- g
    pics[,,3] <- b
    writeJPEG(pics, sprintf("picture_svd_%03d.png", m))
  }
}

compress_jpg(wyz, k, plots = TRUE)
dev.off()
#设置生成的压缩图片路径,使用im.convert函数进行GIF图生成
bm.files <- sprintf("picture_svd_%03d.png", k)
im.convert(files = bm.files, output = "Compresspics.gif")

我在这里采用的是吴彦祖的照片:基于R语言SVD的图像压缩方法_第1张图片
其灰度图如下:
基于R语言SVD的图像压缩方法_第2张图片
通过上面的函数可以画出奇异值函数图:
基于R语言SVD的图像压缩方法_第3张图片
生成的GIF图如下,从GIF图很容易看出取奇异值的个数对压缩后图片清晰度的影响:

本人所学有限,如果有错误的地方还望指正,如需转载,请标明出处,谢谢!

你可能感兴趣的:(基于R语言SVD的图像压缩方法)