본문 바로가기
코스웨어/10년 시스템제어

[시스템제어] 19번 이상은

by 알 수 없는 사용자 2010. 6. 8.
728x90
반응형

RFID 프로젝트
프로젝트명 : 출퇴근 관리 프로그램
API와  RFID 그리고 영상처리를 사용하여
회사의 출근기록 퇴근기록을 보다 쉽게 관리 할수 있게 하며 입사 사원을 관리하게 했다.
일단 간단한 대화상자를 통해서 만들어보았다.
=초안=

사원에대한 개인정보와 사진, 그리고 현재 시간, 출근시간, 퇴근시간,
새로입사한 사원을 위한 새로만들기 정보수정 출퇴근통계 기록 검색까지했다.
#include<windows.h>
#include
 "resource.h"
#include<time.h>
time_t NowTime;

HDC hdc;
PAINTSTRUCT ps;
SYSTEMTIME st;
TCHAR sTime[128];
BOOL CALLBACK AboutDlgProc(HWND hDlg,UINT iMessage, WPARAM wParam,LPARAM lParam);
LPCTSTR lpszClass = TEXT("First");

HINSTANCE g_hInst; 
UINT TimeState[10];
int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lPSZCmdParam, int nCmdShow)
{

  
  DialogBox(g_hInst,MAKEINTRESOURCE(IDD_Main),NULL,AboutDlgProc);
}
BOOL CALLBACK AboutDlgProc(HWND hDlg,UINT iMessage, WPARAM wParam,LPARAM lParam)
{
  switch(iMessage)
  {
  case WM_INITDIALOG:
    
    SetTimer(hDlg,1,1000,NULL);
    return TRUE;
  case WM_TIMER:
    GetLocalTime(&st);
    wsprintf(sTime,TEXT("%d년 %d월 %d일 [%d시:%d분:%d초]"),st.wYear,st.wMonth,st.wDay,st.wHour,st.wMinute,st.wSecond);
    InvalidateRect(hDlg,NULL,TRUE);
    return 0;
  case WM_PAINT:
    hdc=BeginPaint(hDlg,&ps);
    SetDlgItemText(hDlg,IDC_Time,sTime);
    EndPaint(hDlg,&ps);
    return 0;
  case WM_COMMAND:
    switch(LOWORD(wParam))
    {
    case IDCANCEL:
      EndDialog(hDlg,IDCANCEL);
      return TRUE;
    case IDC_Close:
      EndDialog(hDlg,IDCANCEL);
      return TRUE;
    }


  }
  return FALSE;

}
..... to be continue...
=====================================================================
============================================================================================
영상처리


기본적으로 API에서 영상을 처리하기 위해서는 어제까지 무식하게 한 포인터였지만
이제 좀더 직관적으로 파일을 열어서 그것을 동적할당을 받아서 데이터를 입력하는경우이다.
그런경우 3가지의 포인터가 필요하다. BITMAPFILEHEADER을 받는 포인터
BITMAPINFO를 받는 포인터 그리고 실질적인 비트맵 DATA를 받는 포인터....
test.bmp를 받았고 파일포인터로 file이 잡고 있다. 그리고
하나씩 크기만큼 동적할당을 했다. 
bmfh는 BITMAPFILEHEADER크기만큼 동적할당하고
binfo같은경우 BITMAPINFO크기가 조금 다르다. BITMAPINFOHEADER부분과 RGBQUAD부분이 함쳐진것과 같다. 그렇기 때문에 값을 더 크게 잡아줘야한다.
그리고 imageData같은경우 전체 그림 크기만큼 잡아야한다. 그것에 대한 크기가 BITMAPINFO안에 header안에 sizeimage에 있다. 그래서잡아준다.
그리고 fread로 실질적인 값을 넣어준다.
===========================================================================================
===========================================================================================
영상처리에 필요한 함수들
HWND VFWAPI capCreateCaptureWindow
 (
        LPCSTR lpszWindowName,  //Capture window의 이름 지정
        DWORD dwStyle,    //Capture window의 style지정
        int x, int y,       //Capture window의 시작점의 x,y좌표
        int nWidth, int nHeight,    //Capture window의 폭및 높이
        HWND hwndParent,    //Capture window의 핸들
        int nID      //Window ID
);
hVFW = capCreateCaptureWindow(TEXT("VFW"), WS_CHILD|WS_VISIBLE,0,0,1,1,hWnd,0);
일단  카메라캡쳐의 윈도우를 만들어주는 함수이다. window핸들을 반환하고 실패하면 NULL을 반환한다.
===============================================================

