출처 : http://www.cinelove.net/imageprocessing.htm

이미지 프로세싱 스터디 목차

 

0. 들어가기 전에..
     1) 이곳 이미지 프로세싱 스터디에서는 Visual C++을 이용한 영상처리에 대한 부분만을 다루었습니다.
     2) Java를 이용한 영상처리 역시 기본개념은 비슷하리라고 생각하기 때문에 Java코드로 바꾸는데에도
       비슷한 이론을 적용하면 될겁니다. Visual C++을 이용한 자료가 많으므로 일단 시작은 이렇게 했습니
       다.
     3) Java로 컨버팅하시다가 잘 안되는 분 계시면 질문답난을 이용해 주시고 같이 고민해 보도록 합시다.

     아래부분에 항목별로 분류하는 것 부터가 쉬운일이 아니군요.. ㅠ.ㅠ
     분류가 엉성하고 잘못된 부분이 있더라도 양해바랍니다.

1. 영상처리란 무엇인가.
2. 기본적으로 알아두어야 할 내용들
   - 장치독립 비트맵
   - 비트맵의 컬러 모드
   - BMP파일 비트맵 포맷
   - 비트맵 사용시 주의할 사항
3. 이미지 처리를 위한 기본 프로그램
   - 기본 프로그램
4. 기본 용어 정리
   - 표본화/양자화
5. 2진 영상처리
   - 경계값 처리하기
   - 히스토그램에 의한 농담 분포조사
   - 히스토그램의 평활화
   - 콘트라스트(contrast)기법

6. 마스크를 이용한 이미지 처리
   - 가우시안을 이용한 잡음 만들기
   - 평활화 방법을 통한 잡음 제거
   - 미디언 필터를 이용한 방법
   - 이진 영상데이타의 잡음제거
   - 영상을 날카롭게 만들기
   - 경계선 추출하기
   - 엠보싱 효과내기
7. 자바에서 BMP파일 포맷 다루기
   - 자바에서는 기본적으로 BMP파일 포맷을 다루지 않기 때문에 위에서 설명한 내용을 가지고 자바용으로
     테스트 해보기에는 사실 무리가 따릅니다. GIF, JPEG등의 이미지는 자체 지원을 해주기 때문에
     이미지 프로세싱을 하는데는 별 문제가 안될것 같다는 느낌이 잠시 스치긴 하지만 말입니다. ^^;

     여기서는 자바에서 BMP파일을 읽어오고 쓰는 방법에 대해서 다루도록 하겠습니다.
     자료의 출처는 http://java.sun.com의 포럼사이트와.. http://www.javaworld.com에서 자료를 얻었습니다.
   - BMP파일 저장하기
   - BMP파일 불러오기
8. 영상 데이타 압축 알고리즘 - 1
   - Discrete Fourier Transform (DFT:이산푸리에변환)
   - Fast Fourier Transform (FFT:고속푸리에변환)
   - 2차원 푸리에 변환 프로그래밍하기
9. 영상 데이타 압축 알고리즘 - 2 [1page][2page][3page]
   (1 page 내용)
   - 가역 부호화와 비가역 부호화의 의미
   - 이진 영상 부호화(run length coding)
   - 예측 부호화(DPCM)
   - 가변 길이 부호화(Huffman Coding)
   (2 page 내용)
   - JPEG이란?
   - 다른기술과 차별화 되는 JPEG의 압축기술
   - JPEG의 압축 방법
   - Baseline 압축에 대해 좀더 자세히!!
   - JPEG의 실제 압축과 복원과정 알아보기
   - 확장 JPEG에 대해서..
   (3 page 내용)
   - JPEG의 파일구조
   - JPEG의 전체구조
   - JPEG의 마커코드와 구조들
 

스터디 참고 자료
   - Visual C++실용 영상 신호처리(이문호저)
   - 학교 수업 교재
   - 디지털 영상처리의 구현(장동혁저)
   - Visual C++ 6(김용성저)
   - Visual C++ Programming Bible(이상엽저)
영상처리 관련 사이트 소개
   - http://java2u.wo.to
   - http://www.javaworld.com
   - http://vision.inchon.ac.kr/
 

기본적으로 알아두어야 할 내용들

[목차로가기]

순서..............................
   - 장치독립 비트맵
   - 비트맵의 컬러 모드
   - BMP파일 비트맵 포맷
   - 비트맵 사용시 주의할 사항

장치독립 비트맵

   이미지 프로세싱은 비트맵을 조작하는 것이므로 비트맵을 조작하기 위한 사전 지식이 필요하다.

   DDB(Device Dependent Bitmap)
     윈도우즈 제어판의 디스플레이 등록정보에 설정되어 있는 화면의 설정에 종속적이라는 의미임.
     현재 화면이 256컬러로 설정이 되어 있으면 DDB도 픽셀당 256컬러로 표현이 되고, 트루컬러나
     하이컬러로 설정이 되어 있으면 DDB도 트루컬러나 하이컬러로 설정이 되는 것을 의미함.

    

   DIB(Device Independent Bitmap)
     현재 시스템의 디스플레이 정보에 무관하게 나름대로의 색상을 가지고 있는 비트맵을 DIB라고 함.
     일반적인 파일로 저장되어 있는 비트맵들이 이에 해당한다고 생각하면됨.

     비트맵이미지 파일 형태(GIF, JPEG, BMP, MAC, PCD, PCT, TGA 등등...)

 

비트맵의 컬러 모드

   트루(True) 컬러 이미지
     이미지의 각 픽셀을 빨강(R), 녹색(G), 파랑(B) 각각 1바이트씩의 조합인 3바이트로 표현되는 이미지
     를 의미함. 즉 한 픽셀을 24비트로 표현함

   인덱스(Indexed)컬러 이미지
     트루컬러이미지는 학 픽셀당 3바이트가 필요하기 때문에 용량이 상당히 커지게 됨. 그래서 현재의
     이미지에서 가장 많이 사용하는 색 8비트만들 추려서(256컬러만을 추출하여) Index컬러이미지를
     만들게됨.
     이렇게 하면 이미지를 본래의 색으로 정확하게 표현하지는 못하지만 파일의 크기를 대폭 줄일수
     있게됨.

     트루컬러로 만들어진 이미지를 인덱스로 만들게 되면 실제로 추려낸 256컬러의 이미지를 표로 만들어
     저장을 하게 되는데 이것을 팔레트라고 부름.

     인덱스컬러이미지가 반드시 256컬러일 필요는 없고 64개 또는 16개의 컬러만으로 팔레트를 생성할
     수도 있음을 알아 두어야 하고.. 이렇게 팔레트를 만들게 되면 실제 픽셀을 나타내기 위한 이미지는
     인텍스의 위치를 알려주는 정보만 가지고 있으면 되므로 6비트 혹은 4비트의 공간만을 사용하게됨.

   흑백 이미지(그레이스케일)
     팔레트에 담겨져 있는 색들이 모두 회색계열(흰색부터 검은색까지..)일때 이러한 이미지를 말함.
     RGB의 값이 동일한 비율로 되어 있으면 이 픽셀의 색은 회색이 됨(테스트 해보기 바람)

     흑백 이미지는 인덱스 값이 곧 밝기가 되고 실제로는 팔레트가 고정되어 있어 팔레트자체가
     무의미한 경우가 많음.

   이진 이미지
     검은색과 흰색으로만 이루어진 컬러 이미지를 의미함

 

BMP파일 비트맵 포맷
  
   각종 이미지 파일마다 데이타를 저장하는 형식이 다 다르기 때문에 여기에서는 대표적인 이미지 파일
   BMP파일에 대해서 다루어 보도록 하겠다.

   이 파일 형식이 가장 간단하다.

   BMP파일 포맷 형식
 

비트맵 파일에 대한 정보
BITMAPFILEHEADER

비트맵 자체에 대한 정보
BITMAPINFOHEADER

팔레트
RGBQUAD[]

이미지 비트

     1. 비트맵 파일에 대한 정보
          BITMAPFILEHEADER는 비트맵 파일에 대한 정보를 저장하기
          위한 구조체로 다음과 같이 정의되어  있다.

          typedef struct tagBITMAPFILEHEADER {
               WORD      bfType;        // "BM"이라는 값을 저장
               DWORD     bfSize;        // 파일의 크기 (바이트 단위)
               WORD      bfReserved1;        // 예약값
               WORD      bfReserved2;         // 예약값
               DWORD     bfOffBits;    // 실제 이미지 비트까지의 오프셋
          } BITMAPFILEHEADER




     2. 비트맵 자체에 대한 정보
          typedef struct tagBITMAPINFOHEADER {
               DWORD      biSize;             // 이 구조체의 크기
               LONG        biWidth;             // 이미지의 폭 (pixel)
               LONG        biHeight;           // 이미지의 높이(pixel)
               WORD        biPlanes;          // 비트 플레인 수(항상 1)
               WORD        biBitCount;         // 픽셀 당 비트수
               DWORD       biCompression;   // 압축 유형
               DWORD      biSizeImage;       // 이미지의 크기(바이트 단위)
               LONG        biXPelsPerMeter;   // 가로 해상도
               LONG        biYPelsPerMeter;   // 세로 해상도
               DWORD      biClrUsed;          // 실제 사용되는 색상 수
               DWORD      biClrImportant;      // 중요한 색상 인덱스(0이면 전체 인덱스를 의미)
          } BITMAPINFOHEADER

     3. 팔레트
         팔레트는 RGBQUAD 구조체의 배열로 저장됨
         즉, 16비트이면 16개 길이의 배열. 256 이면 256개 길이의 배열로 저장되는 것임.
         트루컬러 이미지일 경우에는 팔레트가 필요하지 않음.

         typedef struct tagRGBQUAD {
              BYTE rgbBlue;        // 파란색 성분
              BYTE rgbGreen;       // 녹색 성분
              BYTE rgbRed;         // 빨간색 성분
              BYTE rgbReserved;   // 확장을 위해서 예약된 값
         } RGBQUAD;

     4. 파일의 전체적인 구조
  

이름

바이트

내용

구조체

구조체

bfType

2

"BM"이라는 글자 설정

BITMAPFILEHEADER

 

bfSize

4

비트맵 파일의 전체크기

 

 

bfReserved1

2

예약변수 (0으로 설정)

 

 

bfReserved2

2

예약변수 (0으로 설정)

 

 

bfOffBits

4

파일에서 비트맵 데이타가
 있는 위치

 

 

biSize

4

BITMAPINFOHEADER

구조체 크기

 

biWidth

4

비트맵의 가로크기

 

 

biHeight

4

비트맵의 세로크기

 

 

biPlanes

2

Plane수(1로 설정)

 

 

biBitCount

2

한 픽셀당 비트수

 

 

biCompression

4

압축 유무 플래그

BITMAPINFOHEADER

 

biSizeImage

4

그림 데이터의 크기

 

 

biXPelsPerMeter

4

한 픽셀당 가로 미터

 

 

biYPelsPerMeter

4

한 픽셀당 세로 미터

 

BITMAPINFO

biClrUsed

4

그림에서 실제 사용되는 컬러수

 

 

biClrImportant

4

중요하게 사용되는 컬러

 

 

팔레트 수만큼

 

 

 

 

rgbBlue

1

 

 

 

rgbGreen

1

현재 그림이 팔레트를 사용할

RGBQUAD

RGB순서가

rgbRed

1

경우 팔레트 테이블이 생성

 

아니라 BGR의

rgbReserved

1

 

 

순서임!!

실제 데이터

 

실제 그림정보가 설정되어 있는 데이터

비트맵 사용시 주의할 사항

   메모리상에는 이미지가 거꾸로 저장이 됨
     비트맵 정보가 메모리에 저장이 되면 그 이미지는 거꾸로 뒤집힌 형태로 저장이 됩니다.
     비트를 다루어 이미지를 처리할때는 이러한 사항들을 주의깊게 이해하고 있어야 합니다.

   가로의 길이는 4바이트의 정수가 되어야 함
     예를 들어 픽셀당 8비트로 표현되는 256컬러 이미지의 가로 길이가 38픽셀이었다면 이미지의 가로
     줄을 저장하는데에는 38바이트가 필요하겠지만 실제로는 40바이트에 저장되어야 한다는 것이고
     나머지 2바이트에는 무의미한 정보가 들어가게 됨

이미지 처리를 위한 기본 프로그램

[목차로가기]

순서...................................
   - 기본 프로그램

기본 프로그램

   이미지 프로세싱을 연습하기 위해서는 이미지 파일을 불러오고 저장하는 프로그램이 필요하다.
   여기서 선택한 프로그램은 영진출판사 Visual C++ 6 Chapter13(김용성 저)에 수록되어
   있는 이미지 프로세싱 프로그램을 이용하도록 하겠다.

   기본적인 이미지 프로세싱에 대한 설명이 되어 있고.. 간단한 것들은 책속에 설명도 되어 있으므로
   참고한다면 좋은 자료가 될것이라고 생각한다.

   일단 코드를 추가하고 이미지 프로세싱에 대한 설명을 하려면 이미지 프로세싱 프로그램이 필요할께다.
   다운받아서 하나씩 해볼수 있는 기틀을 마련해 보도록 합시다.

