이미지 관련 기초적인 것들부터 정리를 다시 해야할 것 같다.


USB 카메라 테스트를 진행하느라

raw data 이미지를 bitmap 이미지로 변환해서 저장해야 하는 상황이라

대충 인터넷에 예제가 많이 있겠거니 하고 뒤져서 구현했는데

의외로 간단하게 256컬러에 대해서만 구현되어있는 경우가 많지 않아서

이번에 구현한 함수를 이곳에 기록해 둔다.


최초로 참고한 소스코드는

http://blog.naver.com/gemian/20030425129

위의 링크지만 다른 곳의 소스코드들도 대부분 흑백 256 칼라에만 맞춰져 있어 별반 다르지 않았다.


BMP 포맷에 대한 정확한 내용은

https://ko.wikipedia.org/wiki/BMP_%ED%8C%8C%EC%9D%BC_%ED%8F%AC%EB%A7%B7

https://msdn.microsoft.com/ko-kr/library/windows/desktop/dd183376(v=vs.85).aspx

위 링크를 참조하면 된다.


실제 소스코드는 아래와 같으며

주석에서 설명한 것처럼 256 그레이와 3바이트, 4바이트 칼라영상에 대해서도 모두 동일하게 적용하는 것이 가능한 소스코드이다.

(이 코드도 100% 정석대로 작성된 코드는 아니므로 위의 스팩을 잘 살펴보시고 수정하기 바랍니다.)


간단하게만 설명하면 256그레이 이미지는 RGB팔레트 처리가 필요하기 때문에 해당 부분이 추가되어야 하고

나머지는 헤더부분만 잘 정의하면 된다.


개발환경은 Visual Studio 2010에서 C++로 작성된 코드이다.


/*

 *

 * filename      : save image file name

 * rawdata       : raw data (image) 1 dimension byte array (or pointer)

 * width         : width size

 * height        : height size

 * _byteperpixel : byte per pixel (gray is 1, color is 3 or 4)

 *

 */

int CQuadCamDlg::WriteBitmapFile(char *filename, BYTE *rawdata, int width, int height, int _byteperpixel)

{

int color = 256; // default color = 256

DWORD size,len = 0; // use file handling

RGBQUAD rgb[256];

BITMAPINFO biHeader;

BITMAPFILEHEADER bfHeader;


// 256 color is 1078 (included RGBQUAD)

int bfOffBits = (_byteperpixel == 1) ? 1078 : 54;


memset(&biHeader, 0, sizeof(BITMAPINFO));

memset(&bfHeader, 0, sizeof(BITMAPFILEHEADER));

memset(&rgb, 0, sizeof(RGBQUAD)*color);


biHeader.bmiHeader.biSize = 40; // sizeof(BITMAPINFOHEADER); //Size of  bmiHeader = 40 bytes

biHeader.bmiHeader.biWidth = width;

biHeader.bmiHeader.biHeight = height;

biHeader.bmiHeader.biPlanes = 1;

   

//gray is 8, color is 24 or 32

biHeader.bmiHeader.biBitCount = 8 * _byteperpixel;

biHeader.bmiHeader.biSizeImage= width * height * _byteperpixel;


biHeader.bmiHeader.biCompression = BI_RGB; // Whether Compression or non compression (BI_RGB = 0)

biHeader.bmiHeader.biXPelsPerMeter = 0; // Meter per XPixel

biHeader.bmiHeader.biYPelsPerMeter = 0; // Meter per YPixel

biHeader.bmiHeader.biClrUsed = 0; // Real Color If 0 then use maximum colors

biHeader.bmiHeader.biClrImportant = 0;


// Set about BITMAPFILEHEADER

bfHeader.bfType = DIB_HEADER_MAKER; //'BM'

bfHeader.bfOffBits = bfOffBits; // 54 or 1078


// Location of bitmap data

bfHeader.bfSize = bfOffBits + biHeader.bmiHeader.biSizeImage;

bfHeader.bfReserved1 = 0;

bfHeader.bfReserved2 = 0;


// Gray is set palette

if(_byteperpixel == 1)

{

for(int i = 0; i < 256; i++)

{

rgb[i].rgbBlue = (BYTE)i;

rgb[i].rgbGreen = (BYTE)i;

rgb[i].rgbRed = (BYTE)i;

rgb[i].rgbReserved = 0x00;

}

}


// create file

HANDLE fd= CreateFile(filename, GENERIC_READ|GENERIC_WRITE, 0, NULL,CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);


// Save file (bfheader)

size = sizeof(BITMAPFILEHEADER);

if(!WriteFile(fd, (LPSTR)&bfHeader, size, &len, NULL))

AfxMessageBox("Error bf Header");


// Save file (biheader + RGBQUAD)

size = sizeof(BITMAPINFO);

if(!WriteFile(fd, (LPSTR)&biHeader, size, &len, NULL))

AfxMessageBox("Error bi Header");


// 256 Color(Gray) add RGBQUAD

if (_byteperpixel == 1)

{

size = color * sizeof(RGBQUAD);

if(!WriteFile(fd, (LPSTR)&rgb, size, &len, NULL))

AfxMessageBox("Error RGBQUAD");

}


// Save file (raw data)

size = biHeader.bmiHeader.biSizeImage;

if(!WriteFile(fd, rawdata, size, &len, NULL))

AfxMessageBox("Error Raw data");


CloseHandle(fd);


return len;

}


코드는 사실 스팩을 보고 조금만 고쳐주면

칼라영상까지 저장이 가능하도록 수정하는 것은 어려운 일이 아니었지만

오히려 주의할 점은




좌우 사진에서와 같이 로우이미지의 경우 대부분의 카메라 특성상 데이터가 역순으로 되어있기 때문에

가로줄 데이터를 뒤집어 주는 과정이 필요하다.

또 칼라이미지의 경우 RGB 순서가 다르게 배열되어 있는 경우도 있기 때문에

아래의 두개 영상처럼 바이트순서를 조정해서 칼라를 맞춰주는 과정이 필요하기도 하다.


이상으로 간단하게 정리한 raw image data를 bmp로 변환하기 정리 끝.

Posted by 휘프노스
,