BMP 파일을 읽고 쓰는 방법

코딩과 교육/전문코딩 2007. 11. 23. 16:46


비트맵포맷의 구조는 다음과 같다.

기본적으로 디바이스 독립적 영상파일 저장 표준규격은 JPEG, GIF, BMP, TIFF, PCX, PGM 등의 여러 규격이 존재한다. 많이 사용하는 영상 포맷들은 압축 알고리즘을 사용하여 작은 크기로 변환시킨다.
BMP 파일포맷은 압축을 수행하지 않아 파일 크기가 커지는 반면, 파일의 구조가 가장 간단하다.


## 비타맵 파일에 대한 정보(파일헤드)

typedef struct tagBITMAPFILEHEADER {    // 14 = 2+4+2+2+4 (byte)
   WORD    bfType;  // "BM"이라는 글자가 설정됨
   DWORD   bfSize;  // 비트맵 파일의 전체 크기
   WORD    bfReserved1;  // 예약변수(0으로 설정함)
   WORD    bfReserved2;  // 예약변수(0으로 설정함)
   DWORD   bfOffBits;  // 파일에서 비트맵 데이터가 있는 위치
} BITMAPFILEHEADER;

비트맵파일인지 아닌지를 확인하는 변수가 bfType 이다. BMP파일의 첫 두 바이트는 항상 "BM" 문자가 저장된다. bfOffBits 는 파일 시작부분에서 실제 영상데이터가 존재하는 위치까지 바이트 단위의 거리를 나타낸다. WORD 는 unsigned short, DWORD 는 unsigned long 으로 정의되어 있다.

## 영상 자체에 대한 정보(영상헤드)

비트맵 영상에 대한 크기, 흑백, 칼러정보, 팔레트 크기 정보등을 저장하기 위한 구조체 변수이다.

typedef struct tagBITMAPINFOHEADER {  // 40 = 4+4+4+2+2+4+4+4+4+4+4 (byte)
   DWORD biSize;  // 구조체의 크기
   LONG biWidth;  // 비트맵의 가로 크기
   LONG biHeight;  // 비트맵의 세로 크기
   WORD biPlanes;  // Plane수(1로 설정함)
   WORD biBitCount;  // 한 픽셀당 비트수
   DWORD biCompression;  // 압축 유무 플래그
   DWORD biSizeImage;  // 그림 데이터의 크기
   LONG biXPelsPerMeter;  // 한 픽셀당 가로 미터
   LONG biYPelsPerMeter;  // 한 픽셀당 세로 미터
   DWORD biClrUsed;  // 그림에서 실제 사용되는 컬러 수
   DWORD biClrImportant;  // 중요하게 사용되는 컬러
} BITMAPINFOHEADER;

중요한 정보는 영상파일의 크기를 나타내는 변수 biHeight, biWidth 와 흑백과 칼라여부를 나타내는 biBitCount, 팔레트 크기를 나타내는 biClrUsed 등이다.
biBitCount 가 8이면 흑백영상이거나 2^8 = 256 칼라의 영상이다. 24라면 트루칼라, 16이면 2^16 개의 칼라를 사용하는 영상이다.


## 팔레트

팔레트는 인덱스에 의한 칼라값을 저장하기 위한 구조체이다. 이 구조체를 사용, 팔레트 수만큼 배열을 할당, 저장한다. 256 칼라모드의 경우 팔레트배열 크기는 256이 되고, 16비트 칼라영상의 경우 2^16 개의 팔레트배열 크기를 가지게 된다.

typedef struct tagRGBQUAD { // 4
   BYTE rgbBlue;  // B성분 (파랑)
   BYTE rgbGreen; // G성분 (초록)
   BYTE rgbRed;   // R성분 (빨강)
   BYTE rgbReserved;
} RGBQUAD;
typedef struct tagBITMAPINFO {
   BITMAPINFOHEADER bmiHeader;
   RGBQUAD bmiColors[1];
} BITMAPINFO;