[DOWNLOAD]
-= 남의 소스를 이용할때에는 감사의 마음을 가지면서..  =-

   본인지 Visual C++프로그래밍을  해본지가 오래되질 않아서 VC로 프로그램을 짜고서도 제대로 짠
   소스라고 보여주고 하기에는 쑥스러운 경우가 많이 있어서 혹시라도 제대로 프로그래밍을 못하더라도
   너그러이 이해해달라고 부탁드리는 바입니다. ^^;

기본 용어 정리

[목차로가기]

순서..............................
   - 표본화/양자화
   - 약어 관련


표본화/양자화

   표본화
     공간적으로 영속적인 영상을 이산적인 화소의 집합으로 분할하는 것.
     즉, 세밀하지 않게 표본화를 수행하면 모자이크 처리된느낌의 영상을.. 세밀하게 표본화를 하면
     선명한 이미지를 얻을 수 있다.

   양자화
     화소의 농담을 이산적인 정수 값으로 변환하는 조작을 말함.
     즉, 1bit양자화는 0과 1의 값만을 가질 수 있으므로.. 어둡고 밝은 두단계의 양자화를 가질 수 있음.
     2비트 양자화는 00, 01, 10, 11의 값을 가질 수 있으므로 어둡고 밝은 4가지의 단계로 양자화가 됨.
     그렇다면... 우리가 흔히 이야기하는 256컬러 그레이드 이미지는?
     8비트 양자화를 수행한것이겠군..

     컬러이미지의 경우에는.. RGB각각 양자화를 가질수도 있겠군...

 

약어 관련

   CCITT(International Telegraph and Telephone Consultative Committee)
     국제 전신전화 위원회

   DCT(Discrete Cosine Transform)
     영상블록을 서로 다른 주파수 성분의 코사인 함수로 분해하는 과정

2진 영상처리

[목차로가기]

순서.......................................
   - 경계값 처리하기
   - 히스토그램에 의한 농담 분포조사
   - 히스토그램의 평활화
   - 콘트라스트(contrast)기법

경계값 처리하기

   개요
     아래에 보이는 이미지가 있다고 가정해봄.
    
    
    
     이 이미지는 0부터 255까지의 그레이 스케일 이미지 이다. 이 이미지에서 100이라는 명암을 경계로
     그 이상의 숫자에 대해서는 1로.. 그 이하의 숫자에 대해서는 0으로 지정을 하게되면
     흑, 백 두가지 색상만을 가지게 되는 이미지가 나타날 것이다. 이러한 것을 경계값 처리라고 한다.

     반대로 처리할 수도 있을것이다. 즉, 100이상이면 0으로 100이하이면 1로.. 즉, 반전이 되겠지?

   소스코드 보기
     // 경계선 처리하기.
     void CImageFiltering::Boundary(int value)
     {
         ASSERT(m_pImage);
         ASSERT(m_pImage->GetHandle() != NULL);
         ASSERT(m_pImage->GetUndoHandle() != NULL);

         if(m_nDepth == 8) BoundaryGray(value, _T("경계선 처리하기")); // 흑백이미지일 경우.
         else BoundaryColor(value, _T("경계선 처리하기"));  // 컬러이미지일 경우.
     }
     void CImageFiltering::BoundaryGray(int value, CString strProgressBar)
     {
         CProgressBar bar(strProgressBar, 100, m_nHeight);
         int i, j;
         CPixelPtr ptrSorce(m_pImage->GetUndoHandle());
         CPixelPtr ptrDest(*m_pImage);
         CPixel p;

         for(j=0 ; j<m_nHeight ; j++)
         {
             for(i=0 ; i<m_nWidth ; i++)
             {
                 if(ptrSorce[j][i] >= value) {
                     ptrDest[j][i] = 0;
                 } else {
                     ptrDest[j][i] = 255;
                 }
             }

             bar.StepIt();
         }
     }
     void CImageFiltering::BoundaryColor(int value, CString strProgressBar)
     {
     }

   프로그램 실행화면
    
     경계값처리에 대한 실습을 다른 이미지로 해보았다.

 

히스토그램(Histogram:빈도분포)에 의한 농담 분포조사

   개요
     한점의 화소를 특정한 값과 비교하는 경계값처리와는 다른 방식임.
     0부터 255까지의 이미지 숫자가 있다면 각 이미지마다 몇개가 분포되어 있는가를 그래프로
     나타내는 것을 말함.

     이 이미지를 활용하여 경계값을 처리하는 방식을 말하는 것임.

   구현소스
    
     현재 이소스는 이문호님이 지으신 실용영상신호처리 책에서 따온것이랍니다.
     이것을 어떤 사이즈에든 상관없이 만들려고 노력을 해봤습니다만.. 생각보다 복잡하군요.
     결국은 256x256사이즈만 인식하는 프로그램이 되어 버렸답니다.

     혹시라도 고치신 분이 계시면.. 알죠? ^^

   실행결과 화면
    
     왼쪽이미지에 대한 빈도 분포를 오른쪽 그림에서 보여주고 있다.
     왼쪽부터 어두운이미지 빈도분포.. 오른쪽으로 갈수록 밝은 부분의 빈도 분포이다.

 

히스토그램의 평활화
   위 설명에서와 같이 농담 분포를 조사하게 되면 너무 밝은 쪽으로 히스토그램이 나타나거나 또는
   너무 어두운 쪽으로만 히스토그램을 나타날 수 있는 경우가 생긴다.. 위의 이미지의 경우에는 보는
   바와 같이 중간정도의 이미지 색상 빈도가 상당히 높게 나타나고 있다. 이를 평활화 하면,
   즉, 명암값의 분포의 균일화를 이루게되면 좀더 경계선이 뚜렷해 지는 효과를 볼 수 있게 된다.

   히스토그램 평활화 방법
    
     1의 축적된 빈도수는 명도값0의 빈도와 명도값1의 빈도수를 더한값
     2의 축적 빈도수는 명도값0, 명도값1, 명도값2의 빈도수를 더한값
     255의 축적 빈도수는 명도값0~명도값255까지의 빈도수를 더한값을 나타낸다.

     이런식으로 평활화를 수행하게 되면 보이지 않던 색깔까지 나타내주기때문에 결과적으로는
     경계선이 좀더 뚜렷해지는 효과를 볼수 있게 된다.

   실행화면 보기
    
     평활화를 수행하 후에는 이미지에 약간의 변화가 있음을 알게 될것임.
     오른쪽 히스토그램도 변화가 생겼음.

   소스보기
    
 

콘트라스트(contrast)기법
   화면의 밝은부분과 어두운 부분의 명도비를 콘트라스트라고 하고, 이러한 콘트라스트의 비가 높으면
   선명하고 보기 쉬운 영상이 되지만.. 콘트라스트비가 낮으면 흐릿한 이미지가 된다.

   여기에 대한 구현부분은 기본프로그램에 이미 구현이 되어 있는 상태이다.
   함께 제공되는 소스를 참고하여 분석하기 바람

  
   원본이미지, 콘트라스트증가, 콘트라스트 감소

마스크를 이용한 이미지 처리

[목차로가기]

순서.....................................
   - 가우시안을 이용한 잡음 만들기
   - 평활화 방법을 통한 잡음 제거
   - 미디언 필터를 이용한 방법
   - 이진 영상데이타의 잡음제거
   - 영상을 날카롭게 만들기
   - 경계선 추출하기
   - 엠보싱 효과내기

가우시안을 이용한 잡음 만들기
   잡음을 제거하는 방법에 대해 설명하기 전에 잡음제거에 쓰일 이미지를 만들어 보기로 함.
   가우시안 알고리즘을 이용하여 이미지에 잡음을 넣을 수 있도록 하는 코드를 하나 추가 하였음.

   소스 코드는 아래와 같음.

   소스보기
    // 이미지에 잡음을 첨가하는 알고리즘.(그레이 스케일용)
    void CImageControl::Gaussian()
    {
        ASSERT(m_pImage);
        ASSERT(m_pImage->GetHandle() != NULL);
        ASSERT(m_pImage->GetUndoHandle() != NULL);

        if(m_nDepth == 8) GaussianGray();
        else GaussianColor();
    }
    void CImageControl::GaussianGray()
    {
        CProgressBar bar(_T("잡음 추가하기"), 100, m_nHeight);
        int x, y, mean, var;
        float noise, theta;
        CPixelPtr ptrSorce(m_pImage->GetUndoHandle());
        CPixelPtr ptrDest(*m_pImage);
        CPixel p;

        for(y=0; y<m_nHeight; y++) {
            for(x=0; x<m_nWidth; x++) {
                noise = sqrt(-2*200*log(1.0-(float)rand()/32767.1));
                theta = (float)rand()*1.9175345*exp(-4)-3.141592654;
                noise = noise*cos(theta);
                noise += 15;
                if(noise>255) noise = 255;
                if(noise<0) noise = 0;

                ptrDest[y][x] = ptrSorce[y][x]+(unsigned char)(noise + 0.5);
            }

            bar.StepIt();
        }
    }

   잡음이 추가된 형태
     <잡음 추가된 이미지>

 

평활화 밥법을 통한 잡음 제거
  
위의 이미지에서 잡음이 추가된 형태를 보면 검은색 점들 때문에 이미지가 부 자연스러워 보이는데
   이러한 부분은 주변의 색상과의 금격한 농도차로 인해서 보기 싫은 이미지가 된다. 이러한 부분에서의
   농도차를 줄여줌으로써 잡음을 제거하는 기법이다.

   간단한 잡음 제거법으로 이동 평균법이라는 것이 있는데.. 이는 어떤 화소 주변의 3X3화소의 평균값을
   그 화소의 값과 교환하는 방식이다.

   즉, 영상을 흐리게 만들어서 세세한 잡음을 눈에 감추는 기법을 이용한 것이다.

  

   소스코드 보기
        for(y=1 ; y<m_nHeight-1 ; y++)
        {
            for(x=1 ; x<m_nWidth-1 ; x++)
            {
                buf[0] = ptrSorce[y-1][x-1];
                buf[1] = ptrSorce[y-1][x];
                buf[2] = ptrSorce[y-1][x+1];
                buf[3] = ptrSorce[y][x-1];
                buf[4] = ptrSorce[y][x];
                buf[5] = ptrSorce[y][x+1];
                buf[6] = ptrSorce[y+1][x-1];
                buf[7] = ptrSorce[y+1][x];
                buf[8] = ptrSorce[y+1][x+1];

                for(k=0, sum=0; k<9; k++) sum = sum+buf[k];
                ptrDest[y][x] = sum/9;
            }
        }

   잡음을 3회 연속 제거한 이미지
    
     부드러워 진것이 보이는가?

   이러한 방식에서의 단점
     이러한 방식이 잡음을 제거하는데 간편하기는 하지만 전체적인 이미지 영상이 흐려지는 단점을 가지고
     있다.

 

미디언 필터를 이용한 방법
  

   평균값을 내는 것과는 다름에 유의해야 한다.
   이렇게 하면 이미지가 흐려져 보이는 단점을 보안 할수 있다.

   소스코드보기
    int i,j,k,l,temp,f,g;
    unsigned char buf[9];

    for(i=1, l=1; i<m_nHeight-1,l<m_nHeight-1; i++, l++) {
        for(j=1,k=1;j<m_nWidth-1,k<m_nWidth-1;j++,k++) {
            buf[0] = ptrSorce[i-1][j-1];
            buf[1] = ptrSorce[i-1][j];
            buf[2] = ptrSorce[i-1][j+1];
            buf[3] = ptrSorce[i][j-1];
            buf[4] = ptrSorce[i][j];
            buf[5] = ptrSorce[i][j+1];
            buf[6] = ptrSorce[i+1][j-1];
            buf[7] = ptrSorce[i+1][j];
            buf[8] = ptrSorce[i+1][j+1];

            for(g=0; g<8; g++) {
                for(f=0; f<8; f++) {
                    if(buf[f] > buf[f+1]) {
                        temp = buf[f];
                        buf[f] = buf[f+1];
                        buf[f+1] = temp;
                    }
                }
            }

            ptrDest[i][j] = buf[4];
        }
    }


   실행결과 이미지
     미디언 필터 10번 적용화면
     흐려지지 않음을 확인가능.

     단점이 있다면 평활화 방법보다는 시간이 오래 걸린다는 점이다.

 

