이미지 관련 기초적인 것들부터 정리를 다시 해야할 것 같다.
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로 변환하기 정리 끝.
'C/C++' 카테고리의 다른 글
윈도우 HID 후킹 - RegisterRawInputDevices (0) | 2021.06.15 |
---|---|
[C++] MFC ActiveX에서 Tab Control 사용 (0) | 2019.07.28 |
log4cxx.dll 컴파일시 오류에 대하여 (Visual Studio 2010) 64비트 버젼 컴파일 (0) | 2018.11.19 |