아마추어 천문학 및 이상한 이야기
별이 빛나는 방
아마추어 천문학 및 이상한 이야기
 
분류 전체보기
아마추어 천문학
이상한 fiction
이상한 세상
이상한 사진
이상한 일상
기록의 습작
사는 이야기
먹었어요
놀았어요
Matlab
스크랩(불펌포함?)
     
«   2008/10   »
      1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31  
 
/106  matlab을 이용한 jpeg 압축 알고리즘 재..
/105  나도 문근영 기하학
/104  비스듬히 던진 공의 궤적 그리기
/103  8월, 9월의 근황
/53  마지막(예고편)
   
미엘리키의 달팽이 사육소
인간성 문답
미엘리키의 달팽이 사육소
인간성 문답
미엘리키의 달팽이 사육소
스텔라리움 0.10.0이 릴리즈되었습니다
 
2008/10 - 1
2008/09 - 3
2008/08 - 9
2008/07 - 4
2008/06 - 1
  

Total 23,029, yesterday 123, today 102
powered by Tatter tools, designed by kokoro studio.

jpeg 압축은 참으로 오묘하다. 처음 jpeg을 배웠을 때 너무나 신기해서 감탄, 또 감탄을 하였으니..
jpeg 압축 과정을 말로써 좀 쉽게 풀이하자면 다음과 같다.

1. 그림을 작게 조각조각 낸다.(이건 기술이라고 말할 수도 없다.)
2. 한 조각을 잘 살핀다. 이것이 어떤 '무늬'들의 합으로 표현될 수 있는지..
3. 그 무늬들에 대한 정보만 모으되, 중요하지 않은 정보는 버린다.
4. 중요한 정보들만 모였으면, 자주 나오는 숫자에 낮은 비트를 할당하여 비트 스트림을 만든다.

1~3까지는 이해하기 쉽다. 4번은 허프만(Huffman) 코딩으로, 아주 중요하지는 않다. jpeg은 사실 3번까지만 해도 jpeg의 본질이라 불릴 수 있다고 본다. 개인적인 생각으로는..

그런데 그 무늬들은 무엇인가? 무늬는 누가 만들었나?

www.cs.cf.ac.uk/Dave/Multimedia/DCT.jpg


아니 이건 갑자기 웬 바둑판이란 말인가.. 요건 2차원 DCT(discrete cosine transform)이란 것이다. 이름 속에 비밀이 다 숨어있다.

2차원 : 1차원이 아니라 2차원이라는 것은 이미지라는 뜻이다.
discrete : 이산이라는 얘기다. 원래 이미지라는 게 픽셀로 이루어졌으니 당연히 이산이다.
cosine : 코사인이다.
transform : 변환한다는 거다.



즉, 이미지를 cosine을 이용해서 변환하는 것. 자세한 공식은 수업시간에 배웠거나, 위키백과를 이용하면 쉽게 찾아볼 수 으므로 더 이상 자세한 설명은 생략한다. 그러나 본질을 이야기하자면, 결국 DCT = DFT 다. 단지, DFT에서 허수부분을 과감히 날려버린 거라고 보면 된다. (공식은 다르다. 성격이 그렇다는 것)


다시 '무늬' 이야기로 되돌아가자면, 저 64개의 무늬중 첫번째 무늬를 보자. 아무 무늬도 없다. 그냥 밋밋하다. 사실 8*8정도 되는 작은 이미지 블록은 저렇게 밋밋한 경우가 태반이다. 따라서 밋밋한 경우를 raw(= bmp, 압축없음)에서처럼,


(1,1) = 0
(1,2) = 0
(1,3) = 0
.
.
(8,8) = 0

이라고 고지식하게 표현할 이유가 없다. 그냥,

"무늬 1번"

이라고 하면 얼마나 절약되겠는가.