이진 영상데이타의 잡음제거
   여기서 말하고자 하는 이진영상이라는 것은 흰색과 검은색으로만 이루어진 이미지를 말하는 것이다.
   수축처리
     어떤 화소 주변에 하나라도 0의 값이 있으면 그 화소는 0으로 하고 나머지는 1로 처리하는 것이다.

   확장처리
     수축처리와는 반대로 어떤 화소 주변에 하나라도 1의 값이 있으면 그 화소는 1로하고 나머지는 0으로
     처리하는 것이다.

   실행화면 이미지
     일단 두가지를 한꺼번에 알아봐야 하기 때문에 실행화면을 먼저 보면..
    

   수축처리 소스
        for(i=1; i<m_nHeight-1; i++) {
            for(j=1; j<m_nWidth-1; j++) {
                ptrDest[i][j] = ptrSorce[i][j];
                if(ptrSorce[i-1][j-1] == 0) ptrDest[i][j] = 0;
                if(ptrSorce[i-1][j] == 0) ptrDest[i][j] = 0;
                if(ptrSorce[i-1][j+1] == 0) ptrDest[i][j] = 0;
                if(ptrSorce[i][j-1] == 0) ptrDest[i][j] = 0;
                if(ptrSorce[i][j+1] == 0) ptrDest[i][j] = 0;
                if(ptrSorce[i+1][j-1] == 0) ptrDest[i][j] = 0;
                if(ptrSorce[i+1][j] == 0) ptrDest[i][j] = 0;
                if(ptrSorce[i+1][j+1] == 0) ptrDest[i][j] = 0;
            }
        }

   확장처리 소스
        for(i=1; i<m_nHeight-1; i++) {
            for(j=1; j<m_nWidth-1; j++) {
                ptrDest[i][j] = ptrSorce[i][j];
                if(ptrSorce[i-1][j-1] == 255) ptrDest[i][j] = 255;
                if(ptrSorce[i-1][j] == 255) ptrDest[i][j] = 255;
                if(ptrSorce[i-1][j+1] == 255) ptrDest[i][j] = 255;
                if(ptrSorce[i][j-1] == 255) ptrDest[i][j] = 255;
                if(ptrSorce[i][j+1] == 255) ptrDest[i][j] = 255;
                if(ptrSorce[i+1][j-1] == 255) ptrDest[i][j] = 255;
                if(ptrSorce[i+1][j] == 255) ptrDest[i][j] = 255;
                if(ptrSorce[i+1][j+1] == 255) ptrDest[i][j] = 255;
            }
        }

   단점
     위 실행 결과에서 볼 수 있듯이.. 좀 복잡한 이미지의 경우에는 원본이미지를 많이 회손하게 된다.

 

영상을 날카롭게 만들기
   인접 픽셀들 간의 밝기 값의 차이를 늘려주면 영상을 날카롭게 만들수 있음.

  

   원본이미지가 마스크의 값에 따라서 어떻게 변화 되는가를 보여주고 있다.

   마스크 값들을 자세하게 살펴보면 공통적인 특징이 보일것이다. 모든 값들의 합은 0보다 크다는
   것과 중심점의 가중치가 양수라는 것이다.

   소스보기
    

    
이 소스는 첫번째 마스크를 적용한 경우입니다. 부분 소스임.

 

경계선 추출하기
   이진영상처리부분에서도 경계값을 추출하기 알고리즘을 만들었지만 여기서는 마스크를 이용한
   경계선 추출 방법임.

  

   위에서 사용한 이미지에 대한 마스크값을 적용한 예임.

   마스크값의 공통점을 추출해 보면.. 마스크 속의 가중치의 합들은 모두 0이됨을 알 수 있음.

   그외 경계선을 추출하는 마스크를 몇가지 더 보여주고자 한다.
   각각의 마스크는 특징들을 가지고 있으므로 한번씩 적용해 보면 좋다.

  

 

엠보싱 효과
   영상을 올록볼록하게 만드는 것을 의미함.

  

   이와 같은 마스크를 이용하면 엠보싱 효과를 낼 수 있게 된다.

자바에서 BMP파일 포맷 다루기

[목차로가기]

순서.......................................
   - BMP파일 저장하기
   - BMP파일 불러오기

자바는 기본적으로 BMP파일을 입출력할 수 있는 방법을 제공하지 않습니다.

하지만 저의 경우에.. 자바에서 기본적으로 제공하는 GIF, JPEG등의 이미지 파일을 가지고 이미지를 다루는 부분
부분 보다도 직접 이미지 파일에 접근하여 그 이미지 파일을 생성하고 읽고.. 쓰는 작업대 대해서 알아야 할
필요가 생겼답니다.

이러한 방식으로 이미지 파일에 접근 하는 것은 Visual C++이나 다른 언어에서는 상당히 상세하게 설명되어
있는 책이 있지만 자바에 관해서는 그러한 서적이 전무한것을 느끼고.. 웹쪽으로 눈을 돌린 후  웹서핑을 하다가
저의 궁금증을 해결해 줄만한 좋은 자료들을 발견할 수 있었습니다.

개인적으로는 이러한 방법을 마스터하게 되면 JPEG, GIF와 같은 이미지의 스펙만을 보고 이미지파일을
직접 다룰수 있는 노하우(?)가 생길 듯하여 공부하게 되었답니다.

저도 사실은 많이 알지를 못합니다. 그냥.. 이미지 프로세싱에 대해 공부하고 싶은 초년생에 불과하죠..
많은 분들이 이 정보를 가지고 도움을 받을 수 있었으면 하는 바입니다.

저도 잘 모르지만.. 같이 알아가는 재미가 있잖아요.. ^^
 

BMP파일 저장하기
   BITMAP FILE HEADER
     C언어나 C++언어로 작성된 책을 보면 BMP파일에 대한 헤더 정보를 아래와 같이 정의하고 있습니다.

     typedef struct tagBITMAPFILEHEADER {
        UINT       bfType;
        DWORD    bfSize;
        UINT       bfReserved1;
        UINT       bfReserved2;
        DWORD    bfOffBits;
     } BITMAPFILEHEADER;

     typedef struct tagBITMAPINFOHEADER {
        DWORD    biSize;
        LONG      biWidth;
        LONG      biHeight;
        WORD      biPlanes;
        WORD      biBitCount;
        DWORD    biCompression;
        DWORD    biSizeImage;
        LONG      biXPelsPerMeter;
        LONG      biYPelsPerMeter;
        DWORD    biClrUsed;
        DWORD    biClrImportant;
     } BITMAPINFOHEADER;

     지금까지의 정보는 bmp이미지를 만들기 위해서 정의되어진 것들입니다.
    
     Visual C++에서 BMP파일을 불러오는 것과 Java에서 BMP파일을 불러오는 부분을 아래의 소스코드와 비교
     해 보시면.. 좋을겁니다.

   소스보기 [DOWNLOAD] - http://www.javaworld.com에서 얻어온 소스임
     소스코드에 대해서 하나하나 살펴보기로 하겠습니다.

     저도 완벽하게 분석하지 못한 부분이 있을 수도 있으므로.. 잘못된 부분이 있으면 지적바랍니다.
     완전한 소스는 위의 DOWNLOAD링크를 클릭하시면 됩니다.

    

     이부분은 BMP파일 에 대한 BITMAPFILEHEADER정보를 설정하고 있습니다.
     일단은 기본값으로 저장을 하고 있습니다.

    

     여기서는 BITMAPINFOHEADER부분을 정의하고 있군요.. 이정보들이 byte형으로 40만큼의 길이를 가지고
     있는가 봅니다. 위에 배열 설정한 부분을 보면말입니다.

     biBitCount부분에 보면 24라고 설정이 되어 있지요.. 이부분은.. 한픽셀에 24비트의 색깔이미지를 사용한다는
     의미입니다. 즉.. 현재 이 프로그램은 그러한 색상만을 지원하는 알고리즘이라고 할 수 있습니다.

    

     이부분은 Image객체를 파일 저장을 위해서 변환을 해주는 부분이군요..

     bitmap = new int[parWidth*parHeight]  라는 부분에서 현재의 이미지 크기만큼의 배열을 설정하구요.

     PixelGrabber pg = new PixelGrabber(parImage,0,0,parWidth,parHeight,bitmap,0,parWidth);
     라고 하는 부분은.. 주어진 배열정보로 부터 PixelGrabber객체를 만들어 내는군요..

     pg.grabPixels() 라고 하는 부분은 작업이 완료될때까지 기다리게 만드는 부분인가 봅니다.

     pad = (4-((parWidth*3)%4))*parHeight;
     BMP파일 이미지가 저장이 될때 폭의 길이가 반드시 4의 배수이어야 하고.. 4의 배수가 아니라면
     의미 없는 값으로 나머지 길이를 채워주어야 한다는 것을.. 설명했었죠?

     그 부분때문에 pad라고 하는 부분을 만든것으로 보입니다.
     그럼 biSizeImage는 ((parWidth*parHeight)*3)+pad만큼의 크기가 되는것이고..
     bfSize라고 하는 값에는.. 파일헤더사이즈와 정보헤더사이즈.. 그리고.. 이미지사이즈가 들어가겠군요.
     이미지의 폭과 높이는 parWidth, parHeight가 각각되겠군요..

     제대로 변환이 이루어 졌으면 true를 반환합니다.
    
    
    
     writeBitmapFileHeader, writeBitmapInfoHeader를 설명해야 하되지만 그 메소드 안에서  위의 두가지
     메소드를 호출하는 군요.. 그래서 이것먼저 설명하고 넘어가도록 하겠습니다.

     우선 int형 자바변수값을 이미지파일이 인식할 수 있는.. byte형 배열로 변환해서 리턴하는 군요..
     아마도 이미지 파일에서는 이와 같은 형태로 저장되 되어 있어야 하는가 봅니다.

     intToWord(int parValue)메소드는.. 2라는 길이를 가진 byte형 배열로 되어 있고. intToDWord(...)메소드는
     4바이트 길이군요..

     int형을 WORD나 DWORD타입으로 변환을 할때는 이러한 방법으로 사용해야 하는가 봅니다.

     그렇다면 writeBitmapFileHeader(), writeBitmapInfoHeader()메소드는 해결이 되는군요.
     정보들이 이미지 파일에 맞도록 변형시켜서 파일에 적어주는 겁니다.

     마지막으로 writeBitmap()메소드 이군요..

     <부분소스임>

     pad = 4-((biWidth*3)%4); 라고 하는 부분은 아까도 설명을 했듯이 이미지를 4의 배수로 맞추기 위해서
     사용한다고 했었습니다.

     for(...) {...}
  
     rgb[3]이라고 하는 바이트형 배열을 만들어서 value형을 비트 연산한 결과를 넣어주는군요..
     rgb하면.. 색깔정보를 의미 하는가 본데.. 이렇게 하면 색 정보가 들어가는가 보죠? 저도 자세하게
     모르겠습니다.

     중간에 if문이 있는것은.. 일렬로 늘어서있는 이미지정보를 타일에 넣기 위해 작업해 주는 부분입니다.
    
     저도 사실은 완벽하게 분석을 못하겠군요.. 내공이 부족한가 봅니다. ㅠ.ㅠ

BMP파일 불러오기
   지금까지 이미지 파일을 저장하는 법에 대해서 분석을 해 봤습니다.

   이번에는 BMP파일을 불러오는 부분을 구현해 볼차례입니다.
   소스코드는 위에서 소스를 다운 받으시면 같이 있습니다.

   약간의 버그가 있습니다. 이미지 파일을 제대로 읽어 오지를 못하는 군요.. 한번 해보세요..
   원본 이미지와는 다르게 색이 외곡되서 나타날겁니다.

   이번 예제에서 불러올수 있는 BMP이미지 파일은 24 or 8비트로 픽셀이 구성된 두가지 종류만이 가능하다고
   합니다.

영상 데이타 압축 알고리즘 - 1

[목차로가기]

순서.........................................................
   - Discrete Fourier Transform (DFT:이산푸리에변환)
   - Fast Fourier Transform (FFT:고속푸리에변환)
   - 2차원 푸리에 변환 프로그래밍하기


주파수 영역에서 데이터를 표현하기 위해서는 여러가지 변환 방법이 필요한데 그중에서 가장 많이 알려진
방법이 Fourier변환이라는 것이다.

Fourier변환이라는 것은 시간영역에서 주파수 영역으로의 변환을 의미하는 것으로 "모든 파형은 단순한
정현파의 합으로 표현할 수 있다."라고 하는 기본원리를 바탕으로 두고 있는 것이다.

하나의 사람에 대한 그림을 분석해 보면 머리카락, 손, 발 등의 부분으로 나누어 볼 수 있고..
사람에 대한 복잡한 이미지를 각각의 부분으로 분류하여 분석하면 좀더 쉽게 분석이 가능하고..
이러한 하나하나의 부분들을 다시 원래대로 복원(?)을 하게되면 원래의 이미지를 얻어 낼수 있듯이

