OZ1NG의 뽀나블(Pwnable)

[BMP] BMP 파일 포맷 - GrayScale BMP 본문

Laboratory

[BMP] BMP 파일 포맷 - GrayScale BMP

OZ1NG 2022. 6. 21. 04:33

[*] 바이너리 파일을 BMP로 바꾸어 머신러닝 CNN로 돌리기 위해, 그리고 만드는 BMP 구조를 내 입맛대로 바꾸기 위하여 BMP 파일 구조를 공부하게 되었다.

[그림0] - 변환된 GrayScale BMP 이미지 (살짝 무섭ㄷ..)

[*] GrayScale BMP?

BMP파일의 여러 종류 중 하나로 이미지를 흰색, 검은색, 흰색으로 표현한다.
(흔히 아는 흑백 그림 같은 느낌인데 흑백으로 말하지 않는 이유는 진짜 흑과 백으로만 표현하는 BMP 종류가 있기 때문이다.)

한 픽셀을 이미지의 밝기 정보로 사용한다는 특징이 있다.

 

[*] GrayScale BMP 구조

GrayScale BMP는 기본적으로 Bitmap File Header, Bitmap Info Header, RGBQUAD, 실제 이미지 데이터(Pixel 데이터)로 구성된다.

또한 모든 필드들의 바이트 오더는 Little Endian이다.

한 픽셀은 8bit(1바이트)로 표현한다.

미리 모든 색을 RGBQUAD에 정의해두고 픽셀 데이터(1byte)는 RGBQUAD의 인덱스로 사용한다는 특징이 있다.

 

[+] BITMAPFILEHEADER

BITMAPFILEHEADER는 이름처럼 비트맵 파일의 헤더이다.
BMP 파일의 종류에 상관 없이 공통적으로 들어가 있고 항상 14byte의 크기를 갖는다.

 

BITMAPFILEHEADER 구조체는 다음과 같다.

typedef struct tagBITMAPFILEHEADER {
  WORD  bfType;
  DWORD bfSize;
  WORD  bfReserved1;
  WORD  bfReserved2;
  DWORD bfOffBits;
} BITMAPFILEHEADER, *PBITMAPFILEHEADER;

- bfType : BMP의 시그니처 부분

  + 크기 : 2byte

  + 값 : 0x424d("BM") - 고정

- bfSize : BMP 파일 전체 크기

  + 크기 : 4byte

  + 값 : 유동적

- bfReserved1 : 예약된 공간

  + 크기 : 2byte

  + 값 : 0x0(0) - 고정

- bfReserved2 : 예약된 공간

  + 크기 : 2byte

  + 값 : 0x0(0) - 고정

- bfOffBits : 비트맵 데이터(픽셀 데이터)의 시작 위치

  + 크기 : 4byte

  + 값 : 0x436(1078) - 고정

 

[+] BITMAPINFOHEADER

BITMAPINFOHEADER는 비트맵 파일의 실질적인 정보들에 대한 헤더 파일이다.

해당 부분은 Windows Version 3부터 사용되고 만약, OS/2라면 BITMAPCOREHEADER가 해당 부분에 대신 들어가게 된다. (해당 글에서는 BITMAPINFOHEADER만 설명하겠다.)
Windows Version 3계열이라면 BMP 파일의 종류에 상관 없이 공통적으로 들어가 있고 항상 40byte의 크기를 갖는다. 

 

BITMAPINFOHEADER 구조체는 다음과 같다.

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, *PBITMAPINFOHEADER;

- biSize : BITMAPINFOHEADER 필드의 크기

  + 크기 : 4byte

  + 값 : 0x28(40)byte - 고정

- biWidth : 비트맵 이미지의 가로 크기(픽셀 개수)

  + 크기 : 4byte

  + 값 : 유동적

- biHeight : 비트맵 이미지의 세로 크기(픽셀 개수)

  + 크기 : 4byte

  + 값 : 유동적

  + 특징 : 값이 양수면 이미지의 상하가 뒤집혀진 상태로 저장되어 있음을 뜻한다. 보통 양수 값이 저장된다.
               즉, 기본적으로 픽셀은 이미지의 상하가 뒤집혀진 상태로 저장이 된다는 뜻이다.

[그림1] - 이미지 픽셀 데이터 저장 순서

- biPlanes : 사용하는 색상 판의 수를 뜻

  + 크기 : 2byte

  + 값 : 0x1 - 고정

- biBitCount : 픽셀 하나를 표현하는 비트 수. 한 픽셀을 1byte로 표현하므로 8이 된다.

  + 크기 : 2byte

  + 값 : 0x8(8) - 고정

- biCompression : 압축 방식을 뜻하지만 BMP는 별개의 압축 방식을 지원하지 않아 0으로 고정이다

  + 크기 : 4byte

  + 값 : 0x0(0) - 고정

