6월9일 RFID( 프로젝트) 영상처리(영상뒤집기)
그림이나 영상같은경우
칼라이기때문에
폭과 높이 그리고 색상을 곱해서 나오게되면..전체 크기와 같아야한다.
예를 들면 폭 316 높이 309 에 색상 *3으로 하게되면 size가 292932가 나와야한다.
보통 폭이 4의 배수가 되면 자동으로 그림이 나온다. 하지만.....
폭이 4배수가 아닌경우
예를 들어서 폭이 473이고 높이가 478인경우 색상은 3이라서
473*478*3=678282로
전체 사이즈 678760이랑 차이가 난다. 그래서
출력되는 그림도 역시 한픽셀이나 두픽셀이 밀려난다 ㅠ
안타깝게도 실패했다.
이문제를 해결하기위해서는 바로 폭을 4의 배수로 맞추는것이 필요하다.
#define WIDTH4(width) ((width+3)>>2)<<2
입력된 폭을 +3을 하고 왼쪽으로 시프트 2번 오른쪽으로 시프트 2번 하게 되면
일단 첫수에서 +3을 하고 나누기 4하고 곱하기 4하는것과 같다.
이는 소수점자리를 버리거나 반올림해서 만들고 혹시 폭을 만들어야한다.
그래서 결국에는 폭*높이*3 해서 678760을 맞춰준다.
그러면 그림이 깨지지 않는다.
전에 비트맵뷰어를 만들때 그림이 안좋아서 안되는가 햇느지만.
아마 모든 컴퓨터파일은 0101이기때문에 홀수가 될수없고 맞추기위해서 4의배수로 맞춰진다.
나중에도 그렇고 영상을 처리할때 이 폭과 높이에 대해서 맞춰야하는경우가 많다.
#include<Windows.h>
#include"vfw.h"
#include <math.h>
#include <iostream>
#pragma comment(lib, "vfw32.lib") //vfw32 라이브러리 포함
#define WIDTH4(width) ((width+3)>>2)<<2 //가장중요(폭을 4배수로 맞추는것이다.
#define ID_BCAPTURE 101
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
LRESULT CALLBACK FramInfo(HWND, LPVIDEOHDR); // 콜백 함수
TCHAR str[10]; //정수데이터값 확인을 위한 버퍼
unsigned char Menuflag; //인터페이스 선택
HINSTANCE g_hInst;
HWND hWndMain;
HWND hVFW;
HWND Hwndmain;
HBITMAP hBit;
BITMAPINFO Bm; //비트맵 정보를 가짐
HDC Hdc;
//비트맵의 정보
BYTE *imageData,*Dataimage;
BITMAPFILEHEADER *bmfh;
BITMAPINFO *binfo;
//영상 메모리
int Action;
LONG width;
LONG height;
LONG width4;
LPCTSTR lpszClass = TEXT("VFW 기본 예제");
char lpstrFile[MAX_PATH]; //충반한 길이의 버퍼제공하기위해 입력받는 파일명을 MAX_PATH(260)크기만큼선언
FILE * file;
int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszCndParam, int nCmdShow)
{
HWND hWnd;
MSG Message;
WNDCLASS WndClass;
g_hInst = hInstance;
WndClass.cbClsExtra = 0;
WndClass.cbWndExtra = 0;
WndClass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
WndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
WndClass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
WndClass.hInstance = hInstance;
WndClass.lpfnWndProc = WndProc;
WndClass.lpszClassName = lpszClass;
WndClass.lpszMenuName = NULL;
WndClass.style = CS_HREDRAW|CS_VREDRAW;
RegisterClass(&WndClass);
//영상을 보여줄 윈도우 생성
hWnd = CreateWindow(lpszClass, lpszClass,WS_OVERLAPPEDWINDOW|WS_CAPTION,200,200,
300, 300, NULL, (HMENU)NULL, hInstance, NULL);
hWndMain = hWnd;
ShowWindow(hWnd, SW_SHOW);
while(GetMessage(&Message, 0,0,0))
{
TranslateMessage(&Message);
DispatchMessage(&Message);
}
return (int)Message.wParam;
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam)
{
int i;
int j;
LONG abc,def;
switch(iMessage)
{
case WM_CREATE:
file=fopen("test.bmp","rb");
bmfh=(BITMAPFILEHEADER *)malloc(sizeof(BITMAPFILEHEADER));
fread(bmfh,sizeof(BITMAPFILEHEADER),1,file);
binfo=(BITMAPINFO *)malloc(sizeof(BITMAPINFOHEADER)+sizeof(RGBQUAD)*256);
fread(binfo,sizeof(BITMAPINFOHEADER),1,file);
imageData=(BYTE *)malloc(binfo->bmiHeader.biSizeImage);
fread(imageData,sizeof(BYTE),binfo->bmiHeader.biSizeImage,file);
Dataimage=(BYTE *)malloc(binfo->bmiHeader.biSizeImage); //복사받을
width4=binfo->bmiHeader.biWidth*3;
width4=WIDTH4(width4);
abc=binfo->bmiHeader.biHeight; //높이를 대입
for(height=0;height<binfo->bmiHeader.biHeight;height++)
{
def=binfo->bmiHeader.biWidth;
for(width=0;width<binfo->bmiHeader.biWidth;width++)
{
Dataimage[(height*width4)+width*3]=
imageData[(abc*width4)+def*3];
Dataimage[(height*width4)+width*3+1]=
imageData[(abc*width4)+def*3+1];
Dataimage[(height*width4)+width*3+2]=
imageData[(abc*width4)+def*3+2];
def--;
}
abc--;
}
fclose(file);
return 0;
case WM_COMMAND:
return 0;
case WM_LBUTTONDOWN:
Hdc =GetDC(hWnd);
width=binfo->bmiHeader.biWidth;
height=binfo->bmiHeader.biHeight;
MoveWindow(hWnd,200,200,(int)width*2+10,(int)height+33,TRUE);
SetDIBitsToDevice(Hdc,0,0,width,height,NULL,NULL,NULL,height,imageData,binfo,DIB_RGB_COLORS);
if(Dataimage!=NULL)
{
SetDIBitsToDevice(Hdc,width+5,0,width*2,height,NULL,NULL,NULL,height,Dataimage,binfo,DIB_RGB_COLORS);
}
ReleaseDC(hWnd,Hdc);
return 0;
case WM_DESTROY:
free(imageData);
free(binfo);
free(bmfh);
free(Dataimage);
PostQuitMessage(0);
return 0;
}
return (DefWindowProc(hWnd,iMessage,wParam,lParam));
}
사진을 뒤집게 했다.
이 사진을 조금더 업그레이드 시켜서 영상도 뒤집게 했다.
결과값은
#include<Windows.h>
#include"vfw.h"
#include <math.h>
#include <iostream>
#pragma comment(lib, "vfw32.lib") //vfw32 라이브러리 포함
#define WIDTH4(width) ((width+3)>>2)<<2 //가장중요(폭을 4배수로 맞추는것이다.
LONG width4;
LPVIDEOHDR VideoHdr_1; //실제 이미지 데이터를 받기위한 변수
BITMAPFILEHEADER *bmfh; //비트맵헤더 포인터
BYTE * imageData; //비트맵이미지 포인터
BYTE * Dataimage; //디집기
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
LRESULT CALLBACK FramInfo(HWND, LPVIDEOHDR); // 콜백 함수
LONG abc,iheight,iwidth,def;
TCHAR str[10]; //정수데이터값 확인을 위한 버퍼
unsigned char Menuflag; //인터페이스 선택
HINSTANCE g_hInst;
HWND hWndMain;
HWND hVFW;
HWND Hwndmain;
HBITMAP hBit;
BITMAPINFO Bm; //비트맵 정보를 가짐
//영상 메모리
int TempPos=0; //스크롤바에 의한 변화값
int grayimage[240][320]; //그레이영상
int hueimage[240][320][3]; //hue 영상
int resultimage[240][320]; //영상처리결과영상
LPCTSTR lpszClass = TEXT("VFW 기본 예제");
void screenshot()
{
FILE * file; //파일 포인터생성
file=fopen("test.bmp","wb"); //파일 열기
bmfh=(BITMAPFILEHEADER *)malloc(sizeof(BITMAPFILEHEADER)); //BITMAPFILEGEADER부분을 동적할당
//비트맵이기때문에 magic number를 입력해준다. 그리고 size , offbit까지 기존에 가진정보로
// 쉽게 가져온다.
bmfh->bfType=0x4D42;
bmfh->bfSize=(Bm.bmiHeader.biSizeImage+sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER));
bmfh->bfReserved1=0;
bmfh->bfReserved2=0;
bmfh->bfOffBits=sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER);
// imageData=(BYTE *)malloc(Bm.bmiHeader.biSizeImage);
imageData=VideoHdr_1->lpData;
fwrite(bmfh,sizeof(BITMAPFILEHEADER),1,file);
fwrite(&Bm,sizeof(BITMAPINFOHEADER),1,file);
fwrite(imageData,sizeof(BYTE),Bm.bmiHeader.biSizeImage,file);
fclose(file);
}
int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszCndParam, int nCmdShow)
{
HWND hWnd;
MSG Message;
WNDCLASS WndClass;
g_hInst = hInstance;
WndClass.cbClsExtra = 0;
WndClass.cbWndExtra = 0;
WndClass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
WndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
WndClass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
WndClass.hInstance = hInstance;
WndClass.lpfnWndProc = WndProc;
WndClass.lpszClassName = lpszClass;
WndClass.lpszMenuName = NULL;
WndClass.style = CS_HREDRAW|CS_VREDRAW;
RegisterClass(&WndClass);
//영상을 보여줄 윈도우 생성
hWnd = CreateWindow(lpszClass, lpszClass,WS_OVERLAPPEDWINDOW|WS_CAPTION,200,200,
300, 300, NULL, (HMENU)NULL, hInstance, NULL);
hWndMain = hWnd;
ShowWindow(hWnd, SW_SHOW);
while(GetMessage(&Message, 0,0,0))
{
TranslateMessage(&Message);
DispatchMessage(&Message);
}
return (int)Message.wParam;
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam)
{
HDC Hdc;
switch(iMessage)
{
case WM_CREATE:
Hwndmain = hWnd;
Hdc = GetDC(hWnd);
//캡쳐된 비디오 영상을 보여줄 윈도우 생성
hVFW = capCreateCaptureWindow(TEXT("VFW"), WS_CHILD|WS_VISIBLE,0,0,1,1,hWnd,0);
//(캡쳐될 윈도우 이름 생성, 윈도우 스타일, 시작점x좌표, 시작점y좌표, 넓이, 높이, 부모윈도우 핸들, window ID)
capDriverConnect(hVFW,0); //캡쳐윈도우와 드라이버 연결(캡쳐윈도우 핸들, 캡쳐드라이버의 인덱스)
capPreviewRate(hVFW,1); //프리뷰모드에서 보여질 프레임 속도(rate)설정
//capPreview(hVFW,TRUE); //프리뷰모드 사용여부
capGetVideoFormat(hVFW, &Bm, sizeof(Bm)); //영상복사를 위해 사용, 포맷의 크기를 바이트로 전환
//(캡쳐윈도우 핸들, bitmapinfo 구조체 포인터, 구조체 크기)
CreateWindow(TEXT("button"),TEXT("스샷"),WS_CHILD|WS_VISIBLE|BS_PUSHBUTTON,330,20,50,40,hWnd,
(HMENU)0,g_hInst,NULL);
hBit = CreateCompatibleBitmap(Hdc, Bm.bmiHeader.biWidth, Bm.bmiHeader.biHeight);
//DC에 호환하는 비트맵을 생성하여 반환(dc의 핸들, 비트맵의가로 사이즈, 비트맵의 세로 사이즈)
// 비디오 프레임이 캡쳐되었을 경우 처리하기 위한 함수를 설정해주는 매크로 함수
if(capSetCallbackOnFrame(hVFW, FramInfo)==FALSE)
//(캡쳐 윈도우 핸들, 콜백함수에 의해 호출되는 함수 포인터)
{
return FALSE;
}
ReleaseDC(hWnd, Hdc);
return 0;
case WM_COMMAND: //버튼 클릭시
switch(LOWORD(wParam))
{
case 0:
screenshot();
MessageBox(hWnd,TEXT("캡쳐가 완료되었습니다."),TEXT("Button"),MB_OK);
break;
}
return 0;
case WM_DESTROY:
free(Dataimage);
PostQuitMessage(0);
return 0;
}
return (DefWindowProc(hWnd,iMessage,wParam,lParam));
}
LRESULT CALLBACK FramInfo(HWND hVFW, LPVIDEOHDR VideoHdr)
{
HDC Hdc;
HDC hMemDC; //메모리DC
HBITMAP OldBitmap;
int height, width;
//비트맵 영상 정보
//영상출력을 위한 변수
VideoHdr_1=VideoHdr;
capPreviewRate(hVFW, 0); //프리뷰모드에서 보여질 프레임 속도를 0으로 설정함
// 즉 어떠한 처리가 다 끝나기 전에는 다시 콜백함수가 호출되게 하지 않는다
width4=Bm.bmiHeader.biWidth*3;
width4=WIDTH4(width4);
abc=Bm.bmiHeader.biHeight; //높이를 대입
Dataimage=(BYTE *)malloc(Bm.bmiHeader.biSizeImage); //복사받?nbsp;
for(iheight=0;iheight<Bm.bmiHeader.biHeight;iheight++)
{
def=Bm.bmiHeader.biWidth;
for(iwidth=0;iwidth<Bm.bmiHeader.biWidth;iwidth++)
{
Dataimage[(iheight*width4)+iwidth*3]=
VideoHdr_1->lpData[(abc*width4)+def*3];
Dataimage[(iheight*width4)+iwidth*3+1]=
VideoHdr_1->lpData[(abc*width4)+def*3+1];
Dataimage[(iheight*width4)+iwidth*3+2]=
VideoHdr_1->lpData[(abc*width4)+def*3+2];
def--;
}
abc--;
}
height = Bm.bmiHeader.biHeight; //캡쳐영상의 세로
width = Bm.bmiHeader.biWidth; //캡쳐영상의 가로
Hdc=GetDC(Hwndmain);
hMemDC = CreateCompatibleDC(Hdc);
OldBitmap = (HBITMAP)SelectObject(hMemDC,hBit);
MoveWindow(hWndMain,100,100,width+80,height*2+70,true);
SetDIBitsToDevice(Hdc,0,0,width,height,NULL,NULL,NULL,height,VideoHdr->lpData,&Bm,DIB_RGB_COLORS);
SetDIBitsToDevice(Hdc,0,height+10,width,height,NULL,NULL,NULL,height,Dataimage,&Bm,DIB_RGB_COLORS);
SelectObject(hMemDC, OldBitmap);
DeleteDC(hMemDC);
ReleaseDC(Hwndmain, Hdc);
capPreviewRate(hVFW,1); //다음 프레임에는 다시 영상처리를 시작 하게 된다.
return 0;
}