## DIB 와 DDB
DDB 는 Device Dependent Bitmat 의 약자로 디바이스 종속적인 비트맵이고, DIB 는 Device Independent Bitmat 의 약자로 디바이스에 독립적인 비트맵이다. 다시말해서 DIB 는 어느 기종에서 보든 즉, PC 거나, MAC 이거나, 아니면 핸드폰으로 보거나, PDA로 보거나 상관없이 같은 그림과 칼라가 나온다는 의미이다.

## BMP 에서 DIB 사용시 주의사항
비트맵 영상이 저장될 때는 이미지가 거꾸로 저장된다. 즉, 비트맵에서 영상데이타를 영상처리를 위해 사용할 배열로 다시 저장할 때는 거꾸로 뒤집어서 저장해 주어야 한다.

 for(int i=0; i<biHeight; i++) {
  for (int j=0; j<biWidth; j++)
   ProcImg[i*biWidth+j] = lpImg[(biWidth-i-1)*rwsize+j];
 }


 
영상의 가로길이는 4바이트의 배수가 된다.
비트맵은 메모리 저장시 가로줄의 크기가 항상 4바이트의 배수가 되어야 한다. 실제 사용 영상의 가로길이는 4바이트의 배수가 아닐 수 있다. 이런 경우 4의 배수바이트로 바꾸어 저장한다. 즉 78 x 80 크기의 BMP 파일은 가로 픽셀 78이 78byte 로 4의 배수가 아니므로 80byte 로 만들고 나머지 2byte 는 쓰레기값으로 처리된다. 실제 저장되는 메모리는 80 x 80 으로 저장된다.

아래는 간단한 소스이다.

#include <stdio.h>
#include <windows.h>
#define WIDTHBYTES(bits) (((bits)+31)/32*4)
#define BYTE    unsigned char

int main()
{
 FILE *infile;
 infile = fopen("ImgBW.bmp", "rb");
 if(infile==NULL) {printf("No Image File"); return;}

 // BMP Header Information
 BITMAPFILEHEADER hf;
 BITMAPINFOHEADER hInfo;
 fread(&hf, sizeof(BITMAPFILEHEADER),1,infile);
 if(hf.bfType!=0x4D42) exit(1);
 fread(&hInfo,sizeof(BITMAPINFOHEADER),1,infile);
 if(hInfo.biBitCount!=8) {printf("Bad File Format!!"); return;}

 // BMP Pallete
 RGBQUAD hRGB[256];
 fread(hRGB, sizeof(RGBQUAD), 256, infile);

 // Memory
 BYTE *lpImg = new BYTE [hInfo.biSizeImage];
 fread(lpImg, sizeof(char), hInfo.biSizeImage, infile);
 fclose(infile);

 int rwsize = WIDTHBYTES(hInfo.biBitCount * hInfo.biWidth);

 // Reversed Image
 for(int i=0; i<hInfo.biHeight; i++) {
  for (int j=0; j<hInfo.biWidth; j++)
   lpImg[i*rwsize+j] = 255 - lpImg[i*rwsize+j];
 }

 // Image Output
 FILE *outfile = fopen("ImgBWR.bmp", "wb");
 fwrite(&hf, sizeof(char), sizeof(BITMAPFILEHEADER), outfile);
 fwrite(&hInfo, sizeof(char), sizeof(BITMAPINFOHEADER), outfile);
 fwrite(hRGB, sizeof(RGBQUAD), 256, outfile);
 fwrite(lpImg, sizeof(char), hInfo.biSizeImage, outfile);
 fclose(outfile);

 delete [] lpImg;
}

사용자 삽입 이미지

256 회색 이미지를 반전 처리한


BMP 파일을 읽고 쓰는 방법
http://electoy.tistory.com/35
JelicleLim (2007.11.24.)