- biSizeImage : 헤더 부분들을 제외한, 순수한 비트맵 이미지의 픽셀 데이터 크기이다. 압축되지 않은 크기를 뜻한다.

  + 크기 : 4byte

  + 값 : 유동적

- biXPelsPerMeter : 그림의 가로 해상도(미터당 픽셀)을 나타낸다. 그냥 0으로 고정해도 상관 없다.

  + 크기 : 4byte

  + 값 : 0x0 - 원하면 고정

- biYPelsPerMeter : 그림의 세로 해상도(미터당 픽셀)을 나타낸다. 그냥 0으로 고정해도 상관 없다.

  + 크기 : 4byte

  + 값 : 0x0 - 원하면 고정

- biClrused : 색상 테이블에서 실제 사용되는 색상 수. 그냥 0으로 고정해도 상관 없다.

  + 크기 : 4byte

  + 값 : 0x0 - 원하면 고정

- biClrImportant : 비트맵을 표현하기 위해 필요한 색상 인덱스 수. 그냥 0으로 고정해도 상관 없다.

  + 크기 : 4byte

  + 값 : 0x0 - 원하면 고정

 

[+] RGBQUAD

RGBQUAD는 팔레트 데이터(GrayScale에서는 밝기 정도)를 뜻한다.

실제로 파일을 열어보면 어떤 구조체로 묶여 있는 것은 아니고 그냥 RGBQUAD 구조체가 쭉 나열되어 있는 형태이다.

이를 sudo 코드로 나타내 보면 다음과 같다.(GrayScale의 경우에 한에서)

struct RGBQUAD_STRUCT {
  RGBQUAD color[256]; // in GrayScale BMP
} RGBQUAD_STRUCT;

 

하나의 색상당 4byte로 표현한다. (정확힌 3byte)

 

GrayScale의 경우 이 팔레트 부분에 총 256개의 밝기 데이터를 미리 저장해두고 픽셀 데이터는 인덱스 값으로써 사용한다

0x00000000, 0x00010101, 0x00020202 ~ 0x00fdfdfd, 0x00fefefe, 0x00ffffff 와 같이 R,G,B에 해당하는 부분의 값을 전부 동일하게 하여 밝기를 표현한다.

[그림2] - 팔레트 데이터

그리고 실제 픽셀 데이터에서는 1바이트를 RGBQUAD의 인덱스로써 사용하여 미리 설정해둔 RGBQUAD 팔레트의 실제 색(밝기 색)에 접근하는 형식이다.

예) pixel data : 0x80 => RGBQUAD_STRUCT에서 0x80번째로 정의된 RGBQUAD 구조체 접근 => 해당 구조체의 값인 0x00808080을 사용

즉, 어떻게 보면 한 픽셀을 1바이트가 아닌 4바이트로 표현하는 것이 되기도 한다.

 

RGBQUAD의 구조체는 다음과 같다.

struct RGBQUAD {
  UBYTE rgbReserved;
  UBYTE rgbRed;
  UBYTE rgbGreen;
  UBYTE rgbBlue;
} RBGQUAD;

- rgbReserved : 예약된 곳 (사용 x)

  + 크기 : 1바이트

  + 값 : 0x0(0) - 고정

- rgbRed : 빨간색의 정도를 나타낸다. 

  + 크기 : 1바이트

  + 값 : 0x0 ~ 0xff (0 ~ 255)

- rgbGreen : 초록색의 정도를 나타낸다.

  + 크기 : 1바이트

  + 값 : 0x0 ~ 0xff (0 ~ 255)

- rgbBlue : 파란색의 정도를 나타낸다.

  + 크기 : 1바이트

  + 값 : 0x0 ~ 0xff (0 ~ 255)

 

각 필드의 값(rgbReserved 제외)이 0이면 어두운 색(검정색)에 가까워지고, 255에 가까울 수록 해당 색을 뚜렷하게 나타낸다. (따라서 255,255,255면 흰색, 0,0,0이면 검정색이다.)

  

[+] Pixel Data Field

실제 픽셀 데이터가 들어가는 부분이다.
(이 부분에 대한 정확한 이름은 모르겠다. - 아시는 분은 댓글로 알려주시면 감사하겠습니다... ㅠㅠ)

 

GrayScale BMP의 경우 한 픽셀을 8bit(1byte)로 사용하고 픽셀의 값은 RGBQUAD의 인덱스 값으로 사용한다.

[그림3] - RGBQUAD 색상 인덱싱

 

 

해당 부분(또는 BMP)의 특징은 한 행에 해당하는 모든 픽셀 바이트의 길이가 4바이트로 정렬되어야 한다는 것이다.

한 행의 길이가 4바이트 단위가 아닌 경우 남은 부분은 padBytes라는 이름으로 패딩된다.

 

- padBytes : 한 행에 대한 패딩 값

  + 크기 : 0~3 byte : (length(한 행의 픽셀 데이터 길이) % 4)

  + 값 : 뭐가 들어가든 상관 없지만 보통 0으로 통일된다.

Comments