잘 이해가 안된다면.. 이렇게 생각해보자. 64명이 중국집에 갔다. 여기 "짜장면이랑, 짜장면이라, 짜장면이랑... 짜장면 주세요"라고 말할 필요가 없어서, "짜장 64그릇이요"라고 말하려고 했더니, 세상에.. 메뉴판에 이렇게 써 있는 거다. <세트메뉴A : 짜장 64그릇> 따라서 짜장의 "짜"자도 말할 필요도 없다. 그냥 "세트메뉴 A요"라고 하면 된다. 만약 다 곱배기로 시키고 싶다(이미지에서는 밝기값intensity를 의미한다).. 그러면 "곱배기*세트메뉴 A 주세요"라고 하면된다.

게다가 중국집에 너무 친절해서 이런 세트메뉴들도 있더라. <세트메뉴B : 짜장 32그릇과 짬뽕 32그릇>, <세트메뉴 C : 짜장 16그릇과 짬뽕 16그릇, 그리고 도 짜장 16그릇, 짬뽕 16 그릇>..
물론 이러한 비유가 아주 적절한 것은 아니다. 실제 dct 변환에서는 메뉴는 모두 짜장이고, 각 짜장의 춘장 양이 다르며, 세트메뉴를 64개 시키니까..(비유가 점점 안드로메다로 가고 있다..)
 
하여튼, DCT는
이미지를 저런 "미리 만들어진 무늬들의 합"으로 표현해줘서, 정보전달량을 팍 줄일 수 있는 여지를 제공하는 것이다. 여지를 제공하다니? 줄여주는 게 아니란 말인가? 그렇다. 64개의 값이 들어오면 64개의 값이 나오므로 줄어드는 건 아니다. 오히려 소수점으로 나오기 때문에 이진수로 표현할 때 비트수가 늘어난다.

matalb에서는 dct가 이미 구현이 되어있어서 dct(변수)하면 된다. 변수가 2차원이면 자동으로 2차원 dct가 된다. 테스트를 해보자.

A = zeros(8,8);
B = dct(A);
C = ones(8,8);
D = dct(B);


B랑 D를 보고 아하~라는 감탄사가 나오지 않으면 반성하길 바란다. 감탄사가 나오지 않았더라도 D의 값들에 소수점이 포함었다는 사실에 주목하자. 원래 이미지가 8bit, 즉 0~255까지의 자연수값을 가졌더라도, 계수는 필연적으로 소수점값을 갖는다. 이를 어째? 해답은 간단하다. 반올림하는 것이다. 아니 그럼 정보가 손실되잖아! 당연하다. 원래 jpeg은 손실 압축이다. 게다가 반올림으로 인한 정보손실은 아주 적다. 이것보다 좀 더 과감한 압축방법이 있나니..


64개의 계수에 대해서 weight(가중)을 주는 것이다. 이것의 원리와 목적은 역시 수업시간에 배웠을테니 넘어가자. 기억이  날 경우를 대비해서 말해주자면, 오른쪽 아래에 있는 복잡한 무늬는 무시하자..라는 생각에서 계수를 0으로 만들어버리는 것이다. 여기에서 상당한 정보손실이 일어난다. 예를 들어 8*8이지만 모든 64개 픽셀값들이 차이가 없는 밋밋한 원본 이미지 블럭은 이 과정을 거치면 정말로 밋밋한 블럭으로 변환되고 만다. (사실 가중치를 먼저 곱해주고, 나중에 반올림을 한다.) 아참, 가중치를 어떻게 곱해주는 지를 안 썼군. 이것은 대체로 약속이 되어있다. 이렇게..


qTable =   [16  11  10  16  24  40  51  61;
            12  12  14  19  26  58  60  55;
            14  13  16  24  40  57  69  56;
            14  17  22  29  51  87  80  62;
            18  22  37  56  68  109  103  77;
            24  35  55  64  81  104  113  92;
            49  64  78  87  103  121  120  101;
            72  92  95  98  112  100  103  99]; % 표준적인 양자화 테이블


 
