void DrawBmp()//hbmScreen에 원본 그림 복사 { HDC hdc; HDC memDC; unsignedint uiCount[256] = { 0, }; int iCountY; int iCountX; unsignedint uiMaxVal; //그래프의 세로 축 최대 값을 구하기 위함.
hdc = GetDC(hWnd);
//bmp 사진 크기, 그래프 사진 크기 만큼 할당 hbmScreen = CreateCompatibleBitmap(hdc, stBinfoHead.biWidth, stBinfoHead.biHeight); hbmHisto = CreateCompatibleBitmap(hdc, X_GRAPH_SIZE + 1, Y_GRAPH_SIZE + 1); memDC = CreateCompatibleDC(hdc);
SelectObject(memDC, hbmScreen);
//ucpData, 즉 불러들인 bmp 파일로부터 memDC를 활용하여 hbmScreen에 원본 비트맵 파일 저장. //( stBinfoHead.biWidth - iCountY 함으로써 현재 리틀엔디안으로 상이 뒤집힌 상태로 저장된 것을 올바르게 다시 저장) for (iCountY = 0; iCountY < stBinfoHead.biHeight; ++iCountY) { for (iCountX = 0; iCountX < stBinfoHead.biWidth; ++iCountX) { SetPixel(memDC, iCountX, stBinfoHead.biHeight - iCountY, RGB(*(ucpData + ((iCountY*stBinfoHead.biWidth) * 3) + ((iCountX * 3) + 2) + (iCountY*uiPad)), *(ucpData + ((iCountY*stBinfoHead.biWidth) * 3) + ((iCountX * 3) + 1) + (iCountY*uiPad)), *(ucpData + ((iCountY*stBinfoHead.biWidth) * 3) + ((iCountX * 3) + 0) + (iCountY*uiPad)))); ++uiCount[ucpData[((iCountY * stBinfoHead.biWidth) * 3) + ((iCountX * 3) + 2) + (iCountY * uiPad)]]; //R, 빈도 수 계산 ex) 0 값 몇개 ~ 1 값 몇개 ~ ++uiCount[ucpData[((iCountY * stBinfoHead.biWidth) * 3) + ((iCountX * 3) + 1) + (iCountY * uiPad)]]; //G, 빈도 수 계산 ++uiCount[ucpData[((iCountY * stBinfoHead.biWidth) * 3) + ((iCountX * 3) + 0) + (iCountY * uiPad)]]; //B, 빈도 수 계산 } }
for (iCountX = 0; iCountX < X_GRAPH_SIZE; ++iCountX) { uiCount[iCountX] = (uiCount[iCountX] * Y_GRAPH_SIZE) / uiMaxVal; // 그래프의 세로(상한선)가 255를 못 넘게 끔 만든다. 이때 숫자를 키운뒤 나누면 오차율이 줄어든다. }// 이제 uiCount 는 높이 값을 가지게 된다.
버그 수정 완료. 그래프 쪽 조그만 버그가 있긴한데 우선 나중에 확인 후 다시 올리겠습니다.
일단 영상처리로 넘어가기 위해 기본형 준비.
비디오 캡쳐를 하기 위해서 먼저 vfw32.lib 를 프로젝트에서 포함 해주어야 하며 작성 소스 상단에 Vfw.h를
include 해주어야 한다.
비디오 캡쳐를 위한 작업 순서를 간단히 다음과 같다.
- 캡쳐 윈도우를 생성
- 윈도우와 캡쳐 드라이버를 연결
- 캡쳐된 비디오 프레임을 캡쳐 윈도우를 통해서 출력
캡쳐된 비디오 프레임을 캡쳐 윈도우에 보여주기 위해서는 한 프레임이 캡쳐 될 때마다 특정 함수를 호출하도록 한 다음에 호출된 함수에서 그 프레임을 화면에 출력한다.
이러한 작업을 수행하기 위해서는 VFW 라이브러리 중에서 다음과 같은 함수들을 사용해야 한다.
capGetDriverDescription()
capCreateCaptureWindow()
capDriverConnect()
capPreviewRate()
capSetVideoFormat()
capDriverDisconnect()
capSetCallbackOnFrame()
- 함수의 원형
HWND VFWAPI capCreateCaptureWindowW ( LPCWSTR lpszWindowName, DWORD dwStyle, int x, int y, int nWidth, int nHeight, HWND hwndParent, int nID);
이 함수는 캡쳐 윈도우를 생성한다. name 에는 윈도우위 이름을 지정한다. style 윈도우위 스타일을 지정한다. (x,y)에는 캡쳐 윈도우의 좌측 상단의 좌표를 지정한다. width height에는 캡쳐 윈도우의 크기를 지정한다. hWnd에는 부모윈도우의 핸들값을 입력한다. id에는 윈도우의 식별 번호를 입력한다. 캡쳐 윈도우가 정상적으로 생성되면 캡쳐 윈도우의 핸들의 함수 결과값으로 반환되고 그렇지 않으면 NULL값이 반환된다.
BOOL VFWAPI capGetDriverDescriptionW (UINT wDriverIndex, LPWSTR lpszName, int cbName, LPWSTR lpszVer, int cbVer);
이 함수는 캡쳐 드라이버의 이름 및 버전 정보를 검색한다. 첫 번째 매개변수인 index는 검색하고자 하는 드라이버의 번호를 나타내는데, 0부터 9까지의 값을 가질 수 있다.
즉, 한 컴퓨터에서 9대의 캡쳐 장치가 사용될 수 있다고 가정하고 있다. 검색하고자 하는 번호의 드라이버가 존재하면 이 함수는 name에 드라이버의 이름을 저장하고 version에 드라이버 버전을 저장한 다음에 함수 결과값으로 TURE 값을 반환한다.
이 함수는 미리보기 (preview)모드에서의 프레임 재생 속도를 설정한다. 여기에서 미리보기란 카메라에서 입력된 비디오를 파일에 저장하는 것이 아니라 화면에 보여준다는 것을 의미한다. hWnd는 캡쳐 윈도우의 핸들 값으로 설정하고 rate는 밀리초(ms) 단 위의 시간으로 설정한다. 예를 들어, rate 값을 66으로 설정하면 0.066초마다 새로운 비디오 프레임을 캡쳐해서 디스플레이 하게 도니다. 이와 같은 속도로 재생을 하면 1초에 15개의 비디오 프레임이 디스플레이된다.
VFW 라이브러리에서는 캡쳐된 비디오 프레임을 화면에 보여주는 작업을 callback함수를 사용해서 처리하도록 하고 있다. capSetCallbackOnFrame()함수는 캡쳐 장치로부터 비디오 프레임이 캡쳐되었을때에 이를 화면에 보여주기 위해서 호출되는 callback 함수를 설정한다. hWnd는 캡쳐 윈도우의 핸들 값으로 설정하고 func는 호출될 함수 이름으로 설정한다.
//1. 비디오캡처 윈도우를 지정된 영역에 원하는 크기로 생성 hCamera = capCreateCaptureWindowW(L"Camera", WS_CHILD | WS_VISIBLE, 0, 0, 320, 240, hWnd, 0); //2. 캡처 윈도우와 드라이버 연결 capDriverConnect(hCamera, 0); //3. 캡처윈도우에 프리뷰 모드로 찍을 프레임 값 설정 capPreviewRate(hCamera, 48); //숫자가 작을 수록 화면에 보이는 속도가 빨라진다. //4. 캡처되는 이미지의 정보를 얻어냄 capGetVideoFormat(hCamera, &bmpInfo, sizeof(bmpInfo)); //5. 원하는 크기로 생성시킨 값으로 수정 bmpInfo.bmiHeader.biWidth = 320; bmpInfo.bmiHeader.biHeight = 240; //6. 콜백 함수 호환을 위한 비디오 포맷 설정 //capSetVideoFormat(hCamera, &bmpInfo, sizeof(bmpInfo)); //7. 캡쳐 윈도우에 프리뷰 모드로 보여줌 capPreview(hCamera, TRUE);
//1. 비디오캡처 윈도우를 지정된 영역에 원하는 크기로 생성 hCamera = capCreateCaptureWindowW(L"Camera", WS_CHILD | WS_VISIBLE, 0, 0, 640, 480, hWnd, 0); //2. 캡처 윈도우와 드라이버 연결 capDriverConnect(hCamera, 0); //3. 캡처윈도우에 프리뷰 모드로 찍을 프레임 값 설정 capPreviewRate(hCamera, 1); //숫자가 작을 수록 화면에 보이는 속도가 빨라진다. //4. 캡처되는 이미지의 정보를 얻어냄 capGetVideoFormat(hCamera, &bmpInfo, sizeof(bmpInfo)); //5. 원하는 크기로 생성시킨 값으로 수정 bmpInfo.bmiHeader.biWidth = 640; bmpInfo.bmiHeader.biHeight = 480; //6. 콜백 함수 호환을 위한 비디오 포맷 설정 capSetVideoFormat(hCamera, &bmpInfo, sizeof(bmpInfo)); //7. 캡쳐 윈도우에 프리뷰 모드로 보여줌 capPreview(hCamera, TRUE);
return 0; }
화면이 커졌다.
프레임 속도를 1로 빠르게 설정해줘야 잘 뜬다. 아마 영상이 커지는 만큼 그에 비하는 속도도 빨라져야 부드러운 영상이 연출 되나 보다.
BOOL StretchBlt(HDC hdcDest, int nXOriginDest, int nYOriginDest, int nWidthDest, int nHeightDest, HDC hdcSrc, int nXOriginSrc, int nYOriginSrc, int nWidthSrc, int nHeightSrc, DWORD dwRop);
인수
▶hDC:비트맵이 복사될 DC
▶X,Y,nWidth,nHeight:비트맵이 복사될 위치의 좌상단 좌표와 폭, 넓이, 이 값은 논리적 좌표값이다.
▶HSrcDC:복사될 비트맵을 가지고 있는 DC. 만약 dwROP가 소스가 필요없는 값이라면 NULL이 될 수도 있다.
▶nXOriginSrc,nYOriginSrc, nWidthSrc, nHeightSrc:복사될 비트맵의 좌측 상단 좌표와 크기. 이 영역의 크기와 복사처의 크기가 다를 경우 비트맵은 복사처의 크기에 맞게 축소되거나 확대된다.
▶dwROP:ROP코드, 즉 브러쉬와 복사원, 복사처의 비트맵 색상이 논리 연산될 방법을 지정한다. 모두 256개의 가능한 값이 있지만 실제로 의미를 갖는 없은 다음 15개이며 매크로 상수가 정의되어 있다. 이 외의 ROP코드가 필요할 경우에는 상수를 직접 사용해야 한다. 아래 표에서 S는 복사원 (Source), D는 복사처(Destination), P는 패턴(Pattern, 즉 브러쉬)를 의미한다.
매크로 상수
상수
연산식
BLACKNESS
0x00000042
0
DSTINVERT
0x00550009
~D
MERGECOPY
0x00C000CA
D&S
MERGEPAINT
0x00BB0226
~S|D
NOTSRCCOPY
0x00330008
~S
NOTSRCERASE
0x001100A6
~(S|D)
PATCOPY
0x00F00021
P
PATINVERT
0x005A0049
P^D
PATPAINT
0x00FB0A09
P|~(S|D)
SRCAND
0x008800C6
S&D
SRCCOPY
0x00CC0020
S
SRCERASE
0x00440328
S&~D
SRCINVERT
0x00660046
S^D
SRCPAINT
0x00EE0086
S|D
WHITENESS
0x00FF0062
1
리턴
성공하면 nonzero, 에러 발생시 0을 리턴한다.
설명
DC간에 비트맵을 전송하여 복사한다. BitBlt와 동작하는 방식이 유사하나 단 복사원의 크기와 높이를 따로 지정할 수 있기 때문에 확대및 축소 복사할 수 있다. 20*40의 크기를 가지는 비트맵을 40*80영역에 복사하면 이 비트맵은 2배로 확대되며 10*20영역에 복사하면 절반으로 축소된다.
예제 1
다음 예제는 리소스에 정의된 IDB_BITMAP1을 두배로 확대하여 작업 영역에 출력한다.
복사원과 복사처의 크기 부호가 다르면 거울에 비친 것처럼 반사된 모양의 비트맵이 출력된다.
참고함수
BitBlt:비트맵을 확대하지 않고 출력한다.
플렛폼
95이상
본문참조
StretchDIBits function
The StretchDIBits function copies the color data for a rectangle of pixels in a DIB, JPEG, or PNG image to the specified destination rectangle. If the destination rectangle is larger than the source rectangle, this function stretches the rows and columns of color data to fit the destination rectangle. If the destination rectangle is smaller than the source rectangle, this function compresses the rows and columns by using the specified raster operation.
int StretchDIBits(
_In_ HDC hdc,
_In_ int XDest,
_In_ int YDest,
_In_ int nDestWidth,
_In_ int nDestHeight,
_In_ int XSrc,
_In_ int YSrc,
_In_ int nSrcWidth,
_In_ int nSrcHeight,
_In_ const VOID *lpBits,
_In_ const BITMAPINFO *lpBitsInfo,
_In_ UINT iUsage,
_In_ DWORD dwRop
);
Parameters
hdc [in]
A handle to the destination device context.
XDest [in]
The x-coordinate, in logical units, of the upper-left corner of the destination rectangle.
YDest [in]
The y-coordinate, in logical units, of the upper-left corner of the destination rectangle.
nDestWidth [in]
The width, in logical units, of the destination rectangle.
nDestHeight [in]
The height, in logical units, of the destination rectangle.
XSrc [in]
The x-coordinate, in pixels, of the source rectangle in the image.
YSrc [in]
The y-coordinate, in pixels, of the source rectangle in the image.
nSrcWidth [in]
The width, in pixels, of the source rectangle in the image.
nSrcHeight [in]
The height, in pixels, of the source rectangle in the image.
lpBits [in]
A pointer to the image bits, which are stored as an array of bytes. For more information, see the Remarks section.
lpBitsInfo [in]
A pointer to a BITMAPINFO structure that contains information about the DIB.
iUsage [in]
Specifies whether the bmiColors member of the BITMAPINFO structure was provided and, if so, whether bmiColors contains explicit red, green, blue (RGB) values or indexes. The iUsage parameter must be one of the following values.
Value
Meaning
DIB_PAL_COLORS
The array contains 16-bit indexes into the logical palette of the source device context.
//1. 비디오캡처 윈도우를 지정된 영역에 원하는 크기로 생성 hCamera = capCreateCaptureWindowW(L"Camera", WS_CHILD | WS_VISIBLE, 0, 0, X_SCALE, Y_SCALE, hWnd, 0); if (hCamera == 0) { MessageBox(hWnd, "On_Create() 의 capCreateCaptureWindowW() 에러. 캡쳐 윈도우 생성 실패.", "Button", MB_OK); PostQuitMessage(0); } //2. 캡처 윈도우와 드라이버 연결 bRet = capDriverConnect(hCamera, 0); if (bRet == FALSE) { MessageBox(hWnd, "On_Create() 의 capDriverConnect() 에러. 드라이버에 연결할 수 없습니다.", "Button", MB_OK); PostQuitMessage(0); } //3. 캡처윈도우에 프리뷰 모드로 찍을 프레임 값 설정 capPreviewRate(hCamera, 1); //숫자가 작을 수록 화면에 보이는 속도가 빨라진다. //4. 캡처되는 이미지의 정보를 얻어냄 capGetVideoFormat(hCamera, &stBmpInfo, sizeof(stBmpInfo)); //5. 원하는 크기로 생성시킨 값으로 수정 stBmpInfo.bmiHeader.biWidth = X_SCALE; stBmpInfo.bmiHeader.biHeight = Y_SCALE; //6. 콜백 함수 호환을 위한 비디오 포맷 설정 capSetVideoFormat(hCamera, &stBmpInfo, sizeof(stBmpInfo)); //7. capSetCallbackOnFrame(hCamera, Capture); //8. 캡쳐 윈도우에 프리뷰 모드로 보여줌 capPreview(hCamera, TRUE);
setpixel() 로 찍으면 오른쪽 이 뚝뚝 끊긴다. 원복 복사가 아닌 점으로 다 일일이 찍기 때문이다.
이제 평균값을 구한다고 하는데,
LRESULT CALLBACK Capture(HWND hWpWnd, LPVIDEOHDR lpVHdr) { HDC hdc; int iCountX; int iCountY; WCHAR wcStr[50] = { 0, }; static BYTE vData[X_SCALE * Y_SCALE * 3]; //영상처리를 위해서 데이터 값을 그대로 가져올 것.