일반적인 신호도 여러가지 개개의 특징적인 신호들로 분류할 수 있다는 것을 의미하는데.. 이러한
신호들이 하나로 뭉쳐진 상태에서는 분석하기 힘들지 몰라도 특징적인 신호들로 분류된 여러개의 신호를
분석하여 원 신호로 나타내는 과정을 수행하면 분석하기도 쉽고.. 원래의 신호로 복원도 가능하다는
아이디어에서 출발한 것이다.

이러한 기본적인 아이디어를 염두해 두고 주파수 영역에 대한 부분을 살펴보도록 하자.

Discrete Fourier Transform (DFT:이산푸리에변환)
  
   이 그림은 (a)라고 하는 파형을 세가지 정현파 a-1, a-2, a-3로 분해를 한 그림이다.

   정현파라고 하는것은 a-1, a-2, a-3와 같이 같은 모양으로 파형이 반복되는 경우를 의미하는 것이다.
   이 정현파를 자세히 분석해 보면 다음과 같이 나누어 볼 수 있게 된다.

  
   실선으로 그려진 진폭 A를 가지는 파형과 위상(∮)으로 나타낼 수가 있다.

  
   실세계에 존재하는 임의의 파형 (a)있다고 했을때 이를 퓨리에 변환을 할 수가 있게되는데
   가로축이 주파수이고, 세로축이 진폭인 그래프와, 가로축이 주파수이고 세로축이 위상인 그래프
   두가지 경우로 나타낼 수 있게 된다.

   이러한 과정을 통해서 우리는 일반적인 신호를 주파수의 영역으로 변환할 수가 있게 된다. 주파수영역
   에서의 그래프로 보면 알겠지만 연속적인 신호로 나타나는 것을 볼 수 있을 것이다.

   지금까지의 것들을 수식적으로 표현해 보도록 하자.

   실제의 공간(시간영역)과 주파수 영역의 관계를 수식으로 나타내면..

   이 되고, 이를 직교변환의 한종류인 Fourier변환이라고 부른다.

   Fourier변환은 보통 진폭(A)와 위상( )을 나타내므로, 복소수로 표현을 한다.
   이 복소수로 표현을 하는경우, 진폭과 위상을 구별해서 다루지 않고, 함께 다룰수 있는 장점이 있기때문에
   여러가지 변환을 하는데 있어서 매우 편리하다

   위의 식을 복소스 함수인 X(f)을 사용하여 다음과 같이 나타낼 수 있다.

   이 Fourier변환 과정의 결과를 나타내면 다음과 같은 식이 된다.

   <푸리에 변환>

   Z표시는 적분기호를 의미합니다.(아래한글로 편집하고 있는데 적분표시가 이렇게 나오네요.)

   우리가 수학적으로 전개해 나가는 Fourier변환을 프로그램으로 표현하여 결과를 얻을려면 어떤과정을
   거쳐야 되는가 하는 점을 알기 위해서는 수학과 컴퓨터의 연산방식의 차이점을 알아야 하는데.

   우리가 다루는 신호, 즉 수학적인 신호는 연속적인 analog신호이지만 컴퓨터에서 사용하는 신호는
   digital신화는 점이다. 또한가지 알아 두어야 할것은 우리가 다루는 신호는 무한대의 범위를 가질수도
   있지만 컴퓨터에서 다루는 신호의 경우에는 한정된 범위에서 계산을 해야 한다는 것이다.

   이처럼 자연신호를 이산적인 신호(즉, 연속적인 흐름이 아닌신호)로 나타내는 푸리에 변환을 바로
   DFT, 즉 이산 푸리에 변환이라고 부르는 것이다.

   DFT는 위에서 알아본 푸리에 변환식을 이산적으로 변환하여 유도할 수 있는데 만약, 입력 신호가
   x(0), x(1), x(2), ... , x(N-1)의 이산 값이라고 하면, 그 주파수 영역에의 변환 결과(복소수)도
   N개의 이산 값이 되고, 그것을 X(0), X(1), ... , X(N-1)로 하면 다음과 같은 관계가 성립된다.

   DFT는 FT공식에 을 대입하면 된다.

  

   위의 수식이 바로 DFT의 기본 식이 된다. 하지만 문제가 되는 부분이 있는데 적분식이 제거되고
   비록 덧셈의 식이 되기는 했지만 회전자라고 하는 W는 처리하기 곤란한 상황이 되었다. 그래서
   이 W를 오일러 공식을 이용하여 다음과 같이 변환을 할 수가 있다.

  

   이 식을 위의 식에 대입을 하면 W라는 회전자 기호를 없앨 수 있다. 즉, 삼각 함수의 곱셈과 덧셈에
   대한 연산이 되므로 컴퓨터로 처리할 수 있는 식이 되었지만, 그 연산량이 상당히 방대해지게 되는
   문제가 발생하게 된다.

   이러한 문제를 해결하기 위하여 고안된 알고리즘이 고속 푸리에 변환(FFT: Fast Fourier Transform)
   이라고 하는 알고리즘이다.

Fast Fourier Transform (FFT:고속푸리에변환)
   FFT라고 하는 것은 DFT에 비해 상당히 빠른 계산을 수행할 수 있게 해주는 알고리는이다.

   그 이유는 데이터 수가 2의 멱 제곱의 경우 계산 량을 대폭 줄일 수 있게 되는데, N-point DFT를
   더 작은 크기의 DFT로 분해해서 complex number W의 대칭성과 주기성을 이용해서 계산을 간단하게
   하는 알고리즘이고..

   데이타 개수 N에 대한 DFT의 계산은 N의 제곱의 곱셈이 필요하지만 FFT의 계산은
   곱셈이 필요하다. 따라서 FFT는 계산시간을 많이 줄일수 있는 알고리즘이 되는 것이다.

   그럼 지금까지 배운 내용을 가지고 4 Point FFT를 수행하면서 내용을 정리해 보도록 합시다.

  

   좀더 쉽게 계산하기 위해서 이를 행렬로 풀어보도록 하죠.

  

   힘드네요. ㅋㅋ

2차원 푸리에 변환 프로그래밍하기
   지금까지의 이론적인 내용을 바탕으로하여 이를 영상이미지로 직접 프로그매밍화 해야 할것이다.

   지금까지의 설명과 조금 다른 부분이 있다면, 지금까지는 1차원에 대해서 설명을 한 부분이지만
   영상이미지는 2차원이미지 이므로 2차원으로 계산을 할 필요가 있다. 2차원으로 FFT를 수행하고자
   한다면 FFT를 수평, 수직으로 2번 수행하면 해결이 된다.

   일단은 결과 화면을 먼저보고..

  
   원본이미지                    이차원 푸리에 변환           변환결과를 다시 원본 이미지화

  
   사실 푸리에 변환이라는건 알고보면 별거아니다. 하지만 왜 영상데이타의 압축에 대해서 이야기
   하는데 푸리에 변환이라는 말이 나오는 것일까?

   간단하다. 이미지 데이타를 푸리에 변환을 한 뒤에 그 변환된 값을 데이타로 저장을 하면
   기존의 이미지 데이타보다 용량을 적게 차지하게 된다. 그래서 이미지가 압축 되는 것과 같은
   효과를 낼 수가 있는 것이다.

   실제로 이러한 알고리즘을 이용해서 몇몇 이미지 포맷들은 압축을 수행하고 있다.

   소스분석하기
     완성된 소스코드에 대해서는 프로그램을 다운로드 받아서 확인해 보도록 하고.. 여기서는
     다른 소스를 보여주고자 한다.

     본 소스는 http://vision.inchon.ac.kr/ 에서 얻어온 소스임을 밝혀 둔다.
     이미지 프로세싱에 대해서 고급스러운 주제까지는 다루고 있지는 않지만 참고할 만한 사항들이 많은
     사이트이다.

 

 1차원 FFT를 수행하는 함수

 

/****************************************************/
/* function for 1 dimension Fast Fourier Transform */
/****************************************************/
fft1(float a_rl[],float a_im[],int inv)
{
      int I,j,k,w,j1,j2;
      int numb,lenb,timb;
      float s_tbl[64],c_tbl[64];
      float xr,xi,yr,yi,nrml;
      float xx,deg;
 

      xx = ((-pi)*2.0)/(float)64;
      if(inv < 0) xx = -xx; /* if Inverse Fast Fourier Transform , */
 

      /* make sine and cosine table */
      for(i = 0; i < 64; i++) {
           deg = (float)i*xx; s_tbl[i] = sin(deg); c_tbl[i] = cos(deg);
      }
      if(DC == 1)
      for(i = 1; i < 64; i+=2) { a_rl[i] = -a_rl[i]; a_im[i] = -a_im[i]; }
      numb = 1; lenb = 64;
 

      /* FFT excution routine */
      for(i = 0; i < 8; i++) {
            lenb /= 2; timb = 0;
            for(j = 0; j < numb; j++) {
                 w = 0;
                 for(k = 0; k < lenb; k++) {
                 j1 = timb + k ; j2 = j1 + lenb;
                 /* 두 개의 부분으로 나눌 때 상하의 대응하는 위치 지정 */
                 xr = a_rl[j1] ; xi = a_im[j1];
                 /*  상단의 실수, 허수 부분 계산 값을 변수에 입력 */
                 yr = a_rl[j2] ; yi = a_im[j2];
                 /*  하단의 실수, 허수 부분 계산 값을 변수에 입력 */
                 a_rl[j1] = xr + yr; a_im[j1] = xi + yi;
                 /*  상단의 실수, 허수 부분 계산 값을 배열에 입력 */
                 xr = xr - yr ; xi = xi - yi;
                 /*  하단의 실수, 허수 부분 계산 값을 배열에 입력 */
                 a_rl[j2] = xr*c_tbl[w] - xi*s_tbl[w];
                 a_im[j2] = xr*s_tbl[w] + xi*c_tbl[w];
                 w += numb;
                 }
            timb += (2*lenb);
            }
            numb *= 2;
       }
       birv(a_rl); birv(a_im);  /* 비트 연산 */
       if(DC == 1)      /* DC성분을 중앙에 쏠리게 하고 싶은 경우 */
       for(i = 1; i < 64; i += 2) { a_rl[i] = -a_rl[i]; a_im[i] = -a_im[i]; }
       nrml = 1.0/sqrt((float)64);
       for(i = 0; i < 64; i++) { a_rl[i] *= nrml; a_im[i] *= nrml; }
}
 

 

 비트연산 수행 루틴

 

birv(float a[64])
{
      int I,ii,k,bit;
      static float b[64];
 
      for(i = 0; i < 64; i++) {
           for(k = 0, ii = i, bit = 0; ; bit <<=1,ii>>=1) {
                bit = (ii & 1) | bit;
                if(++k == 6) break;
           }
           b[i] = a[bit];
      }
      for(i = 0; i < 64; i++) a[i] = b[I];   /* 처리한 값을 다시 배열에 저장 */
}

 

영상의 2차원 퓨리에 변환

 

   앞 단원의 내용은 신호 x(t)가 1차원인 경우에 대한 설명이었다. 그러나, 영상 데이터의 경우는 평면이기 때문에 신호는 x(i, j)라고 하는 2차원에서 살펴 볼 필요가 있다. 2차원은 수직과 수평의 두 개의 좌표축으로 나타내는 평명이라 할 수 있다. 그러므로 2차원 FFT를 취하고자 한다면, 1차원 FFT를 2번 수행(수평, 수직)하면 된다.

 

 2차원 FFT를 수행하는 루틴

 

/*******************************************************/
/* function for 2 dimension Fast FourierTransform*/
/*******************************************************/
float b_rl[64][64];
float b_im[64][64];
 
fft2(float a_rl[][64],float a_im[][64],int inv)
{
       int I;
 
       /* excute 1-dimension FFT ( horizontal ) */
       for(i = 0; i < 64; i++) fft1(a_rl[I],a_im[i],inv);
       rvmtx(a_rl,b_rl); rvmtx(a_im,b_im); /* transpose matrix */
 
       /* excute 1-dimension FFT ( vertical ) */
       for(i = 0; i < 64; i++) fft1(b_rl[I],b_im[i],inv);
       rvmtx(b_rl,a_rl); rvmtx(b_im,a_im); /* transpose matrix */
 
       printf("FFT_2D PASSED !!!\n");
}

 

 2차원 FFT를 수행시킨 결과 영상은 다음과 같다.

 

영상 데이타 압축 알고리즘 - 2

[목차로가기]
[1][2][3] : 페이지 이동

순서.........................................................
   - 가역 부호화와 비가역 부호화의 의미
   - 이진 영상 부호화(run length coding)
   - 예측 부호화(DPCM)
   - 가변 길이 부호화(Huffman Coding)

...............................................................