수에 이 가중치가 곱해지면(그러나 보면 알듯이 사실 나눠진다. 역수를 곱해주는 것임), 8*8 블록 안에서 왼쪽위에서 오른쪽 아래까지 대각선 방향으로 값들이 작아지게 된다. 사실 작아지는 정도가 아니라 중앙부터 오른쪽 아래는 거의 다 0 이 되어버린다. 이제 지그재그 스캔이라는 방법이 쓰인다. 아쉽게도 matlab에는 지그재그 스캔하는 함수가 없다. 따라서 본인이 직접 만들어야 하지만.. 구글링하면 다 나온다. 지그재그 스캔하는 zigzag2dto1dm 파일을 다운 받았다.


그럼 8*8 계수가 64*1 또는 1*64 벡터로 변환이 되고, 눈으로 보기에도 앞쪽에 값들이 확 몰려있는 것을 확인할 수 있다. 몰려있다..는 것에 주목하자. 이제 run length encoding을 할 수 있다. 이것은 뒤에 위치한 0들을 짧게 표기한다. 예를 들어, 0 0 0 0 0 은, 0 5 로 표기한다. [3 1 2 3 0 0 0 1 0 0 0 ....] 이런 벡터가 있다면, [ 3 1 1 1 2 1 3 1 0 2 1 1 0 0 ]으로 표기한다. 맨 뒤의 0 0 은 end를 의미한다. 이것도 다 배웠으리라 생각한다. 단, 64개 계수를 모두 rle하면 안된다. 맨 처음 값은 따로 빼놓고, 63개 값만 rle하자.

맨 처음 값은 dc성분이기 때문이다. 이것들은 특별대우해줘야 한다. 앞 블록과 뒷 블록 전체의 dc값(밝기)은 거의 비슷하기 마련이고, 이것은 차분을 이용하면 정보전달량이 매우 작게 줄어들기 때문이다. 그러나 ac성분값이 비슷하리란 보장은 거의 없기에 특별대우 불가.

다음 코드를 보면 jpeg 지그재그 스캔까지는 끝나게 된다.

 
clear all
close all
clc
qTable =   [16  11  10  16  24  40  51  61;
            12  12  14  19  26  58  60  55;
            14  13  16  24  40  57  69  56;
            14  17  22  29  51  87  80  62;
            18  22  37  56  68  109  103  77;
            24  35  55  64  81  104  113  92;
            49  64  78  87  103  121  120  101;
            72  92  95  98  112  100  103  99]; % 표준적인 양자화 테이블
blockSize = 8; % 블록사이즈 설정
load ori_ % 그레이스케일 원본 이미지읽어옴
X = double(X); % double 형식으로 변환
[rowSize,colSize]=size(X); % 이미지의 세로, 가로 사이즈.둘 다 blockSize의 배수여야 함
rowBlockN = rowSize/blockSize; % 세로로 블럭이 몇 개나 있나
colBlockN = colSize/blockSize; % 가로로는 몇 개나 있나
rowAddr = 1:blockSize:rowSize; % 세로 블럭의 시작 주소들
colAddr = 1:blockSize:colSize; % 가로 블럭의 시작 주소들
for j=1:rowBlockN, % 세로 루프
    for k=1:colBlockN, % 가로 루프
        myBlocks{j,k} = ...
            X(rowAddr(j):rowAddr(j)+7,colAddr(k):colAddr(k)+7); % 주소지정해서 셀에 넣어줌
    end
end
tmpBlock = myBlocks{1,1}; % 한 셀을 임시블록으로 지정
qBlock = round(dct(tmpBlock)./qTable); % 임시블록을 dct하고 양자화테이블로 나눈뒤 반올림
zBlock = zigzag2dto1d(qBlock); % 그것을 지그재그 스캔


코드와 필요함수 파일은 첨부파일에 있다.
   http://starrynight.tistory.com/trackback/106
*1  *2  *3  *4  *5  ... *91