본문 바로가기
코스웨어/15년 스마트컨트롤러

20151124 임현수 업무일지 WIN32API #9 히스토그램 그래프 출력, 카메라 영상 출력

by 알 수 없는 사용자 2015. 11. 24.
728x90
반응형
■ WIN32API



■ 그래프 출력



그래프의 배경이 검정색으로 나오기 때문에 먼저 흰색으로 바꿔준다.

PatBlt는 이미 생성된 MemDC와 교차하는 영역을 어떤 색상으로 표현할지 지정하는 함수이다.

위 코드의 2~4번째 인자가 그리는 영역과 MemDC를 비교하고
겹치는 영역의 색상을 반전시키던가, 투명화하던가, 흰색으로 칠하던가 하는 함수이다.


▲ 교차영역을 흰색으로 칠하였다.


▲ X, Y축을 그린 상황






#include <windows.h>

#define XPOS  20
#define YPOS  20
#define SWIDTH  250
#define SHEIGHT  25
#define YGAP  10
#define XGAP  5

#define XGRAPH  256
#define YGRAPH  256

HINSTANCE g_hInst;
LPSTR lpszClass = L"Win32 api";

typedef struct _stMsgMAP
{
  UINT uiMsg;
  LRESULT(*fP)(WPARAM, LPARAM);
}stMsgMap;

BITMAPFILEHEADER stBFHead;
BITMAPINFOHEADER stBFInfo;


LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
void printBMPInfo(BITMAPFILEHEADER*, BITMAPINFOHEADER*);
LRESULT OnDestroy(WPARAM, LPARAM);
LRESULT OnCreate(WPARAM, LPARAM);
LRESULT OnPaint(WPARAM, LPARAM);
void drawBMP(void);

stMsgMap MsgMap[] =
{
  { WM_PAINT, OnPaint },
  { WM_CREATE, OnCreate },
  { WM_DESTROY, OnDestroy },
  { WM_NULL, 0 }
};

unsigned char *ucpData;

HWND hWnd;

unsigned int uiPad;

HDC MemDC;
static HBITMAP Screen;
static HBITMAP Histo;

unsigned int uiSumR[256];
unsigned int uiSumG[256];
unsigned int uiSumB[256];

int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance
  , LPSTR lpszCmdParam, int nCmdShow)
{
  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)WndProc;
  WndClass.lpszClassName = lpszClass;
  WndClass.lpszMenuName = NULL;
  WndClass.style = CS_HREDRAW | CS_VREDRAW;
  RegisterClass(&WndClass);

  hWnd = CreateWindow(lpszClass,
    lpszClass,
    WS_OVERLAPPEDWINDOW,
    CW_USEDEFAULT,
    CW_USEDEFAULT,
    CW_USEDEFAULT,
    CW_USEDEFAULT,
    NULL,
    (HMENU)NULL,
    hInstance,
    NULL);
  ShowWindow(hWnd, nCmdShow);

  while (GetMessage(&Message, 000))
  {
    TranslateMessage(&Message);
    DispatchMessage(&Message);
  }
  return Message.wParam;
}

LRESULT CALLBACK WndProc(HWND hwpWnd, UINT iMessage, WPARAM wParam, LPARAM lParam)
{
  HDC hdc;
  PAINTSTRUCT ps;
  hWnd = hwpWnd;

  stMsgMap *stpMap = MsgMap;

  while (WM_NULL != (*stpMap).uiMsg)
  {
    if (iMessage == (*stpMap).uiMsg)
    {
      return (((*stpMap).fP)(wParam, lParam));
    }
    ++stpMap;
  }

  return(DefWindowProc(hWnd, iMessage, wParam, lParam));
}