이번에는 다른 영상 데이타 압축 알고리즘을 몇개 알아보도록 하겠다.
많은 분량을 내용에 담을려고 하니 한 페이지로는 부족하여 하나의 페이지를 더 마련했습니다.
화면 맨 위쪽의 오른쪽 위를 보시면 [1][2]라고 보이시죠? [1]은 현재 페이지를 나타내고 [2]는 다음페이지를
나타냅니다.

[1] 에는 가역부호화에 대한 이야기를..
[2] 에는 비가역부호화에 대한 이야기를 담았습니다.

가역 부호화와 비가역 부호화의 의미
   우선은 부호화와 복호화 라는 말을 알아볼 필요가 있을것 같다. 부호화 복호화 하니까 암호학에서 사용하는
   용어로 생각을 하는 분들이 계실거 같은데 영상데이타에서 사용하는 의미도 비슷하다고 생각을 하면 된다.

   부호화라고 하는 것은 "원문"을 암호문으로 바꾸는 것을 말하고, 복호화라는 것은 "암호문"을 원문으로
   바꾸는 것을 의미하듯이 영상데이타에서도 원래의 이미지를 부호화 하게 되면 어떤 일정한 형태로 변형이
   됨을 의미하고 그 변형된 일정한 형태를 복호화하게 되면 원래의 이미지로 변환이 되는 것을 의미하는 것이다.

   여기서 가역 부호화와 비가역 부호화라는 말이 나오게 되는데..

   가역부호화라고 하는 것은 무손실 부호화를 의미하는 것으로 원래의 영상이미지를 복호화 했을때 영상이
   원래의 이미지와 똑 같은 형태로 복호화가 가능하지만..

   비가역부호화는 그 반대의 경우가 되는 것이다. 즉, 비가역 부호화를 수행한 이미지를 복호화 했을때
   원래의 이미지와는 똑 같은 원본의 형태를 얻어 낼수는 없더라도 비슷하게는 얻어 낼수 있다는 것을 의미한다.

   흔히들 우리는 JPEG이미지 파일에 대해서 이야기할때 이 이미지 파일은 원본이미지와 똑 같은 이미지로
   저장이 되는 형태가 아니라고 말하곤 하는데.. 이 이미지 같은 경우가 비가역부호화의 대표적인 예라고
   할 수 있다.

   가역 부호화 방법에서 여러가지 알고리즘이 있는데 그중에서 대표적인 것이 run length 부호화, DPCM,
   가역 길이 부호화라고 하는 것이 있고, 비가역 부호화 방법에서도 역시 여러가지 알고리즘이 있게 되는데
   그중에서 대표적인 것이 DFT, DCT등이다. 위에서 잠시 이야기한 JPEG영상 이미지가 DCT알고리즘을
   사용하는 대표적인 예라고 하겠다. MPEG이미지의 경우에도 DCT알고리즘을 사용하고 있다.

   그렇다면 원래의 이미지 마져 회손하면서 까지 비가역부호화를 사용하는 이유는 무엇일까.

   바로 데이타의 압축률때문이다. 비가역부호화 알고리즘을 사용하면 데이타를 회손하면서까지 부호화를
   시키기 때문에 압축률이 상당히 높아지게 되지만 높아진 압축률만큼이나 손해보는 부분도 있는 것이다.

   반면 가역부호화의 경우 그 특성때문에 어떨땐 압축률이 높다가도 어떨땐 앞축률이 떨어지지만(뒤에서
   알아볼것임) 부호화된 데이타를 원 영상으로 복호화 했을때에서 원본이미지와 동일한 이미지를 획득할 수
   있기 때문에 가역 부호화 방법과 비가역 부하화 방법은 서로서로 장단점을 가진다고 할 수 있겠다.

  
   비가역부호화에 대해서 좀더 생각을 해보도록 하자.

   아래와 같이 (a) 와 (b)라고 하는 각각의 이미지 데이타가 있다고 가정을 했을때..
  
   우리는 비가역 부호화에 대해서 두가지 경우를 생각해 볼 수 있게 된다. 하나는 (a)라고 하는 데이타를
   (c)라고 하는 형식으로 부호화하는 방법과 (b)라고 하는 데이타를 (d)라고 하는 데이타로 부호화 하는
   경우이다.

   하나하나를 살펴보도록 하자.

   첫번째 : (a)  ->  (c)
     위의 그림만 보고는 무슨 의미인지 알기 어려울테니까 설명을 해보면.. 이경우에는 원본이미지에서
     일정한 간격마다 픽셀하나씩을 때내와서 (c)와 같은 형태로 부호화 한것을 의미하고.
   두번째 : (b)  ->  (d)
     두번째 경우는 (b)라고 하는 이미지 데이타 한 픽셀당 16비트의 색상을 가지고 있는 경우라고 하면
     이를 부호화한 (d)라고 하는 이미지 데이타는 한 픽셀당 8비트의 색상만을 가지고 있는 경우라고 할수 있다.

   두가지 경우 모두 우리가 원하는 비가역 부호화의 역할을 충실하게 해 냈지만 짐작하는 바와 마찬가지로
   치명적인 결함을 가지고 있음을 알 수 있다.

   첫번째 경우에는 이 데이타를 복호화 했을때 이미지 데이타를 원본과는 너무나 다른 이미지로 복호화 할
   소지가 많다는 것이다. 두번째 경우는 이미지 데이타 색상이 전체적으로 외곡되어 버리는 경우가 발생 할수
   있다.

   위에서 보여주고자 하는 것은 실제로 비가역 부호화를 수행할때 저렇게 단순한 알고리즘을 사용하지는 않는다
   라는 것을 보여주기 위함이다.

   실제로는 부호화를 시키긴 시키되.. 영상의 질을 가능한한 좋게 하면서 데이타 량을 줄이는 것이 중요하다고
   하겠다. 실제 부호화를 수행할때는 인간의 눈에 잘 띄지 않는 고주파 성분만을 감소시킨다든지 이미지 데이타중
   극 소수만이 가지고 있는 데이타를 삭제한다든지 하는 방식으로 부호화를 수행하게 되는 것이다.

이진 영상 부호화(run length coding)
   이진 영상 부호화는 흑과 백으로만 이루어진 이진 영상데이타를 부호화 하는 방법으로써 run length부호화
   라고 하는 것이 있는데 이 방법에 대해서 알아 보도록 하겠다.

   의미
     run length 부호화에 대한 의미를 알아보도록 하겠다.

     run이라는 말의 의미는 일정한 숫자가 연속해서 달리고(?) 있다는 의미이고 length는.. 얼마만큼의 길이로
     달리고 있는가를 의미한다.

     아래와 같은 흑과 백으로만 이루어진 이진 영상 데이타가 있다고 가정해 보도록 하자.

     00000100000001000000100000001    -> 총 29비트

     화면으로 보면 검은색줄이 5pixel동안 이어지다가 하나의 흰점이 있고, 이어서 검은점이 7개가 나오는 식으로
     이미지가 구성될 것으로 예상이 되는 이미지 이다. 이 숫자들을 쪼개보면 한 가지 색깔이 연속적으로 이어지는
     즉, run length하는 부분으로 쪼개 볼 수 있다.

     00000
     1
     0000000
     1
     000000
     1
     0000000
     1

     무조건 부호화 하게 되면 복호화 할때 문제가 발생하므로 일정할 형식을 만들어 두도록 하자.

    
     이 테이블을 바탕으로 쪼개노은 데이타를 부호화 하면..

     101 000 111 000 110 000 111 000 으로 총 24비트의 길이를 가지는 데이타로 압축이 가능해 집니다. 즉 부호화
     되는 것을 의미한다.

   실제로 비트수가 줄어든것은 5비트 밖에 되지를 않지만 실제의 데이타를 압축해보면 8:1정도의 압축률을 보인
   다고 하니 훌륭한 압축 알고리즘이라고 할 수 있다.

   흑과 백으로만 이루어진 데이타를 어디에 쓰겠냐고 생각할지 모르지만 실제로 팩스나 화일 압축등에도
   이러한 알고리즘이 사용되고 있다.

예측 부호화(DPCM)
   가역 부호화에 대해서 설명할때 DPCM이라고 하는 알고리즘도 있다고 했었는데 이것은 예측 부호화라고
   말하는 것으로 예측 부호화 알고리즘에 대해서 설명해 보도록 하겠다.

   개요
     가변 예측 부호화를 통한 압축 알고리즘 역시 개념은 간단하다.
     현재 픽셀에 위치한 이미지정보를 얻기 위해 그 전에 위치한 이미지 정보값을 이용해서 현재의 이미지
     정보값을 알아내는 원리이다.

     말로만 설명하면 어려우니 그림으로 설명을 해보도록 하겠다.

    
     이런 식으로 이루어진 그레이형 이미지 데이타(한 픽셀에 256 흑백 칼라가 있는 영상)가 있다고 가정해 보자.

     첫 픽셀의 값이 112 이고.. 그 바로 오른쪽에 위치한 픽셀의 값이 64이다. 하지만 두번째 픽셀의 값 64를
     알기 위해서 그 값의 정보를 가지고 있을 필요없이 112라고 하는 값의 정보 만을 가지고 그 값에 -68을 하면
     두번째 픽셀의 정보값 64를 얻어 올 수가 있는 것이다. 이것이 바로.. 정보값으로 두번째 픽셀의 값을 예측해
     낼 수 있는 예측 부호화라고 하는 알고리즘이다.

     바로 전 단계의 이미지 만으로 정보를 알아 낼 수도 있지만 바로 위에 위치한 정보값과의 비교를 통한
     예측도 가능해지고.. 또 왼쪽의 데이타값과 자신의 바로 위에 위치한 픽셀의 데이타값을 바탕으로
     자신의 데이타값을 알아 낼 수 있는 여러 방법을 예상 할 수 있게 된다.

     이러한 방식으로 생각해 봤을때 자주 쓰이는 예측 방법으로 아래와 같은 것들이 있게 된다.

     1. 자신의 데이타 값 <-> 왼쪽의 데이타값
     2. 자신의 데이타 값 <-> (왼쪽의 데이타값 + 위쪽의 데이타값)/2
     3. 자신의 데이타 값 <-> (위쪽의 데이타값 - 왼쪽위의 데이타값 + 왼쪽의 데이타값)
     4. 자신의 데이타 값 <-> 2(왼쪽의 데이타값) - 왼쪽으로 두번째 위치한 데이타의 값

   이런식으로 자신의 데이타 값을 저장해 두기만 하고 복호화 할때는 이 알고리즘을 다시 적용하게 되면
   원래의 이미지 데이타를 얻어 낼 수가 있게 된다.

   만들어 보기

/*****************************************/
/* 1번 방법을 사용한 부호화                */
/*****************************************/
void dpcm1(int line,short int bank[SIZE])
{
    int buf,cnt;
 
    buf = LEVEL;
    for(cnt = 0; cnt < SIZE; cnt++) {
        bank[cnt] = (int)image_in[line][cnt] - buf;
        buf = (int)image_in[line][cnt];
    }
}
/***************************************/
/* 1번 방법을 사용한 복호화              */
/***************************************/
void inv_dpcm1(int line, short int bank[SIZE])
{
    int cnt;
    unsigned char buf;
 
    buf = (unsigned char) LEVEL;
    for(cnt = 0; cnt < SIZE; cnt++) {
        image_out[line][cnt] = (unsigned char)(bank[cnt] + buf);
        buf = image_out[line][cnt];
    }
}

/**************************************/
/* 2번 방법을 사용한 부호화             */
/**************************************/
void dpcm2(int line,short int bank[SIZE])
{
    int buf,cnt;
 
    if(line == 0) /* if first line */
    {
         buf = VAL; /* initialize */
         for(cnt = 0; cnt < SIZE; cnt++)
         {
         /* current value is differential value between current data and previous data
         (subtraction) */
         bank[cnt] = (int)image_in[line][cnt] - buf;
         buf = ((int)image_in[line][cnt] + VAL)/2;
         }
    }
    else
    {
         for(cnt = 0; cnt < SIZE; cnt++)
         {
              if(cnt == 0) /* if first column */
                   buf = (VAL + (int)image_in[line-1][cnt])/2;
              else
                    buf = (int)(image_in[line][cnt-1] + image_in[line-1][cnt])/2;
              bank[cnt] = (int)image_in[line][cnt] - buf;
         }
     }
}
/***************************************/
/* 2번 방법을 사용한 복호화              */
/***************************************/
void inv_dpcm2(int line, short int bank[SIZE])
{
      unsigned char buf;
      int cnt;
 
      if(line == 0) /* if first line, */
      {
           buf = (unsigned char)VAL;
           for(cnt = 0; cnt < SIZE; cnt++)
          {
           /* current value is differential value between current data and previous data
           (addition) */
            image_out[line][cnt] = (unsigned char)bank[cnt] + buf;
            buf = (image_out[line][cnt] + (unsigned char)VAL)/2;
            }
       }
       else
       {
            for(cnt = 0; cnt < SIZE; cnt++)
            {
                 if(cnt == 0) /* if first line, */
                        buf = ((unsigned char)VAL + image_out[line -1][cnt])/2;
                 else buf = (image_out[line][cnt-1] + image_out[line-1][cnt])/2;
                 image_out[line][cnt] = (unsigned char) bank[cnt] + buf;
            }
        }
}

