MIT 선형대수학 강의 31은 Change of basis에 대한 내용이다. 여기에서는 강의 내용 중 소개된 wavelets basis를 이용해서 기저변환의 예제를 살펴보고, 더불어 기저변환을 통한 압축이 어떻게 이루어지는지 개념적인 부분을 확인한다.
입력 이미지는 grayscale 로 구성되어 있다. 실제 값은 아래 주소에서 0에 대한 숫자 이미지가 된다. 이 값을 행렬로 저장하고 image() 함수를 사용해서 실제 0인지 확인한다.
# source: http://scikit-learn.org/stable/auto_examples/classification/plot_digits_classification.html#sphx-glr-auto-examples-classification-plot-digits-classification-py
# input vector in standard basis, number 0
img <- matrix(c(0, 0, 5, 13, 9, 1, 0, 0,
0, 0, 13, 15, 10, 15, 5, 0,
0, 3, 15, 2, 0, 11, 8, 0,
0, 4, 12, 0, 0, 8, 8, 0,
0, 5, 8, 0, 0, 9, 8, 0,
0, 4, 11, 0, 1, 12, 7, 0,
0, 2, 14, 5, 10, 12, 0, 0,
0, 0, 6, 13, 10, 0, 0, 0), nrow=8, byrow = TRUE)
# 행렬 내용을 확인한다.
img
## [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8]
## [1,] 0 0 5 13 9 1 0 0
## [2,] 0 0 13 15 10 15 5 0
## [3,] 0 3 15 2 0 11 8 0
## [4,] 0 4 12 0 0 8 8 0
## [5,] 0 5 8 0 0 9 8 0
## [6,] 0 4 11 0 1 12 7 0
## [7,] 0 2 14 5 10 12 0 0
## [8,] 0 0 6 13 10 0 0 0
# 아래처럼 heatmap을 사용할수 있다. 이때 자동정렬을 막기 위해 Rowv = NA, Colv = NA 옵션을 적용한다.
#heatmap(img, Rowv = NA, Colv = NA,col=paste("gray",1:99,sep=""))
grays = rgb(red = 0:255/255, blue = 0:255/255, green = 0:255/255)
heatmap(img, Rowv=NA, Colv=NA, col=grays, scale = "none")
# 이미지가 회전되어 있어서 행렬의 값을 뒤바꾼다.
rotate <- function(x) t(apply(x, 2, rev))
image(rotate(img))
아래와 같이 Harr wavelets basis를 정의하고, 몇 가지 특징을 살펴본다.
# define wavelets basis: 8x8 in R^8 space
W <- matrix(c(1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, -1, -1, -1, -1,
1, 1, -1, -1, 0, 0, 0, 0,
0, 0, 0, 0, 1, 1, -1, -1,
1, -1, 0, 0, 0, 0, 0, 0,
0, 0, 1, -1, 0, 0, 0, 0,
0, 0, 0, 0, 1, -1, 0, 0,
0, 0, 0, 0, 0, 0, 1, -1), nrow = 8, byrow = FALSE)
W # orthgonal basis vector for haar wavelets
## [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8]
## [1,] 1 1 1 0 1 0 0 0
## [2,] 1 1 1 0 -1 0 0 0
## [3,] 1 1 -1 0 0 1 0 0
## [4,] 1 1 -1 0 0 -1 0 0
## [5,] 1 -1 0 1 0 0 1 0
## [6,] 1 -1 0 1 0 0 -1 0
## [7,] 1 -1 0 -1 0 0 0 1
## [8,] 1 -1 0 -1 0 0 0 -1
p = Wc 라는 공식을 보자.
그럼, 여기서 p와 W를 알고 있고, c를 구하고 싶다. c는 W의 역행렬을 통해서 구할 수 있다.
# find invertible matrix of W - Inverse of W
W_1 <- solve(W) # 역행렬 구하기
#W %*% W_1 # Identity 행렬이 됨을 확인함
# W의 계수를 찾는다.
# p = Wc => c = W-1 p
C = W_1 %*% img
C
## [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8]
## [1,] 0 2.25 10.50 6.0 5.00 8.50 4.50 0
## [2,] 0 -0.50 0.75 1.5 -0.25 0.25 0.75 0
## [3,] 0 -1.75 -2.25 6.5 4.75 -0.75 -2.75 0
## [4,] 0 1.75 -0.25 -4.5 -4.75 2.25 3.75 0
## [5,] 0 0.00 -4.00 -1.0 -0.50 -7.00 -2.50 0
## [6,] 0 -0.50 1.50 1.0 0.00 1.50 0.00 0
## [7,] 0 0.50 -1.50 0.0 -0.50 -1.50 0.50 0
## [8,] 0 1.00 4.00 -4.0 0.00 6.00 0.00 0
여기까지는 무손실 압축 lossless compression 에 해당한다. 만약 위의 계수 행렬 C에서 -2보다 크고 2보다 작은 값들을 0으로 버리면 어떻게 될까? 그리고 그렇게 작은 값들이 버려진 행렬을 C1 이라고 하면, C1을 통해 복구한 이미지는 원래 이미지와 얼마나 다를까?
# cutoff value between -2 and 2
C1 <- C
C1[C1 > -2 & C1 < 2] <- 0 # 작은 값들은 버린다. C1은 이미지에 대한 압축된 정보를 갖는다.
C1
## [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8]
## [1,] 0 2.25 10.50 6.0 5.00 8.50 4.50 0
## [2,] 0 0.00 0.00 0.0 0.00 0.00 0.00 0
## [3,] 0 0.00 -2.25 6.5 4.75 0.00 -2.75 0
## [4,] 0 0.00 0.00 -4.5 -4.75 2.25 3.75 0
## [5,] 0 0.00 -4.00 0.0 0.00 -7.00 -2.50 0
## [6,] 0 0.00 0.00 0.0 0.00 0.00 0.00 0
## [7,] 0 0.00 0.00 0.0 0.00 0.00 0.00 0
## [8,] 0 0.00 4.00 -4.0 0.00 6.00 0.00 0
new_img <- W %*% C1 # C1에서 이미지를 복구한다.
image(rotate(new_img))
위에서 압축된 계수 정보 C1에서 복구한 이미지 new_img 는 원래 이미지 img 와 매우 유사함을 볼 수 있다. 얼마나 압축된걸까?
length(C[C != 0]) # 42
## [1] 42
length(C1[C1 != 0]) # 20
## [1] 20
0 이 아닌 갯수가 42에서 20개로 줄었다. 원본 이미지 대비 약 50% 정도 사이즈가 줄었다.
이상으로 기저변경을 통한 압축 효과를 살펴보았다.