LRESULT OnCreate(WPARAM wParam, LPARAM lParam)
{
  HDC hdc;
  HDC MemDC;

  HANDLE hFile;
  BOOL BRet;
  DWORD dwCnt;

  hFile = CreateFile(TEXT("12.bmp")
    , GENERIC_READ
    , 0
    , NULL
    , OPEN_EXISTING
    , FILE_ATTRIBUTE_NORMAL
    , NULL);

  if (INVALID_HANDLE_VALUE == hFile)
  {
    MessageBox(hWnd, TEXT("파일을 열 수 없습니다."), TEXT("에러"), MB_OK);
    PostQuitMessage(0);
  }

  BRet = ReadFile(hFile, &stBFHead, sizeof(stBFHead), &dwCnt, NULL);
  if (FALSE == BRet)
  {
    MessageBox(hWnd, TEXT("파일헤더를 열 수 없습니다."), TEXT("에러"), MB_OK);
    CloseHandle(hFile);
    PostQuitMessage(0);
  }

  uiPad = (stBFInfo.biWidth % 4);

  BRet = ReadFile(hFile, &stBFInfo, sizeof(stBFInfo), &dwCnt, NULL);
  if (FALSE == BRet)
  {
    MessageBox(hWnd, TEXT("인포헤더를 열 수 없습니다."), TEXT("에러"), MB_OK);
    CloseHandle(hFile);
    PostQuitMessage(0);
  }

  printBMPInfo(&stBFHead, &stBFInfo);

  // 도스용에서 윈도우용으로 포팅했다.
  ucpData = (unsigned char *)malloc(stBFInfo.biSizeImage);
  if (ucpData < 0)
  {
    MessageBox(hWnd, TEXT("동적할당 실패"), TEXT("에러"), MB_OK);
    CloseHandle(hFile);
    PostQuitMessage(0);
  }

  _llseek(hFile, stBFHead.bfOffBits, SEEK_SET);
  BRet = ReadFile(hFile, ucpData, stBFInfo.biSizeImage, &dwCnt, NULL);
  if (FALSE == BRet)
  {
    MessageBox(hWnd, TEXT("데이터 Read 실패"), TEXT("에러"), MB_OK);
    free(ucpData);
    CloseHandle(hFile);
    PostQuitMessage(0);
  }

  drawBMP();

  CloseHandle(hFile);
  return 0;
}

LRESULT OnDestroy(WPARAM wParam, LPARAM lParam)
{

  DeleteDC(MemDC);
  PostQuitMessage(0);
  return 0;
}

LRESULT OnPaint(WPARAM wParam, LPARAM lParam)
{
  PAINTSTRUCT ps;
  HDC hdc;
  HDC MemDC;

  hdc = BeginPaint(hWnd, &ps);
  MemDC = CreateCompatibleDC(hdc);

  // 이미지 draw
  SelectObject(MemDC, Screen);
  BitBlt(hdc, XPOS, YPOS, stBFInfo.biWidth, stBFInfo.biHeight, MemDC, 00, SRCCOPY);
  DeleteDC(MemDC);

  // histo graph draw
  MemDC = CreateCompatibleDC(hdc);
  SelectObject(MemDC, Histo);
  BitBlt(hdc, XPOS + (SWIDTH * 2) + stBFInfo.biWidth + (XGAP * 3), YPOS, XGRAPH+1, YGRAPH, MemDC, 00, SRCCOPY);

  DeleteDC(MemDC);
  EndPaint(hWnd, &ps);
  return 0;
}