/**************************************/
/* 3번 방법을 사용한 부호화             */
/**************************************/
void dpcm3(int line,short int bank[SIZE])
{
       int buf,cnt;
 
       if(line == 0) /* if first line */
       {
             for(cnt = 0; cnt < SIZE; cnt++)
             {
                   if(cnt == 0) /* if first column */
                         buf = VAL;
                   else
                         buf = (int)image_in[line][cnt-1];
 
                   bank[cnt] = (int)image_in[line][cnt] - buf;
             }
        }
        else
        {
        /* current value is differential value between current data and previous data
        (subtraction) */
              for(cnt = 0; cnt < SIZE; cnt++)
              {
                     if(cnt == 0) /* if first column */
                          buf = (int)image_in[line-1][cnt] ;
                     else
                          buf = (int)( image_in[line-1][cnt]-image_in[line-1][cnt-1]
                                   +image_in[line][cnt-1]);
                     bank[cnt] = (int)image_in[line][cnt] - buf;
              }
        }
}
/***************************************/
/* 3번 방법을 사용한 복호화              */
/***************************************/
void inv_dpcm3(int line, short int bank[SIZE])
{
       int buf;
       int cnt;
 
       if(line == 0) /* if first line */
       {
              for(cnt = 0; cnt < SIZE; cnt++)
              {
                     if(cnt == 0) /* if first column */
                           buf = VAL;
                     else
                           buf = (int)image_out[line][cnt-1];
                     image_out[line][cnt] = (unsigned char)(bank[cnt] + buf);
              }
        }
        else
        {
         /* current value is differential value between current data and previous data
             (addition) */
             for(cnt = 0; cnt < SIZE; cnt++)
             {
                     if(cnt == 0) /* if first column */
                            buf = (int)image_out[line-1][cnt];
                     else
                            buf = (int)( image_out[line-1][cnt] - image_out[line-1][cnt-1]
                                     + image_out[line][cnt-1]);
                     image_out[line][cnt] = (unsigned char) (bank[cnt] + buf);
             }
         }
}

/**************************************/
/* 4번 방법을 사용한 부호화             */
/**************************************/
void dpcm5(int line,short int bank[SIZE])
{
        int buf,cnt;
 
 
        /* current value is differential value between current data and previous data
            (subtraction) */
        for(cnt = 0; cnt < SIZE; cnt++)
        {
               if(cnt == 0) /* if first column */
                      buf = VAL;
               else if(cnt == 1) /* if second column */
                           buf = 2*(int)image_in[line][cnt-1] - VAL;
               else
                           buf = 2*(int)image_in[line][cnt-1] - (int)image_in[line][cnt-2];
               bank[cnt] = (int)image_in[line][cnt] - buf;
        }
}
/***************************************/
/* 4번 방법을 사용한 복호화              */
/***************************************/
void inv_dpcm5(int line, short int bank[SIZE])
{
        int buf,cnt;
 
        /* current value is differential value between current data and previous data
            (addition) */
        for(cnt = 0; cnt < SIZE; cnt++)
        {
             if(cnt == 0) /* if first column */
                   buf = VAL;
             else if(cnt == 1) /* if second column */
                     buf = 2*(int)image_out[line][cnt-1] - VAL;
             else
                     buf = 2*(int)image_out[line][cnt-1]-
             (int)image_out[line][cnt-2];
             image_out[line][cnt] = (unsigned char)(bank[cnt]+buf);
        }
}

가변 길이 부호화(Huffman Coding)
   지금까지 DPCM에 대해서 알아 봤다. 하지만 이 알고리즘도 문제점을 가지고 있다.
   DPCM이라는 알고리즘은 예측한 값과 실제의 값과의 차이를 부호화하는 특징때문에 원래의 이미지가 비록
   0 ~ 255까지의 범위를 가지는 영상 이미지라 할지라도 DPCM알고리즘을 수행한 데이타는 -255 ~ 255
   까지의 범위를 가지는.. 결국은 데이타의 길이가 더 늘어나 버리는 결과를 초래하게 된다.

   하지만 DPCM알고리즘을 수행한 데이타를 잘 살펴보면 0의 값을 많이 가지고 있는 것을 알 수 있는데
   이는 이미지 데이타 자체가 반복된는 픽셀들을 가지고 있는 경우가 많기 때문에 자연히 이러한 현상이
   발생하는 것이다.

   이것을 이용하여 좀더 효율적인 압축 알고리즘을 수행할 수 있게 되는데.. 바로 자주 출현하는 숫자들에
   대해서는 짧은 데이타형으로 할당하고, 반대로 가끔씩 출현하는 숫자들에 대해서는 상대적으로 긴
   데이타 형을 할당함으로써 효율을 높이는 알고리즘을 만들수 있다는 것이다.

   이를 가변 길이 부호화 즉, Huffman Coding이라고 하는 것이다.

  

   원래의 데이타 값이 1, 11, 000, 0010, 0011이라고 있고 각각의 발생 빈도수를 확률로 계산해서 표시를
   해보니 각 40, 20, 20, 20, 10, 10%씩 되었다고 해보자.

   발생빈도수가 낮은것두개를 묶어서 20%의 확률이 되도록 만든다음에 기존의 000이라는 데이타값의
   끝을 inverse시켜서 001을 만든다.

   이번에는 또다시 발생활률이 낮은 000, 001을 묶어서 40%의 확률이 되도록 만든 다음에 기존에 있던
   11과 반대되는 00으로 할당을 하고.. 또다시 빈도수가 낮은 두개의 수를 합해서 60%로 만든다음에
   그 값을 inverse시킨 0으로 할당하는 식이다.

 

지금까지 가역 부호화에 대해서 알아 보았는데. 이것이 한가지만을 사용해서 해결이 될것이라고 생각하는것은
큰 오산이다. 일반적인 영상 데이터의 부호화는 다음과 같은 순서로 진행이 됨을 명심해야 한다.

1. 영상 데이터를 순서대로 주사시켜 각 점에서 예측값의 차를 구하고, DPCM 부호화를 행한다.
2. DPCM 데이터를 가변 길이 부호값으로 변환한다.
3. 가변 길이 부호화를 행한다.

복호화의 단계는 당연히 위의 과정의 반대가 될것이다.

1. 가변 길이 부호를 복호화한다.
2. 복호화한 가변 길이 부호값을 DPCM데이터로 변환하다.
3. DPCM 부호화를 할 때와 반대로 주사하면서 예측값과 DPCM데이터를 합하여, 영상 데이터를 순서대로

영상 데이타 압축 알고리즘 - 2

작성일 : 2002.2월말
[목차로가기]
[1]
[2][3] : 페이지 이동

순서.............................................................................................
   - JPEG이란?
   - 다른기술과 차별화 되는 JPEG의 압축기술
   - JPEG의 압축 방법
   - Baseline 압축에 대해 좀더 자세히!!
   - JPEG의 실제 압축과 복원과정 알아보기
   - 확장 JPEG에 대해서..
...................................................................................................

이제는 비가역 부호화에 대한 스터디를 수행하게 되는 군요. 저는 이 스터디가 마치는 즉시 영상데이타 압축
관련 프로젝트를 수행해야 합니다. 상당히 바쁜 나날을 요즘 보내고 있는데.. ㅠ.ㅠ 쓰러지기 일보직전입니다.
산학협력으로 수행되는데.. 이를 통해서 저 개인적으로도 상당히 업그레이드 되지 않을까 하는 희망으로
공부하고 있습니다. ^^

비가역 부호화를 사용하는 정지영상 이미지 데이타 포맷으로 대표적인 것이 JPEG이 있습니다.
JPEG이미지 데이터는 아시는 바와 같이 비가역 부호화 방식을 이용, 즉 손실압축 기법을 사용합니다.

BMP파일로 저장된 이미지를 JPEG이미지로 바꾸게 되면 원본의 이미지 보다 선명하지 못한 이미지를 얻게 되는데
실제로는 눈으로 봐도 그렇게 선명하지 못하다고 생각이 안들정도로 선명함을 느낄 수 있습니다.

이러한 JPEG이미지가 DCT압축 알고리즘을 사용한다고 할 수 있습니다. JPEG이미지 데이타를 분석하면서
DCT압축 알고리즘과 Visual C++언어에서 이를 어떤 식으로 인코딩하고 디코딩 할 수 있을지를 알아 보도록
하겠습니다. 재밌지만 상당히 어려운 부분이네요..

JPEG이란?
   1982년, 국제 표준화 기구 ISO(International Standard Organization)는 정지 영상의 압축 표준을 만들기 위해
   PEG(Photographic Exports Group:영상 전문가 그룹)을 만들었다. PEG의 목표는 ISDN을 이용하여 정지 영상을
   전송하기 위한 고성능 압축 표준을 만들자는 것이 주 목적이 되어 이를 수행하게 된것이다.

   1986년 국제 전신 전화 위원회 CCITT(International Telegraph and Telephone Consultative Commitee)에서는
   팩스를 이용해 전송하기 위한 영상 압축 방법을 연구하기 시작하였다. CCITT의 연구 내용은 PEG의 그것과 거의
   비슷하였기 때문에 1987년 이 두 국제 기구의 영상 전문가가 연합하여 공동 연구를 수행하게 되었고, 이 영상
   전문가 연합을 Joint Photographics Expert Group이라고 하였으며, 이것의 약자를 따서 만든 말이 바로
   JPEG이다. 1990년 JPEG에서는 픽셀당 6비트에서 24비트를 갖는 정지 영상을 압축할 수 있는 고성능 정지 영상
   압축 방법에 대한 국제 표준을 만들어 내게 되었다. 후에 JPEG에서는 만든 압축 알고리즘을 이용한 파일 포맷이
   만들어 지게 되고 이것이 오늘날까지 오게 된것이다.

다른 기술과 차별화 되는 JPEG의 압축기술
   GIF파일 포맷에 대해서 먼저 알아보기로 한다. 이 영상이미지 데이터는 최대 256컬러 영상까지만 저장할 수
   있었기 때문에 실세계의 이미지와 같은 것들을 저장하는데 한계가 있다. 지금은 트루컬러까지 모니터에서
   지원이 되는데 이를 다른곳에 응용하기에는 무리가 있었던 것이다.

   GIF파일에서 사용하는 알고리즘을 LZW라고 하는데 이는 이를 개발한 Abraham Lempel과 Jakob Ziv이고
   이를 개선시킨 Terry Welch등 세사람의 이름을 따서 만든 압축 알고리즘으로 press, zoo, lha, pkzip, arj등과
   같은 우리가 잘 알고 있는 프로그램에서 널리 사용되는 것이다.
 
   이 압축 방법의 특징은 잡음의 영향을 크게 받기 때문에 애니메이션이나 컴퓨터 그래픽 영상을 압축하는데는
   비교적 효과적이라고 할 수 있었지만, 스캐너로 입력한 사진이나 실세계의 이미지 같은 경우에 이를 압축하는데는
   효과적이지 못하다고 평가되고 있다.

   이에 비해 TIFF나 BMP등의 파일 포맷은 24비트 트루컬러까지 지원하여 시진등의 이미지를 잘 표현해 낼 수
   있지만 압축 알고리즘 자체가 LZW, RLE등의 방식을 사용하였으므로 압축률이 그렇게 좋지 않다는 단점이
   있다.

   이에 반해 현재의 JPEG기술은 사진과 같은 자연 영상을 약 20:1이상 압축할 수 있는 성능을 가지고 있어서
   현재 사용되고 있는 정지 영상 파일 포맷중에서는 최고의 압축률을 자랑하고 있다.

   하지만 장점이 있으면 단점도 존재하는법.. 단점이라면 기존의 영상 파일을 영상을 압축하는 시점에서 영상의
   일부 정보를 손실 시키기 때문에 의료 영상이나 기타 중요한 영상 혹은 자연 영상등에는 사용하는데 무리가
   있다. 즉, GIF, TIFF등의 영상 파일은 영상을 압축한 후 복원하면 압축하기 전과 완전히 동일한 무손실 압축
   방법이지만 JPEG이미지 포맷의 경우 손실 압축방법이라는 것이다.  하지만 손실이 된다고 해도 원래의 이미지와
   그렇게 다르지 않은(거의 동일한) 이미지를 얻을 수 있기 때문에 영상 정보가 중요한 부분이 아니라면 효율적인
   방법이라고 할 수 있다.

   이 부분에 대해서 좀도 구체적으로 설명을 해보면..

   JPEG이 압축을 대상으로 삼는 사진과 같은 자연의 영상이 인접한 픽셀간에 픽셀값이 급격하게 변하지 않는다는
   속성을 이용하여 사람의 눈에 잘 띄지 않는 정보만 선택적으로 손실 시키는 기술을 사용하고 있기 때문이다.

   위에서 이야기한 단점에 첨가하여 또다른 문제가 있다. 인접한 픽셀간에 픽셀 값이 급격히 변하는 컴퓨터 영상이나
   픽셀당 컬러 수가 아주 낮은 이진 영상이나, 16컬러 영상 등은 JPEG으로 압축하게 되면 오히려 압축 효율이
   좋지 않을 뿐더러 손실된 부분이 상당히 거슬려 보인다는 것이다.

   즉, 다른 이미지 압축 기술과 차별화 되는 신기술임에는 분명하지만 사용목적에 따라서 적절한 압축 알고리즘을
   사용하는 것은 기본이라 하겠다.

