2010년/6월

6월9일 RFID( 프로젝트) 영상처리(영상뒤집기)

뽀얀햄스터 2010. 6. 9. 16:40


그림이나 영상같은경우
칼라이기때문에
폭과 높이 그리고 색상을 곱해서 나오게되면..전체 크기와 같아야한다.


예를 들면 폭 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
        300300, 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
    300300, 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;

}