6월14일 영상처리(선형보간 영상 축소/확대)

2010. 6. 14. 18:182010년/6월

선형보간 영상 축소/확대

영상을 확대하거나 축소할때 여러가지 방법이 있지만

축소할때 1/4로 줄일경우

특히 저번에 배운경우 픽셀의 4개의 값을 평균으록 구해서 나타내었다.

하지만 그외에 방법으로 픽셀한개의 값만 넣고 나머지 3개를 버리거나

3개를 평균내고 하나를 버리거나.

여러가지 방법이 있다.

이경우가 바로 평안화라고 할수 있다.

하지만 평안화가 만능은 아니다

다음 그림을 보자




중간이 원본화면이고 왼쪽과 오른쪽은 원본화면을 1.5배로 확대 시킨화면이다. 
자세히는 몰라도 약간 오른쪽 픽셀의 울퉁불퉁한것이 너무 각이 져있고 거칠어있다.
왼쪽은 선형보간을 한경우이고 오른쪽은 그냥 평안화만 한것이다.

개인적으로 선형보간과 평안화를 보자면...선형보간은 평안화를 하고 다시 좀더 주위의값을 평안화하는것 같다. 그래서 색상부분의 들쭉한부분을 아마 그렇다고 값을 많이 참조한다면... 그림이 이상하겠지만 일정한 값을 하게된다면 픽셀과 픽셀사이가 부드럽게 보이는것이다.

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

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


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

#define  SQRT1_2  0.70710678118654752440
#define  PI   3.14159265358979323846

#define WIDTH4(width) ((width+3)>>2)<<2
#define ID_BCAPTURE 101

#define ZOOMSIZE 1
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
LRESULT CALLBACK FramInfo(HWND, LPVIDEOHDR); // 콜백 함수
BITMAPINFO* ChangeBitmapInfo(LONG width,LONG height,const BITMAPINFO* binfo);


TCHAR str[10];    //정수데이터값 확인을 위한 버퍼
unsigned char Menuflag;  //인터페이스 선택 

HINSTANCE g_hInst;
HWND hWndMain;
HWND hVFW;
HWND Hwndmain;
HBITMAP hBit;
BITMAPINFO Bm;    //비트맵 정보를 가짐

//영상 메모리
int Action;

LPCTSTR lpszClass = TEXT("VFW 기본 예제");

BYTE *imageData;
BITMAPFILEHEADER bmfh;
BITMAPINFO *binfo;
LONG width4;

// 변환할 정보
BITMAPINFO *chbinfo;
BYTE *chData;

BITMAPINFO* ChangeBitmapInfo(LONG width,LONG height,const BITMAPINFO* binfo,int i){
  
  BITMAPINFO* chbinfo;
  BITMAPINFO* blbinfo;
  LONG width4;
  
  if(i==24){ // 크기를 변환한 bitmapinfo구조체를 리턴해 준다.
    
    chbinfo = (BITMAPINFO *) malloc (sizeof(BITMAPFILEHEADER)+sizeof(RGBQUAD));
    
    chbinfo->bmiHeader.biBitCount = 24;
    chbinfo->bmiHeader.biClrImportant =binfo->bmiHeader.biClrImportant;
    chbinfo->bmiHeader.biClrUsed = binfo->bmiHeader.biClrUsed;
    chbinfo->bmiHeader.biCompression = binfo->bmiHeader.biCompression;
    chbinfo->bmiHeader.biHeight = height;
    chbinfo->bmiHeader.biPlanes = binfo->bmiHeader.biPlanes;
    chbinfo->bmiHeader.biSize =   binfo->bmiHeader.biSize;
    chbinfo->bmiHeader.biWidth = width;
    
    width4 = width *3;     //폭 맞춤
    width4 = ((width4+3)>>2)<<2;
    
    chbinfo->bmiHeader.biSizeImage = width4 * height;
    chbinfo->bmiHeader.biXPelsPerMeter = binfo->bmiHeader.biXPelsPerMeter;
    chbinfo->bmiHeader.biYPelsPerMeter = binfo->bmiHeader.biYPelsPerMeter;
    return  chbinfo;
  }
  else if(i==8)
  {
    blbinfo=(BITMAPINFO *) malloc (sizeof(BITMAPFILEHEADER)+sizeof(RGBQUAD));

    blbinfo->bmiHeader.biBitCount=i;
    blbinfo->bmiHeader.biSize=binfo->bmiHeader.biSize;
    blbinfo->bmiHeader.biWidth=width;
    blbinfo->bmiHeader.biHeight=binfo->bmiHeader.biHeight;
    
    width4 = width *3;     //폭 맞춤
    width4 = ((width4+3)>>2)<<2;

    blbinfo->bmiHeader.biXPelsPerMeter=0;
    blbinfo->bmiHeader.biYPelsPerMeter=0;
    blbinfo->bmiHeader.biSizeImage=width4*height;
    blbinfo->bmiHeader.biPlanes=1;
    blbinfo->bmiHeader.biClrUsed=256;
    blbinfo->bmiHeader.biCompression=0;
    blbinfo->bmiHeader.biClrImportant=0;
    for(i=0;i<256;++i)
    {
      blbinfo->bmiColors[i].rgbBlue=blbinfo->bmiColors[i].rgbGreen=blbinfo->bmiColors[i].rgbRed=i;
      blbinfo->bmiColors[i].rgbReserved=0;
    }
    
    return blbinfo;
  }  
  
}