BOOL capDriverConnect
(
  hwnd,  //Capture window핸들
   i  //Capture Driver의 Index
);
카메라 캡쳐윈도우에 설치되어있는 카메라 드라이브를 연결시켜주는 매크로함수이다. computer에 연결되어지는 순서대로 카메라 0~9까지 index를 부여받게 된다.
======================================================================================
DWORD capGetVideoFormat
(
  hWnd,    //Capture window 핸들
  psVideoFormat,  //Video Format정보를 받을 BITMAPINFO 구조체의 포인터
  wSize    //BITMAPINFO구조체의 사이즈
);
현재 설정되어진 video format정보를 가져온다.
그래서 BITMAPINFO구조체의포인터와 크기를 받는다.
======================================================================================
DWORD capSetVideoFormat
(
  hWnd,    //Capture window 핸들
  psVideoFormat,  //Video Format정보를 받을 BITMAPINFO 구조체의 포인터
  wSize    //BITMAPINFO구조체의 사이즈
);
Get과 Set 사실 이경우는 직접으로 매크로 지정인데 format에서 카메라 드라이버를 지원하지 않을경우 FALSE를 반환한다.

======================================================================================
BOOL capPreviewRate
(
  hWnd,    //Capture window 핸들
  wMs    //Capture 간격에 대한 시간(millisecond 단위)
);
어떻게 보면 가장중요한 부분이다. 카메라로 읽은 속도와 관련이 있기때문에
1초에 30프레임을 잡는데  이경우는 1초에 사진을 30장을 을 찍는다와 같다 그래서 이 프레임 낮으면
뚝뚝 끊기게 되고 최대 33까지 올리면 움직임을 잡아 준다.
하지만 30프레임이라고 다좋은것은 아니다. 만약에 영상을 잡을때 빨간색만 잡을때 만약 화면전체가 빨간색이 되어버리면 영상을 읽어올때 폭주해버린다. 즉 한마디로 뻑가버린다. 그래서 이부분은 잘 설정해야한다.
======================================================================================
BOOL capSetCallbackOnFrame
(
  hWnd,    //Capture window핸들
  fpProc    //Capture Callback함수
);
우리가 윈도우에서 메시지를 처리하기위해서 하나하나 기다리면서 처리를 할수가없다 그래서
사용하는것이 CallBack함수를 사용한다.
 WndClass.lpfnWndProc = WndProc;
마찬가지로 영상을 처리할때 우리가 하나하나 대기를 할수없다 그래서 역시 마찬가지로
Callback함수를 불러들이는데 그함수가 바로
BOOL capSetCallbackOnFrame()이다.

======================================================================================
다음 프로그램은 영상을 PrintScreen처럼 캡쳐한는것을 만들었다.
아래 그림은 영상 캡쳐 화면이고



다음 그림이 캡쳐한 영상을 bmp로 만든경우이다.


간단하게 비트맵의 구조를 알게 되면 쉽다.
복습차원에서 비트맵파일의 구조에 대해서 보자

