JPEG양자화 과정 + 압축률 변경에 대한 예제는 다음과 같다.
일부분만 첨부할 것이고, 이해가 어렵다면 전체 코드를 첨부할테니 참고하도록 한다.
void CJpeg::DCT(short * pos, int bWidth, BOOL Flag)
{
// DCT 분만 아니라, DCT 후의 Zigzag 까지... 게다가 Quantization 까지!?//
BYTE Qtb0[64] ={16, 11, 12, 14, 12, 10, 16, 14,
13, 14, 18, 17, 16, 19, 24, 40,
26, 24, 22, 22, 24, 49, 36, 37,
29, 40, 58, 51, 61, 60, 57, 51,
56, 55, 64, 72, 92, 78, 64, 68,
87, 69, 66, 57, 80, 109, 81, 87,
95, 98, 103, 104, 103, 62, 77, 113,
121, 112, 100, 120, 92, 101, 103, 99};
BYTE Qtb1[64] ={17, 18, 18, 24, 21, 24, 47, 26,
26, 47, 99, 66, 56, 66, 99, 99,
99 ,99, 99, 99, 99, 99, 99, 99,
99, 99, 99, 99, 99, 99, 99, 99,
99, 99, 99, 99, 99, 99, 99, 99,
99, 99, 99, 99, 99, 99, 99, 99,
99, 99, 99, 99, 99, 99, 99, 99,
99, 99, 99, 99, 99, 99, 99, 99 };
int i, j;
int x, y, u, v;
float Cu, Cv;
float Sum;
float dct_coeff[8][8] =
{
+1.0f, +1.0f, +1.0f, +1.0f, +1.0f, +1.0f, +1.0f, +1.0f,
+0.9808f, +0.8315f, +0.5556f, +0.1951f, -0.1951f, -0.5556f, -0.8315f, -0.9808f,
+0.9239f, +0.3827f, -0.3827f, -0.9239f, -0.9239f, -0.3827f, +0.3827f, +0.9239f,
+0.8315f, -0.1951f, -0.9808f, -0.5556f, +0.5556f, +0.9808f, +0.1951f, -0.8315f,
+0.7071f, -0.7071f, -0.7071f, +0.7071f, +0.7071f, -0.7071f, -0.7071f, +0.7071f,
+0.5556f, -0.9808f, +0.1951f, +0.8315f, -0.8315f, -0.1951f, +0.9808f, -0.5556f,
+0.3827f, -0.9239f, +0.9239f, -0.3827f, -0.3827f, +0.9239f, -0.9239f, +0.3827f,
+0.1951f, -0.5556f, +0.8315f, -0.9808f, +0.9808f, -0.8315f, +0.5556f, -0.1951f
};
for(v=0; v<8; v++)
{
for(u=0; u<8; u++)
{
Sum = 0;
for(y=0; y<8; y++)
for(x=0; x<8; x++)
Sum = Sum + pos[(int)(y*bWidth+x)] * dct_coeff[u][x] * dct_coeff[v][y];
Cu = 1.; if(u == 0) Cu = 0.7071f;
Cv = 1.; if(v == 0) Cv = 0.7071f;
ZZ[(int)(v*8+u)] = (short)(Cu * Cv * Sum / 4.);
}
}
Zigzag2();
//m_nQfactor변수값을 이용하여 밝기Y와 색차신호(cb,cr) 양자화 테이블 값을 변경하고 있다.
if(Flag) // TRUE : Chrominance, FALSE : Luminance
for(i=0; i<64; i++)
ZZ[i] = ZZ[i] / (m_nQFactor * Qtb1[i]);
else
for(i=0; i<64; i++)
ZZ[i] = ZZ[i] / (m_nQFactor * Qtb0[i]);
for(i=0; i<8; i++)
for(j=0; j<8; j++)
//pos[i*bWidth+j] = (m_nQFactor * ZZ[i*8+j]);
pos[i*bWidth+j] = ZZ[i*8+j];
}
void CJpeg::Zigzag2()
{
int Index[64] =
{0, 1, 5, 6, 14, 15, 27, 28,
2, 4, 7, 13, 16, 26, 29, 42,
3, 8, 12, 17, 25, 30, 41, 43,
9, 11, 18, 24, 31, 40, 44, 53,
10, 19, 23, 32, 39, 45, 52, 54,
20, 22, 33, 38, 46, 51, 55, 60,
21, 34, 37, 47, 50, 56, 59, 61,
35, 36, 48, 49, 57, 58, 62, 63};
short Temp[64];
memcpy(Temp, ZZ, 64 * sizeof(short));
int i, j, idx;
for(i=0; i<8; i++)
for(j=0; j<8; j++)
{
idx = (i<<3)+j;
ZZ[Index[idx]] = Temp[idx];
}
}
양자화율이 높을수록 (양자화 테이블값이 클수록) 압축률은 높지만 영상의 화질은 떨어지게 된다.
압축률 변경 실습
void CJpeg::PutDQT(HFILE hFile)
{
WORD Marker, SegSize;
BYTE c;
// Luminance Quantization Table //
BYTE Qtb0[64] = {16, 11, 12, 14, 12, 10, 16, 14,
13, 14, 18, 17, 16, 19, 24, 40,
26, 24, 22, 22, 24, 49, 36, 37,
29, 40, 58, 51, 61, 60, 57, 51,
56, 55, 64, 72, 92, 78, 64, 68,
87, 69, 66, 57, 80, 109, 81, 87,
95, 98, 103, 104, 103, 62, 77, 113,
121, 112, 100, 120, 92, 101, 103, 99};
// Chrominance Quantization Table //
BYTE Qtb1[64] = {17, 18, 18, 24, 21, 24, 47, 26,
26, 47, 99, 66, 56, 66, 99, 99,
99 ,99, 99, 99, 99, 99, 99, 99,
99, 99, 99, 99, 99, 99, 99, 99,
99, 99, 99, 99, 99, 99, 99, 99,
99, 99, 99, 99, 99, 99, 99, 99,
99, 99, 99, 99, 99, 99, 99, 99,
99, 99, 99, 99, 99, 99, 99, 99 };
for(int i = 0 ; i < 64 ; i++)
{
Qtb0[i]=Qtb0[i]*m_nQFactor;
Qtb1[i]=Qtb1[i]*m_nQFactor;
}
Marker = (0xdb << 8) | 0xff;
_lwrite(hFile, (LPSTR)&Marker, 2); // Marker
SegSize = 67; SegSize = (SegSize << 8) | (SegSize >> 8);
_lwrite(hFile, (LPSTR)&SegSize, 2); // Segment Size
c = 0; _lwrite(hFile, (LPSTR)&c, 1); // PqTq = 0
_lwrite(hFile, (LPSTR)Qtb0, 64); // Q0 ~ Q63
Marker = (0xdb << 8) | 0xff;
_lwrite(hFile, (LPSTR)&Marker, 2); // Marker
SegSize = 67; SegSize = (SegSize << 8) | (SegSize >> 8);
_lwrite(hFile, (LPSTR)&SegSize, 2); // Segment Size
c = 1; _lwrite(hFile, (LPSTR)&c, 1); // PqTq = 1
_lwrite(hFile, (LPSTR)Qtb1, 64); // Q0 ~ Q63
}
밝기값과 색차신호를 위해 변경된 양자화 테이블을 Q_Factor에 반영하여 JPEG파일의 헤더부분에 저장한다.
다음은 화질을 평가하는 지표인 PSNR을 추출해보자.
double CJpeg::GetPSNR()
{
double MSE = 0.0, PSNR = 0.0;
if( m_pSrc == NULL || m_pTarget == NULL ){
return PSNR;
}
int Height = GetHeight();
int Width = GetWidth();
int bWidth = Width;
int bHeight = Height;
if(Width % 8 != 0)
bWidth = (Width/8 + 1)*8; // bWidth 는 8의 배수로 만든 넓이
if(Height % 8 != 0)
bHeight = (Height/8 + 1)*8; // bHeight 는 8의 배수로 만든 높이
int i,j;
int idxY, idxCb, idxCr;
for(i=0; i<Height; i++)
{
for(j=0; j<Width; j++)
{
idxY = i*(bWidth*3)+j*3; // Y
idxCb = idxY + 1; // Cb
idxCr = idxY + 2; // Cr
MSE += pow( m_pSrc[ idxY ] - m_pTarget[ idxY ], (double)2 ); // Y에 대한 MSE 계산
//MSE += pow( m_pSrc[ idxCb ] - m_pTarget[ idxCb ], 2 ); // Cb에 대한 MSE 계산
//MSE += pow( m_pSrc[ idxCr ] - m_pTarget[ idxCr ], 2 ); // Cr에 대한 MSE 계산
}
}
실행결과
양자화 테이블 값을 변경하여 JPEG로 압축하고 PSNR까지 표시해주는 프로그램을 작성했다.
Q_facter = 1인 경우
Q_factor = 15인 경우
'MFC' 카테고리의 다른 글
H.263 (0) | 2013.10.17 |
---|---|
통신에 대한 대략적인 개요 (0) | 2013.10.10 |
웨이블릿 변환 (2) | 2013.06.27 |
중간 시험 예상 문제 (0) | 2013.06.05 |
데이터 메모리 영역, 허프만 트리..를 만들어야 하지만 리스트 만들기 (0) | 2013.05.30 |