BYTE* ReSizeImage(BITMAPINFO *binfo,const BITMAPINFO* cbinfo,const BYTE* Data,int i)
{
  
  
  BYTE newValue,R,G,B;
  BYTE* chData;
  int new_height=binfo->bmiHeader.biHeight;//새로운 이미지의 높이 계산 
  int new_width=binfo->bmiHeader.biWidth;//새로운 이미지의 폭 계산 
  int heightm1=cbinfo->bmiHeader.biHeight-1;
  int widthm1=cbinfo->bmiHeader.biWidth-1;
  int where,org_where;
  int r,c;//타겟 이미지 좌표 
  float r_orgr,r_orgc;//원 이미지 상의 해당 좌표 (실수값)
  int i_orgr,i_orgc;//원 이미지 상의 해당 좌표 (정수값)
  float sr,sc;// 예 1.24-1=0.24
  float I1,I2,I3,I4;
  float zoominfactor = (float)binfo->bmiHeader.biWidth/cbinfo->bmiHeader.biWidth;
  unsigned int count=0;
  long width4 = binfo->bmiHeader.biWidth*3;
  width4 = WIDTH4(width4);
  
  long cwidth4 = cbinfo->bmiHeader.biWidth * 3;
  cwidth4 = WIDTH4(cwidth4);
  
  //Zoom Image를 위한 동적 메모리 할당
  
  chData=(BYTE* )malloc(binfo->bmiHeader.biSizeImage);
  
  if(i==8)
  {
    for(r=0;r<new_height;r++)
    {
      r =r;
      for(c=0;c<new_width;c++)
      {
        r_orgr=r/zoominfactor;
        r_orgc=c/zoominfactor;
        i_orgr=floor(r_orgr);//예: floor(2.8)=2.0
        i_orgc=floor(r_orgc);
        sr=r_orgr-i_orgr;
        sc=r_orgc-i_orgc;
        //범위 조사 
        //원이미지의 범위를 벗어나는 경우 0값 할당 
        if(i_orgr<0 || i_orgr>heightm1 || i_orgc<0 || i_orgc>widthm1)
        {
          where=r*width4+c;
         chData[where]=0;
          chData[where+1]=0;
          chData[where+2]=0;
          
        }
        //원 이미지의 범위 내에 존재 이중 선형 보간 수행 
        else
        { 
          count++;
          where=r*width4+c;
          
          I1=(float)Data[(cwidth4*i_orgr)+i_orgc*3+0]; //(org_r,org_c)
          I2=(float)Data[(cwidth4*i_orgr)+(i_orgc+1)*3+0];//(org_r,org_c+1)
          I3=(float)Data[(cwidth4*(i_orgr+1))+(i_orgc+1)*3+0];//(org_r+1,org_c+1)
          I4=(float)Data[(cwidth4*(i_orgr+1))+(i_orgc+1)*3+0];//(org_r+1,org_c)
          
          //이중 선형 보간을 통한 새로운 밝기값 계산
          R=(BYTE)(I1*(1-sc)*(1-sr)+I2*sc*(1-sr)+I3*sc*sr+I4*(1-sc)*sr);
          // newValue = (I1+I2+I3+I4)/4;
        //  chData[where]=newValue;
          
          I1=(float)Data[(cwidth4*i_orgr)+i_orgc*3+1];//(org_r,org_c)
          I2=(float)Data[(cwidth4*i_orgr)+(i_orgc+1)*3+1];//(org_r,org_c+1)
          I3=(float)Data[(cwidth4*(i_orgr+1))+(i_orgc+1)*3+1];//(org_r+1,org_c+1)
          I4=(float)Data[(cwidth4*(i_orgr+1))+(i_orgc+1)*3+1];//(org_r+1,org_c)
          
          //이중 선형 보간을 통한 새로운 밝기값 계산
          G=(BYTE)(I1*(1-sc)*(1-sr)+I2*sc*(1-sr)+I3*sc*sr+I4*(1-sc)*sr);
          //newValue = (I1+I2+I3+I4)/4;
          //chData[where+1]=newValue;
          
          
          I1=(float)Data[(cwidth4*i_orgr)+i_orgc*3+2];//(org_r,org_c)
          I2=(float)Data[(cwidth4*i_orgr)+(i_orgc+1)*3+2];//(org_r,org_c+1)
          I3=(float)Data[(cwidth4*(i_orgr+1))+(i_orgc+1)*3+2];//(org_r+1,org_c+1)
          I4=(float)Data[(cwidth4*(i_orgr+1))+(i_orgc+1)*3+2];//(org_r+1,org_c)
          
          //이중 선형 보간을 통한 새로운 밝기값 계산
          B=(BYTE)(I1*(1-sc)*(1-sr)+I2*sc*(1-sr)+I3*sc*sr+I4*(1-sc)*sr);
          //newValue = (I1+I2+I3+I4)/4;  //이중 선형 보간을 안할시 test 코드
          //chData[where+2]=newValue; 
          chData[where]=(R+G+B)/3;
        }
      }
    }
  }
  else if(i==24)
  {
    for(r=0;r<new_height;r++)
    {
      r =r;
      for(c=0;c<new_width;c++)
      {
        r_orgr=r/zoominfactor;
        r_orgc=c/zoominfactor;
        i_orgr=floor(r_orgr);//예: floor(2.8)=2.0
        i_orgc=floor(r_orgc);
        sr=r_orgr-i_orgr;
        sc=r_orgc-i_orgc;
        //범위 조사 
        //원이미지의 범위를 벗어나는 경우 0값 할당 
        if(i_orgr<0 || i_orgr>heightm1 || i_orgc<0 || i_orgc>widthm1)
        {
          where=r*width4+c*3;
          chData[where]=0;
          chData[where+1]=0;
          chData[where+2]=0;
        }
        //원 이미지의 범위 내에 존재 이중 선형 보간 수행 
        else
        { 
          count++;
          where=r*width4+c*3;
          
          I1=(float)Data[(cwidth4*i_orgr)+i_orgc*3+0]; //(org_r,org_c)
          I2=(float)Data[(cwidth4*i_orgr)+(i_orgc+1)*3+0];//(org_r,org_c+1)
          I3=(float)Data[(cwidth4*(i_orgr+1))+(i_orgc+1)*3+0];//(org_r+1,org_c+1)
          I4=(float)Data[(cwidth4*(i_orgr+1))+(i_orgc+1)*3+0];//(org_r+1,org_c)
          
          //이중 선형 보간을 통한 새로운 밝기값 계산
          newValue=(BYTE)(I1*(1-sc)*(1-sr)+I2*sc*(1-sr)+I3*sc*sr+I4*(1-sc)*sr);
          // newValue = (I1+I2+I3+I4)/4;
          chData[where]=newValue;
          
          I1=(float)Data[(cwidth4*i_orgr)+i_orgc*3+1];//(org_r,org_c)
          I2=(float)Data[(cwidth4*i_orgr)+(i_orgc+1)*3+1];//(org_r,org_c+1)
          I3=(float)Data[(cwidth4*(i_orgr+1))+(i_orgc+1)*3+1];//(org_r+1,org_c+1)
          I4=(float)Data[(cwidth4*(i_orgr+1))+(i_orgc+1)*3+1];//(org_r+1,org_c)
          
          //이중 선형 보간을 통한 새로운 밝기값 계산
          newValue=(BYTE)(I1*(1-sc)*(1-sr)+I2*sc*(1-sr)+I3*sc*sr+I4*(1-sc)*sr);
          //newValue = (I1+I2+I3+I4)/4;
          chData[where+1]=newValue;
          
          
          I1=(float)Data[(cwidth4*i_orgr)+i_orgc*3+2];//(org_r,org_c)
          I2=(float)Data[(cwidth4*i_orgr)+(i_orgc+1)*3+2];//(org_r,org_c+1)
          I3=(float)Data[(cwidth4*(i_orgr+1))+(i_orgc+1)*3+2];//(org_r+1,org_c+1)
          I4=(float)Data[(cwidth4*(i_orgr+1))+(i_orgc+1)*3+2];//(org_r+1,org_c)
          
          //이중 선형 보간을 통한 새로운 밝기값 계산
          newValue=(BYTE)(I1*(1-sc)*(1-sr)+I2*sc*(1-sr)+I3*sc*sr+I4*(1-sc)*sr);
          //newValue = (I1+I2+I3+I4)/4;  //이중 선형 보간을 안할시 test 코드
          chData[where+2]=newValue; 
        }
      }
    }
  }
  return chData;
}


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;
  FILE *file;
  LONG width, height;
  int count=0;

  switch(iMessage)
  {
  case WM_CREATE:
    
    Hwndmain = hWnd;
    
    Hdc = GetDC(hWnd);
    
    file=fopen("test.bmp","rb");
    
    if(file==NULL)
      return 0;
    
    
    //BITMAPINFO 
    binfo = (BITMAPINFO *)malloc(sizeof(BITMAPINFOHEADER)+(sizeof(RGBQUAD)*256));
    //초기화
    memset(binfo,0,sizeof(BITMAPINFOHEADER)+sizeof(RGBQUAD)*256);
    //이미지 데이터가 보관될 메모리 할당
    
    
    //비트맵 파일 헤더 읽음 
    fread(&bmfh,sizeof(BITMAPFILEHEADER),1,file);
    //비트맵 인포 헤더를 읽음
    fread(&(binfo->bmiHeader),sizeof(BITMAPINFOHEADER),1,file);
    
    imageData = (BYTE*)malloc(binfo->bmiHeader.biSizeImage);
    //이미지 데이터를 읽음
    fread(imageData,sizeof(BYTE),binfo->bmiHeader.biSizeImage,file);
    
    fclose(file);
    
    //변환할 가로 세로의 크기 Width
    
    LONG chwidth,chheight;
    chwidth = binfo->bmiHeader.biWidth *ZOOMSIZE;
    chheight = binfo->bmiHeader.biHeight *ZOOMSIZE;
    
    //bitmapinfo의 size를 바꾸어서 받아온다.
    chbinfo = ChangeBitmapInfo(chwidth,chheight,binfo,8);
  
    //imageData의 사이즈를 바꾸어 준다.
    chData = ReSizeImage(chbinfo,binfo,imageData,8); 
    
    return 0;
  case WM_COMMAND: 
    return 0;
  case WM_LBUTTONDOWN:
    Hdc = GetDC(hWnd);
    
    width = (unsigned int)binfo->bmiHeader.biWidth;
    height = (unsigned int)binfo->bmiHeader.biHeight;
    
    
    MoveWindow(hWndMain,200,200,width*2+15,height,true);
    
    SetDIBitsToDevice(Hdc,0,0,width,height,
      NULL,NULL,NULL,height,imageData,
      binfo,DIB_RGB_COLORS);
    
    if(chData!=NULL){
      SetDIBitsToDevice(Hdc,width+5,0,chbinfo->bmiHeader.biWidth,chbinfo->bmiHeader.biHeight,
        NULL,NULL,NULL,chbinfo->bmiHeader.biHeight,chData,
        chbinfo,DIB_RGB_COLORS);
    }
    ReleaseDC(hWnd,Hdc);
    return 0;
  case WM_DESTROY:
    if(chData!=NULL){
      free(chData);
      chData=NULL;
    }  
    if(imageData!=NULL){
      free(imageData);
      imageData =NULL;
    }
    free(binfo);
    PostQuitMessage(0);
    return 0;
  }
  return (DefWindowProc(hWnd,iMessage,wParam,lParam));
}