*.bmp 파일은 총 4가지 구역으로 나눌수 있다 원래 그냥 기계어로 010101처럼 되어있지만
보기 쉽게 설명하자면 딱 4군데이다 그리고 RGBQUAD부분은  24비트체제 그림일경우 0이기때문에
저부분이 없다. 그래서 fileheader와 info그리고 data다 부분으로 나누어진다.
=============================================================================================
typedef struct tagBITMAPFILEHEADER {
        WORD    bfType;  //비트맵이라는 뜻으로 BM즉 0x4D42가 들어있다.
        DWORD   bfSize;  //file의 전체 크기
        WORD    bfReserved1;  //사용하지 않음(미래확정성을위한예약)
        WORD    bfReserved2;  //사용하지 않음(미래확정성을위한예약
        DWORD   bfOffBits;  //raw 이미지 데이터 위치
BITMAPFILEHEADER, FAR *LPBITMAPFILEHEADER, *PBITMAPFILEHEADER;
총 bmp파일의 첫 14바이트 이며 각각 bfType으로 비트맵이라는 magic number "BM"이 들어있다.
=============================================================================================
typedef struct tagBITMAPINFO {
    BITMAPINFOHEADER    bmiHeader; //BITMAPINFOHEADER
    RGBQUAD             bmiColors[1];  //RGBQUAD구조체
BITMAPINFO, FAR *LPBITMAPINFO, *PBITMAPINFO;
IOFO는 INFOGEADER와 RGBQUAD구조체를 포함해서 가져온다.
=============================================================================================
typedef struct tagBITMAPINFOHEADER{
        DWORD      biSize;    //이 구조체의크기
        LONG       biWidth;    //이미지의 폭
        LONG       biHeight;    //이미지의 높이
        WORD       biPlanes;    //비트플레인수 항상1
        WORD       biBitCount;    //픽셀당 비트수
        DWORD      biCompression;  //압축유형
        DWORD      biSizeImage;  //이미지의크기
        LONG       biXPelsPerMeter;  //가로해상도
        LONG       biYPelsPerMeter;  //세로해상도
        DWORD      biClrUsed;    //실제 사용되는 색상수
        DWORD      biClrImportant;  //중요한 색상 인덱스 0,이면전체
BITMAPINFOHEADER, FAR *LPBITMAPINFOHEADER, *PBITMAPINFOHEADER;
비트 자체에 대한 정보를 저장하기 위한 구조체 이다 여기서  이미지의 폭 높이 그리고 이미지의크기가
중요하다.
=============================================================================================
typedef struct tagRGBQUAD {
        BYTE    rgbBlue;  //파란색성분
        BYTE    rgbGreen;  //녹색 성분
        BYTE    rgbRed;  //빨간색 성분
        BYTE    rgbReserved;  //예약된 값
} RGBQUAD;
팔레트를 저장하는 구조체이다 하지만 우리가쓰는 24bit 트루컬러일경우 사용하지 않는다.
이는 다음 시간에 더 자세히 나누겠다.
=============================================================================================
Raw Data
BITMAPFILEHEADER 안의 DWORD   bfOffBits 정보를 보게 되면 Raw Data의 대한 시작점을 알수 있다 팔레트가 없다면 BITMAPFILEHEADER+BITMAPINFOHEADER크기를 다음이 시작점임을 알수있다.
한픽셀의 3바이트씩 R G B 가 각각 들어가있다. 그래서 한점대한 칼라가들어가게 된다 하지만
만약 한픽셀에 대한 3바이트가 아닌 1바이트씩 들어있다면 이 그림은 흑백그림이 될것이다.
============================================================================================
기본적으로 영상처리를 하게 되면 주어진 영상의 BITMAPINFO부분이 capGetVideoFormat을 통해 알수 있다.
그리고 두번째 raw data는 윈도우에서 제공하는 vfw.h 알수있다.
callback함수의 두번째 인자로 되는 LRESULT CALLBACK FramInfo(HWND hVFW, LPVIDEOHDR VideoHdr) VideoHdr를 통해서 raw data를 알수가 있다.
그래서 스크린샷을 찍기위해서는 BITMAPFILEHEADER를 만들어주고 파일입출력으로 저장하면된다.
============================================================================================
핵심 부분은
FILE * file;
  file=fopen("test.bmp","wb");
  bmfh=(BITMAPFILEHEADER *)malloc(sizeof(BITMAPFILEHEADER));
  bmfh->bfType=0x4D42; //매직넘버
  bmfh->bfSize=(Bm.bmiHeader.biSizeImage+sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER)); //파일크기
  bmfh->bfReserved1=0;
  bmfh->bfReserved2=0;
  bmfh->bfOffBits=sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER);
  //raw data 시작 위치
  imageData=(BYTE *)malloc(Bm.bmiHeader.biSizeImage);
  imageData=VideoHdr_1->lpData; //실질적 raw data값
  
  fwrite(bmfh,sizeof(BITMAPFILEHEADER),1,file);
  fwrite(&Bm,sizeof(BITMAPINFOHEADER),1,file);
  fwrite(imageData,sizeof(BYTE),Bm.bmiHeader.biSizeImage,file);
  ============================================================================================

  fclose(file);






#include<Windows.h>
#include"vfw.h"

#include <math.h>
#include <iostream>

#pragma comment(lib, "vfw32.lib"//vfw32 라이브러리 포함



LPVIDEOHDR VideoHdr_1; //실제 이미지 데이터를 받기위한 변수
BITMAPFILEHEADER *bmfh; //비트맵헤더 포인터
BYTE * imageData; //비트맵이미지 포인터 

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;    //비트맵 정보를 가짐

//영상 메모리
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));
  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();
        break;

    }
    return 0;
  case WM_DESTROY:
    PostQuitMessage(0);
    free(bmfh);
    free(imageData);

    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으로 설정함
  
  // 즉 어떠한 처리가 다 끝나기 전에는 다시 콜백함수가 호출되게 하지 않는다


  height = Bm.bmiHeader.biHeight;  //캡쳐영상의 세로
  width = Bm.bmiHeader.biWidth;  //캡쳐영상의 가로
  
  Hdc=GetDC(Hwndmain);
  hMemDC = CreateCompatibleDC(Hdc);
  OldBitmap = (HBITMAP)SelectObject(hMemDC,hBit);
  MoveWindow(hWndMain,200,200,width+80,height,true);
  
  SetDIBitsToDevice(Hdc,0,0,width,height,NULL,NULL,NULL,height,VideoHdr->lpData,&Bm,DIB_RGB_COLORS);


  SelectObject(hMemDC, OldBitmap);
  DeleteDC(hMemDC);
  ReleaseDC(Hwndmain, Hdc); 
  capPreviewRate(hVFW,1); //다음 프레임에는 다시 영상처리를 시작 하게 된다.

  return 0;

}
728x90