JPEG의 압축방법
   JPEG압축 알고리즘을 사용했다고 해서 이게 단 한가지의 압축 알고리즘만이 존재한다는 의미가 아님을
   알고 있어야 합니다. 다음과 같이 JPEG압축 알고리즘은 크게 네부분으로 나누어 볼 수 있습니다.

   1. DCT(Discrete Cosine Transform) 압축 방법
     일반적으로 JPEG영상이라고 하면 통용되는 압축 알고리즘이다.
   2. 점진적 전송이 가능한 압축 방법
     영상 파일을 읽어 오는 중에도 화면 출력을 할 수 있는 것을 의미하며 전송 속도가 낮은 네트워크를 통해
     영상을 전송 받아 화면에 출력할때 유용한 모드라고 할 수 있다.

     즉, 영상의 일부를 전송받아 저해상도의 영상을 출력할 수 있으며, 영상 데이터가 전송됨에 따라서 영상의
     화질을 개선시키면서 화면에 출력이 가능하다는 것이다.
   3. 계층 구조적 압축 알고리즘
     피라미드 코딩 방법이라고도 하며, 하나의 영상 파일에 여러가지 해상도를 갖는 영상을 한번에 저장하는
     방법이다.
   4. 무손실 압축
     JPEG압축이라고 하여 손실 압축만 존재하는 것은 아니다. 이 경우에는 DCT압축 알고리즘을 사용하지
     않고 2D-DPCM이라고 하는 압축방법을 이용하게 된다.

   이처럼 JPEG표준에는 이와 같은 여러가지 압축 방법이 규정되어 있지만, 일반적으로 JPEG로 영상을 압축하여
   저장한다고 하면, DCT를 기반으로한 압축 저장방법을 의미 한다.

   이러한 방법을 또다른 용어로 Baseline JPEG이라고 하며, JPEG영상 이미지를 지원하는 모든 애플리케이션은
   이 이미지 데이타를 처리할 수 있는 알고리즘을 반드시 포함하고 있어야 한다.

   즉, 나머지 3가지의 압축 방법을 꼭 지원하지 않아도 되는 선택사항이라는 의미이다.

   Baseline 압축 알고리즘에 대해서 좀더 자세하게 살펴보도록 하자.

   이 방법은 유손실 압축 방법이기 때문에 영상에 손실을 많이 주면 화질이 안 좋아지는 대신 압축이 많이 되고,
   손실을 적게 주면 좋은 화질을 유지하기는 하지만 압축이 조금밖에 되지 않는다는 것이다. 이처럼 손실의 정도를
   나타내는 값을 Q펙터라고 말하는데 이 값의 범위는 1부터 100까지의 값으로 나타나게 된다. Q펙터가 1이면
   최대의 손실을 내면서 가장많이 압축되는 방식이고 100이면 이미지 손실을 적개 주기는 하지만 압축은 적게
   되는 방식이다.

   Q펙터가 100이라고 하여 무손실 압축이 이루어 지는 것은 아니라는데 주의할 필요가 있다.

Baseline JPEG에 대해 좀더 자세히!!
   베이스라인 JPEG은 JPEG압축 최소 사양으로, 모든 JPEG관련 애플리케이션은 적어도 이 방법을 반드시
   지원해야 한다고 했다. 이러한 방식이 어떤 단계를 거치면서 수행되게 되는지 알아보도록 하자.

   1. 영상의 컬러 모델(RGB)을 YIQ모델로 변환한다.
   2. 22 영상 블록에 대해 평균값을 취해 색차(Chrominance)신호 성분을 다운샘플링한다.
   3. 각 컬러 성분의 영상을 88크기의 블록으로 나누고, 각 블록에 대해 DCT알고리즘을 수행시킨다.
   4. 각 블록의 DCT계수를 시각에 미치는 영향에 따라 가중치를 두어 양자화 한다.
   5. 양자화된 DCT계수를 허프만 코딩(Huffman Coding)방법에 의해 코딩하여 파일로 저장한다.

   이렇게 압축된 파일을 다시 원 이미지로 복원할때는 반대의 과정을 거치게 된다.
   이러한 압축과 복원에 관해 어떤 식으로 처리가 되는지 그림으로 살펴보면..

  

   각각의 과정들에 대해서 자세하게 알아보도록하자. 아참!! 덤으로 이야기 하자면 지금 보고 있는 위의 이미지가
   JPEG의 Baseline JPEG기법을 사용해서 저장된 이미지 이다. ^^

   혹시라도 지금까지 쭉 따라온 사람이 있다면 이 이미지를 다운 받아서 선과 글자만 추출해 보시기 바랍니다.
   쉽습니다. 막 알고리즘이 생각나질 않나요? ^^


   1) 컬러모델 변환

     컬러를 표현하는 방법에는 여러가지가 있다. 가장 흔하게 사용하는 방법이고, 프로그래머라면 안들어 봤을리
     없는 Red, Green, Blue이다. 이 색들의 조합을 RGB칼라라고 한다. 하지만 이러한 표현방법이 이것 뿐이라면
     좋겠지만 실제로는 그렇질 않다는 것이다.

     RGB컬러는 모니터에서 사용하는 색상이고 빛의 3원색을 조합했을때 나오는 색도 세가지인데 이들은
     하늘색(Cyan), 주황색(Magenta), 노랑색(Yellow)이고, 이들의 조합으로도 모든 컬러를 표현 할 수 있게 된다.
     이러한 방법을 CMY모델이라고 하며, 컬러 프린터가 이모델을 이용해서 프린팅을 하게된다.

     우리가 논의 할려고 하는 YIQ라고 하는 모델은 밝기(Y:Luminance)와 색차(Chrominance:Inphase&Quadrature)
     정보의 조합으로 컬러를 표현하는 방법이다.

     다른 방법도 있다. 색상(Hue), 채도(Saturation), 명도(Intensity)의 색의 3요소로 색을 표현하는 HSI모델등
     여러가지 컬러 모드가 있는 것이다.

     RGB모델은 YIQ모델로 변환하는 방법이 있는데.. 이른 각각의 모델들도 서로 변환이 될 수 있다.
     RGB를 YIQ모델로 변환하는 식은 다음과 같다.

    

     이와 같은 식을 이용해서 JPEG압축을 하기 위해서는 컬러 모델을 YIQ모델로 변환을 한다.
     많은 모델중에서 이 모델로 변환을 하는 이유는 이중에서 Y성분은 시각적으로 눈에 잘 띄는 성분이지만
     I, Q성분은 시각적으로 잘 띄지 않는 정보를 담고 있는 성질이 있어서, Y값만을 살려두고 I, Q값을 손실시키면
     사람이 봤을때에는 화질의 차이를 별로 느끼지 않으면서 정보를 양을 줄일 수 있는 장점이 있기 때문이다.

   2) 색차 신호 성분 다운샘플링
     앞에서도 이야기 했던 바와 같이 I와 Q의 성분은 시각적으로 눈에 잘 띄지 않는 정보들이기 때문에 이정보는
     손실을 시켜도 사람이 보는데 특별한 지장을 주지 않는다.

     손실을 시킨다는 의미이지 지워버린다는 의미는 아니다. 즉, Y값은 기억시키고, I, Q값은 가로 세로 2x2혹은
     2x1크기를 블록당 한개만을 기억시키는 방식으로 정보만을 줄인다는 개념이다.

     즉, 두번째 단계인 지금은 컬러모델을 변환한것을 다운" 샘플링 한다는 것이다.

   3) DCT적용
     여기서 DCT라고 하는 알고리즘을 설명해야 하는데.. 왜 이부분을 먼저 설명하지 않고 JPEG에 대해서 먼저
     스터디를 하는가에 대해서 의문을 가질 분이 혹시라도 계실텐데.. 실제로 눈에 보이는 그나마 간단한 설명을
     살펴보고 복잡한 DCT알고리즘을 논하는 것이 이해하기 편하기 때문입니다.


     JPEG알고리즘을 적용할 이미지 영상 블록에 어떤 주파수 성분이 얼마만큼 포함되어 있는지를 나타내는
     8x8크기의 계수를 얻을수 있게 된다. 픽셀간의 값의 변화율이 작은 밋밋한 영상은 저주파 성분을 나타내는
     계수가 크게 나오게 되고, 픽셀간의 변화율이 큰 복잡한 영상은 고주파 성분을 나타내는 계수가 크게 나온다.
    
     컬러를 표시하기 위한 각각의 YIQ성분은 8x8크기의 블록으로 나뉘어지고, 각 블록에 대해 DCT가 수행이 된다.

     DCT는 Discrete Consine Transform의 약자로 영상 블록을 서로 다른 주파수 성분의 코사인 함수로 분해하는
     과정을 일컷는다.

    

     왼쪽에 보이는 영상데이터에 DCT를 수행하면 오른쪽 DCT계수가 얻어지게 된다.

     이처럼 DCT를 수행하는 이유는 영상데이터의 경우 저주파 성분은 시각적으로 큰 정보를 가지고 있는 반면
     고주파 성분의 경우는 시각적으로 별 의미가 없는 정보를 가지고 있기 때문에 시각적으로 적은 부분을 손실을
     줌으로써 시각적인 손실을 최소화하면서 데이터량을 줄이기 위한 것이다.

   4) DCT 계수의 양자화
     하지만 이론적으론 DCT자체만으로는 영상에 손실이 일어나지 않으며, DCT계수들을 기억하고 있으면
     DCT역 변환을 통해 원 영상을 그대로 복원해 낼 수 있다. 실제로 영상에 손실을 주며, 데이터량을 줄이는 부분은
     DCT계수를 양자화 하는 바로 이 단계에서 이다.

     계수 양자화란 여러개의 값을 하나의 대표값으로 대치시키는 과정을 말한다. 예를들어 0에서 10까지의 값은
     5로 대치시키고 10에서 20까지의 값은 15로 대치시키면 0부터 20까지의 값으로 분포되는 수많은 수들을 5와
     15라는 두개의 값으로 양자화시킨것이 된다. 이처럼 양자화과정을 거치면 기억해야 할 수많은 경우의 수가
     단지 몇개의 경우의 수로 축소되기 때문에 데이터에 손실이 일어나지만 데이터량을 크게 줄이는 장점이 있다.

     양자화를 조밀하게 하면 데이터의 손실이 적어지는 대신 데이터량은 그만큼 조금 줄게 되고, 양자화가
     성기면 데이터의 손실은 많아지는 대신 데이터량은 그만큼 많이 줄게 됩니다.

     저주파 영역을 조밀하게 양자화하고 고주파 영역은 성기게 양자화하면 전체적으로 영상의 손실이 최소화
     되면서 데이터량의 감소를 극대화 시킬 수 있게 된다.

     이처럼 주파수 성분별로 어느정도 간격으로 양자화를 하느냐에 따라 데이터 이미지의 질이 결정이 되는데
     ISO에서는 실험적으로 결정한 양자화 테이블을 이용하여 양자화를 수행하는게 통상적이다.

    

     영상의 화질과 압축률을 결정하는 변수인 Q펙터가 작용하는 부분도 바로 이단계로. Q펙터를 크게하면
     전체적으로 양자화를 조밀하게 해서 손실을 줄임으로써 영상의 화질을 좋게 하고, Q펙터를 크게하면
     전체적으로 양자화 간격을 넓혀 화질에 손상을 많이 주어서 압축이많이 되도록 하게 된다.

   5) 허프만 코딩(Huffman Coding)
     양자화된 DCT계수는 자체로서 압축 효과를 갖지만 이를 더 효율적으로 압축하기 위해서 허프만 코딩으로
     다시 한번 압축하여 파일에 저장을 한다. 허프만 코딩은 무손실 압축방법의 하나임을 스터디 한적이 있으니
     참고하기 바란다.

     다시한번 허프만코딩에 대해서 설명을 하면 자주 나오는 심벌에는 짧은 코드를 할당하고, 어쩌다 한번나오는
     심벌에는 긴 코드를 할당함으로써 전체적으로 코딩되는 양을 줄이자는데 목적이 있다.