void printBMPInfo(BITMAPFILEHEADER* stpFH, BITMAPINFOHEADER* stpIH)
{
  int iCntY;
  HWND hHandle;

  WCHAR *ucTitle[14=
  {
    L"Magic Number",
    L"Size of File",
    L"Data Position",
    L"Size of BITMAPINFOHEADER",
    L"Width of Image",
    L"Height of Image",
    L"Bit Plane number",
    L"Bit Count",
    L"Compression",
    L"Size of Image",
    L"Resolution of Width (Pixel per meter)",
    L"
Resolution of Height (Pixel per meter)",
    L"
Number of Used Color",
    L"Index of Important Color"
  };

  WCHAR ucValue[14][30];

  wsprintf(ucValue[0], TEXT("[%c%c]")
    , *(((unsigned char *)(&stBFHead)) + 0)
    , *(((unsigned char *)(&stBFHead)) + 1));
  wsprintf(ucValue[1], TEXT("[%d] Bytes"), stBFHead.bfSize);
  wsprintf(ucValue[2], TEXT("[%d] Bytes"), stBFHead.bfOffBits);
  wsprintf(ucValue[3], TEXT("[%d] Bytes"), stBFInfo.biSize);
  wsprintf(ucValue[4], TEXT("[%d] Pixel"), stBFInfo.biWidth);
  wsprintf(ucValue[5], TEXT("[%d] Pixel"), stBFInfo.biHeight);
  wsprintf(ucValue[6], TEXT("[%d]"), stBFInfo.biPlanes);
  wsprintf(ucValue[7], TEXT("[%d]"), stBFInfo.biBitCount);
  wsprintf(ucValue[8], TEXT("[%d]"), stBFInfo.biCompression);
  wsprintf(ucValue[9], TEXT("[%d] Bytes"), stBFInfo.biSizeImage);
  wsprintf(ucValue[10], TEXT("[%d]"), stBFInfo.biXPelsPerMeter);
  wsprintf(ucValue[11], TEXT("[%d]"), stBFInfo.biYPelsPerMeter);
  wsprintf(ucValue[12], TEXT("[%d]"), stBFInfo.biClrUsed);
  wsprintf(ucValue[13], TEXT("[%d]"), stBFInfo.biClrImportant);

  for (iCntY = 0; iCntY < 14; ++iCntY)
  {
    CreateWindow(TEXT("static")
      , ucTitle[iCntY]
      , WS_CHILD | WS_VISIBLE
      , XPOS + stBFInfo.biWidth + XGAP
      , YPOS*(iCntY + 1) + (YGAP*iCntY)
      , SWIDTH
      , SHEIGHT
      , hWnd
      , (HMENU)-1
      , g_hInst
      , NULL);

    hHandle = CreateWindow(TEXT("static")
      , ucValue[iCntY]
      , WS_CHILD | WS_VISIBLE
      //WS_CHILD | WS_VISIBLE | WS_BORDER | ES_READONLY
      , XPOS + SWIDTH + XGAP + stBFInfo.biWidth + XGAP
      , YPOS*(iCntY + 1) + (YGAP*iCntY)
      , SWIDTH
      , SHEIGHT
      , hWnd
      , (HMENU)-1
      , g_hInst
      , NULL);
  }

  return;
}

void drawBMP(void)
{
  HDC hdc;
  HDC MemDC;

  int iCntX;
  int iCntY;


  unsigned int uiMaxVal;

  unsigned int uiCnt[256= { 0, };
  //unsigned int uiCntG[256] = { 0, };
  //unsigned int uiCntB[256] = { 0, };

  /*
  unsigned int uiSumR[256];
  unsigned int uiSumG[256];
  unsigned int uiSumB[256];
  */



  hdc = GetDC(hWnd);
  Screen = CreateCompatibleBitmap(hdc, stBFInfo.biWidth, stBFInfo.biHeight);  // 비트맵을 생성
  Histo = CreateCompatibleBitmap(hdc, XGRAPH + 1, YGRAPH);  // 비트맵을 생성

  MemDC = CreateCompatibleDC(hdc);
  SelectObject(MemDC, Screen); // Screen을 MemDC에 선택해준다. 즉 MemDC는 비트맵이 된다.

  for (iCntY = 0; iCntY < stBFInfo.biHeight; ++iCntY)
  {
    for (iCntX = 0; iCntX < stBFInfo.biWidth; ++iCntX)
    {
      SetPixel(MemDC, iCntX, (stBFInfo.biHeight - iCntY - 1)
        , RGB(*(ucpData + (iCntY * stBFInfo.biWidth * 3) + (iCntX * 3) + 2 + iCntY*uiPad)
        , *(ucpData + (iCntY * stBFInfo.biWidth * 3) + (iCntX * 3) + 1 + iCntY*uiPad)
        , *(ucpData + (iCntY * stBFInfo.biWidth * 3) + (iCntX * 3) + 0 + iCntY*uiPad)));

      // 평활화 1단계
      ++uiCnt[ucpData[iCntY * (stBFInfo.biWidth * 3) + iCntX * 3 + 2 + iCntY*uiPad]];
      ++uiCnt[ucpData[iCntY * (stBFInfo.biWidth * 3) + iCntX * 3 + 1 + iCntY*uiPad]];
      ++uiCnt[ucpData[iCntY * (stBFInfo.biWidth * 3) + iCntX * 3 + 0 + iCntY*uiPad]];
    }
  }

  uiMaxVal = 0;
  for (iCntX = 0; iCntX < XGRAPH; ++iCntX)
  {
    uiCnt[iCntX] = uiCnt[iCntX] / 3;

    if (uiMaxVal < uiCnt[iCntX])
    {
      uiMaxVal = uiCnt[iCntX];
    }
  }

  for (iCntX = 0; iCntX < XGRAPH; ++iCntX)
  {
    uiCnt[iCntX] = (uiCnt[iCntX] * XGRAPH) / uiMaxVal; // 0~255사이의 값을 갖는다. 오차를 줄이기 위해 곱하기를 먼저한다.
  }

  DeleteDC(MemDC);
  free(ucpData);

  MemDC = CreateCompatibleDC(hdc);
  SelectObject(MemDC, Histo);

  // PatBlt으로 배경을 흰색으로 바꾼다.
  PatBlt(MemDC, 00, XGRAPH + 1, YGRAPH, WHITENESS);

  for (iCntY = 0; iCntY < YGRAPH; ++iCntY)
  {
    for (iCntX = 0; iCntX < XGRAPH; ++iCntX)
    {
      // y축 draw
      SetPixel(MemDC, 0, iCntY, RGB(000));

      // y축 높이보다 값이 크면 점을 찍는다.
      if ( (YGRAPH-1-iCntY) <= uiCnt[iCntX] )
      {
        SetPixel(MemDC, (iCntX + 1), iCntY, RGB(000));
      }
    }
  }

  DeleteDC(MemDC);
  ReleaseDC(hWnd, hdc);
  return;
}

■ 라이브러리 땡기기


또는

#pragma comment(lib, "vfw32.lib")

■ capCreateCaptureWindow 함수원형


■ capDriverConnect 함수원형



LRESULT On_Create(WPARAM wParam , LPARAM lParam )
{
     HDC hdc;
     HWND hCamera;
     BOOL bRet;
     BITMAPINFO BInfo;

     hdc = GetDC(hWnd);

     // 화면을 띄울 창 마련
     hCamera = capCreateCaptureWindow(TEXT ("smart" ), WS_CHILD | WS_VISIBLE , 0, 0, XSCALE , YSCALE , hWnd, 0);
     if (NULL == hCamera)
     {
           MessageBox(hWnd, TEXT ("윈도우를 생성할 수 없습니다"), TEXT("에러"), MB_OK);
          PostQuitMessage(0);
     }
     
     // 카메라와 연결 2번째 인자는 장착된 카메라 갯수번호
     bRet = capDriverConnect(hCamera, 0);
     if (FALSE == bRet)
     {
           MessageBox(hWnd, TEXT ("카메라를 연결할 수 없습니다"), TEXT("에러"), MB_OK);
          PostQuitMessage(0);
     }

     // 화면에 보이는 속도
     capPreviewRate(hCamera, 1);

     // hCamera의 정보를 BInfo에 저장
     capGetVideoFormat(hCamera, &BInfo, sizeof(BInfo));

     // 카메라 해상도 셋팅
     BInfo.bmiHeader.biWidth = XSCALE;
     BInfo.bmiHeader.biHeight = YSCALE;

     // BInfo를 hCamera에 저장
     capSetVideoFormat(hCamera, &BInfo, sizeof(BInfo));

     //capSetCallbackOnFrame(hCamera, ????);

     // 출력
     capPreview(hCamera, TRUE);

     
     ReleaseDC(hWnd, hdc);
     return 0;
}




■ 영상 두배로 띄우기 테스트

지금까지는 단순히 카메라 영상을 띄우는 것만 가능하다.
카메라영상을 받아와서 데이터를 조작하기 위해서는 콜백함수(capDriverConnect)를 사용해야한다.

■ capSetCallBackOnFrame 함수원형


■ StrechDIBits 함수원형


■ VIDEOHDR 구조체 원형

▲ LPBYTE - 1바이트단위 포인터
LPBYTE lpData가 실제로 화면이다

▲ 12번째 인자 iUsage

■ CallBack 함수 capture()구현

LRESULT capture(HWND hwpWnd , LPVIDEOHDR lpVHdr )
{
     HDC hdc;
     hdc = GetDC(hWnd);
     StretchDIBits(hdc
          , XSCALE
          , 0
          , XSCALE
          , YSCALE
          , 0
          , 0
          , XSCALE
          , YSCALE
          , lpVHdr->lpData
          , &stBMPInfo
          , DIB_RGB_COLORS
          , SRCCOPY);
     ReleaseDC(hWnd, hdc);
     return 0;
}


■ RGB별로 출력


LRESULT capture(HWND hwpWnd , LPVIDEOHDR lpVHdr )
{
     HDC hdc;
     unsigned int uiCntX;
     unsigned int uiCntY;

     unsigned char cBuf[20];

     static BYTE vData[XSCALE * YSCALE * 3];

     hdc = GetDC(hWnd);

     memcpy(vData, lpVHdr->lpData, lpVHdr->dwBufferLength);

     for (uiCntX = 0; uiCntX < XSCALE; uiCntX++)
     {
           for (uiCntY = 0; uiCntY < YSCALE; uiCntY++)
          {
               //*(vData + (uiCntY * XSCALE + uiCntX) * 3 + 0) = 0; // B
              *(vData + (uiCntY * XSCALE + uiCntX) * 3 + 1) = 0;       // G
              *(vData + (uiCntY * XSCALE + uiCntX) * 3 + 2) = 0;       // R
          }
     }

     StretchDIBits(hdc
          , XSCALE
          , 0
          , XSCALE
          , YSCALE
          , 0
          , 0
          , XSCALE
          , YSCALE
          , vData
          , &stBMPInfo
          , DIB_RGB_COLORS
          , SRCCOPY);
     /////////////////////////////////////////////////////////////////////////
     memcpy(vData, lpVHdr->lpData, lpVHdr->dwBufferLength);

     for (uiCntX = 0; uiCntX < XSCALE; uiCntX++)
     {
           for (uiCntY = 0; uiCntY < YSCALE; uiCntY++)
          {
              *(vData + (uiCntY * XSCALE + uiCntX) * 3 + 0) = 0;   // B
               //*(vData + (uiCntY * XSCALE + uiCntX) * 3 + 1) = 0;     // G
              *(vData + (uiCntY * XSCALE + uiCntX) * 3 + 2) = 0;       // R
          }
     }

     StretchDIBits(hdc
          , 0
          , YSCALE
          , XSCALE
          , YSCALE
          , 0
          , 0
          , XSCALE
          , YSCALE
          , vData
          , &stBMPInfo
          , DIB_RGB_COLORS
          , SRCCOPY);
     /////////////////////////////////////////////////////////////////////////
     memcpy(vData, lpVHdr->lpData, lpVHdr->dwBufferLength);

     for (uiCntX = 0; uiCntX < XSCALE; uiCntX++)
     {
           for (uiCntY = 0; uiCntY < YSCALE; uiCntY++)
          {
              *(vData + (uiCntY * XSCALE + uiCntX) * 3 + 0) = 0;   // B
              *(vData + (uiCntY * XSCALE + uiCntX) * 3 + 1) = 0;       // G
               //*(vData + (uiCntY * XSCALE + uiCntX) * 3 + 2) = 0;     // R
          }
     }

     StretchDIBits(hdc
          , XSCALE
          , YSCALE
          , XSCALE
          , YSCALE
          , 0
          , 0
          , XSCALE
          , YSCALE
          , vData
          , &stBMPInfo
          , DIB_RGB_COLORS
          , SRCCOPY);
     
     //wsprintf(cBuf, TEXT("[%u]"), lpVHdr->dwBufferLength);
     //SetWindowText(hWnd, cBuf);

     ReleaseDC(hWnd, hdc);
     return 0;
}


728x90