JPEG의 실제 압축과 복원과정 알아보기
   지금까지 영상데이터가 인코딩되는 과정을 단계적으로 알아보았고, 이제부터는 각 단계별로 JPEG이미지가
   어떻게 압축과 복원과정을 거치는지 좀더 실질적으로 알아보도록 한다.

  

   실제 8x8영상 블록의 Y값(우)과 압축 후 이를 다시 복원했을때의 값(좌)를 나태내는 그림이고 0부터 255까지의
   값을 가지고 있습니다.

  

   모든 픽셀값에서 128을 빼서 128에서 127까지의 값을 갖도록 한 후, DCT를 수행한 후 얻어진 DCT계수(좌)
   와 양자화된 계수를 역양자화한 값(우)을 나태냅니다.

  

   양자화 테이블을 이용하여 양자화한 결과를 나타냅니다. 이미지가 한결 간단해 졌죠?

확장 JPEG에 대해서...
   베이스라인 JPEG은 JPEG에 필요한 최소의 기능만을 규정한 것이라고 설명을 했다. 이 외에도 JPEG내에는
   많은 압축 방법이 존재한다. 확장 JPEG의 기능은 반드시 지원할 필요는 없지만, JPEG파일 내에서 사용될 수
   있으므로 확장 JPEG의 기능을 일단 인식은 할 수 있어야 하고, 지원되지 않는 기능이 파일에 들어 있을 경우
   에는 에러메시지를 출력하도록 하여야 한다.

영상 데이타 압축 알고리즘 - 3

작성일 : 2002.2월말
[목차로가기]
[1][2]
[3] : 페이지 이동

순서............................................
   - JPEG의 파일구조
   - JPEG의 전체구조
   - JPEG의 마커코드와 구조들
..................................................

실제로 JPEG에 대한 이미지 정보나 자료들은 어떤 책에서든지 내용이 똑 같습니다. ㅠ.ㅠ
심지어 이미지 프로세싱에 대한 내용도 똑 같습니다. 단지 자료를 많이 수집해서 역었는가 아니면 자료가 조금
적게 수집해서 묶었는가의 자이지요.
선지자가 먼저 쓴 내용들을 다른 책에서도 그대로 이용을 하고, 이용된 자료들이 또 다른 형태로 다른책으로
나오고 하는 식으로 되다보니 내용이 똑 같을 수 밖에 없다고 생각합니다. 물론 선지자는 원서를 번역해서 옮겨
두었거나 스펙을 참고 했겠죠.

여기에서도 그러한 여러가지 서적을 참고하기는 했지만 나름대로 간편하게 설명하려고 노력을 하였습니다.
실제 코딩에 관한 부분은 다음으로 미루어야 할것 같습니다. 이정도를 완벽하게 이해하고 있다면 그러한
소스코드를 분석할때 많은 정보가 될것입니다.

JPEG의 파일구조
   우선은 JPEG파일을 입출력하기 위해서는 JPEG의 파일구조에 대해서 알 필요가 있다.

   JPEG파일에 있는 데이터를 항목별로 나누어보면 프레임, 스캔, 테이블 등으로 나눌수 있다.
   각 구조체의 시작은 마커코드라고 하는 것으로 알아 볼 수 있는데 마커코드는 다음과 같은 형태로 이루어져 있다.

 

마커 코드

   드   값

   용

사 용  여 부

 

SOF0

FFC0

Baseline DCT 프레임

O

 

SOF1

FFC1

확장 방식 시퀀셀 DCT 프레임

X

SOF2

FFC2

점층적 전송방식 프레임

X

SOF3

FFC3

공간적 예측 무손실 프레임

X

SOF5

FFC5

차분 영상 DCT 부호화 프레임

X

SOF6

FFC6

차분 영상 점층적 전송 방식 프레임

X

SOF7

FFC7

차분 영상 공간적 예측 프레임

X

DHT

FFC4

허프만 테이블

O

DAC

FFCC

산술 부호화 테이블

X

RST

FFD0 ~ FFD7

DRI로 지정되는 간격마다 삽입되는 마커, MCU재시작

O

SOI

FFD8

영상 전체의 처음

O

EOI

FFD9

영상전체의 끝

O

SOS

FFDA

스캔 데이터 시작

O

DQT

FFDC

양자화 테이블

O

DNL

FFDC

영상의 라인수

X

DRI

FFDD

RST(재시작 간격)

O

DHP

FFDE

계층적 부호화 표시

X

EXP

FFDE

화소수를 가로, 세로 두배 확대

X

RES

FF02 ~ FFBF

사용 불가(Reserved)

X

APP

FFE0 ~ FFEF

응용 프로그램에서 자유롭게 사용가능한 공간

X

JPG

FFF0 ~ FFFD

미래의 JPEG 규약으로 사용하기 위해 예약된 코드

X

 

 

 

 

 

   즉, 마커코드가 FFC0일 경우에는 Baseline DCT프레임이라는 것을 알려주는 식이다.

   JPEG이미지 데이터의 전체적인 구조는 다음과 같다.
  
  

   첫번째 테이블은 JPEG이미지의 전체적인 모습을 나타내고.. 밑에 있는 테이블 일 수록 상세하게 분류하여
   보여주고 있는 부분들이다.

   SOI라고 하는 부분은 Start of Image라는 뜻으로 영상 전체의 시작점이라고 할 수 있다. 그다음으로
   TABLES라고 하는 부분은 영상정보를 다시 복원하고 싶을 때 사용하는 테이블 정보이다.
   EOI는 영상전체의 끝부분을 나타내고 있는 것이구요.

   중간에 FRAME이라고 하는 부분은 Baseline DCT방법을 사용해서 JPEG이미지를 출력할 경우에는 허프만
   테이블과 양자화 테이블이 필요하게 된는데 프레임은 일반적으로 하나의 Frame만을 사용하고 다른 방식의
   JPEG의 경우에는 하나 이상을 사용되는 경우도 있다.

   FRAME안에는 영상 데이터 정보가 들어가게 된다. 여러개의 SCAN DATA라고 하는 부분이 보이는데
   이 부분은 스캔정보와 여러개의 MCU라고 하는 블록을 가지고 있다.

   지금까지는 파일의 구조에 대한 부분이었다 다음으로는 JPEG의 전체적인 구조에 대해서 알아본다.

JPEG의 전체구조
   JPEG의 파일 구조라고 위에서 설명을 하긴 했었지만 이것이 위와 같은 순서대로 구성이 되어 있는 것이
   아니라는데 특징이 있다.

   JPEG에서는 마커코드가 있다라고 설명을 했었는데, 이 마커코드 다음에 어떤 값이 오면 그 값은 어떤 내용이다
   라고 하는 식으로 JPEG의 구조를 기술하고 있기 때문이다.

   결론적으로 마커코드를 가지고 있지 않는다면 그 값은 애플리케이션에서 지원을 하지 않는다는 이야기가
   되는 것이다.

   즉, 우리가 애플리케이션을 작성할때는 가장 기본이 되는 부분은 기본으로 구현을 해주어야 하고, 추가적인
   구현이 필요하다면 또 구현을 해내면 된다.

   순서대로 찾을 필요가 없으므로 프로그래밍을 하기에 그리고 JPEG의 구조를 이해하는데 그리 어려움이 없다는
   생각도 스치기는 하지만(?)... 암튼 JPEG에서는 이러한 마커코드가 상당히 중요한 역할을 담당하게 된다.

   마커코드에 대해서 좀더 심층적으로 분석해 나가는 과정을 가져보도록 하자.

JPEG의 마커코드와 구조들
   JPEG 포맷은 마커코드가 시작점이고, 이 마커코드 다음부터 그 마커코드에 해당하는 정보에 대한 데이터가
   들어가게 된다.

   마커코드 자체로 구조를 가지고 있는데 그 구조에 대해서 알아보도록 하자.  

 

마커코드(2바이트)

마커 블록의 길이(2바이트)

데이터(최대 65533바이트까지 저장 가능)

   마커코드는 2바이트로 할당이 되어 있다. 마커 코드값들이 FFC4와 같은 형태 였으므로 당연히 2바이트를 할당
   해야 한다. 그다음으로 마커 블록의 길이가 2바이트로 할당이 되어 있는데 이는 뒤에 따라오는 데이터의
   길이를 나타내는 것이다.

   즉, 마커코드가 FFC4이고, 다음의 값이 64라면 허프만테이블이며, 데이터 길이는 64바이트라는 것을 의미하는
   것이다.

   마커코드마다 공통점이 있는데 첫번째 바이트는 항상 FF로 시작한다는 것이다. 하지만 두번째 코드는 00또는
   FF로 할당을 하지 않는데 이 이유는 FFFF, FF00이라고 하는 이 두가지의 코드는 허프만 부호에서 사용하고
   있기 때문에 그렇게 하는 것이다.

   1) 프레임 헤더
    

     프레임 마커는 FFC0 ~ FFCF까지이다. 이 마커는 혀재 프레임이 어떤 방식으로 저장이 되는 가를 나타내는
     부분이고 여기서 사용하는 프레임 마커는 FFC0이기 때문에 Baseline DCT를 사용한다는 것을 의미한다.

     그러한 프레임 마커의 구조는 위와 같은 형태를 가지고 있다.

     SOF는 프레임 시작 마커코드를 나타내는데 BaselineDCT의 경우는 FFC0의 값이 들어 있게 된다.
     Lf는 프레임의 헤더 길이를 나타내는 것이며, P는 영상의 샘플 양자화 비트수이다, Y는 영상의 세로크기
     X는 영상의 가로크기, Nf는 프레임을 구성하는 컴포넌트의 갯수이며 C는 컴포는트 번호를 의미한다.
     H는 수평 샘플링 인자, V는 수직 샘플링 인자, Tq는 양자화 테이블 번호를 나타낸다.

   2) 스캔 헤더
    
    
     SOS는 스킨의 마커코드를 나타낸다. 값은 당연히 FFDA가 들어가게 되고 Ls는 스캔 헤더 블록의 길이를
     Ns는 스캔 컴포넌트의 수를 나타내며, Ss는 스팩트럴 선택 방식의 시작부분을 Se는 스팩트럴 선택 방식의
     끝부분을 Ah, ai는 연속 근사 방식시 비트 위치를 표시하는 상위 정보와 하위정보를 나타내며
     Cs는 컴포넌트의 번호를, Td는 DC계수 엔트로피 부호 테이블 번호, Ta는 AC계수 엔트로피 부호 테이블
     번호를 나타내게 된다.

     정리하면 스캔헤더에는 데이터의 부호화에 관련된 코드정보가 들어가게 되는 것이다. 스캔 데이터 이후부터는
     실제 영상 정보가 MCU블록 단위로 등록되어 있다.

   3) 양자화 테이블
     양자화 테이블은 마커코드 다음 양자화 테이블 번호의 정보가 설정되어 있으며, 그 구조는 다음과 같다.
 

 

Code Name

DQT

Lq

Pq

Tq

양자화 테이블

 

byte

2

2

0.5

0.5

64

     양자화 테이블에 대해 설명을 했었는데.. 그 양자화 테이블 이라는 것은 64개의 데이터가 연속적으로 들어가
     있는 형태임을 기억할 수 있을 것이다. Pq가 1일 경우에는 이 64개의 데이터가 각각 2바이트씩 설정되며,
     0의 경우에는 1바이트씩 설정이 되는 것이다.

     DQT는 양자화 테이블 마커코드라고 설명을 했고, Lq는 테이블 헤더 정의부의 길이를, Pq는 양자화 테이블의
     각 값의 비트수가 0일 경우 8비트, 1일 경우 16비트.. Tq는 양자화 테이블 번호를 나타낸다.

   4) 허프만 테이블   

 

Code Name

DHT

Lh

Th

허프만 부호 길이

허프만 부호

 

byte

2

2

1

16

64

     허프만 테이블에는 허프만 부호의 길이와 허프만 부호가 설정되어 있다. 이 두개의 데이터를 이용해서 영상
     정보의 원래의 데이터를 복구하는데 사용되는 것이다.

     DHT는 허프만 메커인 FFC4가 들어가게 되고, Lg는 허프만 테이블 헤더의 길이가, Th는 허프만 테이블 번호가
     들어가게 된다.

 

'알고리즘 > 이미지 처리' 카테고리의 다른 글

GDI+에서의 투명색 처리...  (0) 2008.01.25
이미지 회전 알고리즘  (0) 2008.01.18
Image Processing  (0) 2008.01.18
그림 띄우기  (0) 2008.01.07
[본문스크랩] BMP(비트맵) 파일  (0) 2007.09.06
BMP 에 관해서  (0) 2007.07.08

+ Recent posts