메모용 글.

솔직히 내가 이걸 직접 설명할정도로 이해를 하진 못했다..

threshold는 '문턱'이라는 의미로, 한계치를 정해놓고 영상을 흑과 백으로 나누고 싶을때 쓴다. 이진화라고 하기도 함.

종류가 되게 다양한데 otsu threshold외에는 너무 진입장벽이 높다...(주관적)

통계적인 부분이 포함되어있는거같다. 난 일단 최대한 이해를 포기하도록했다....

4학년때 시뮬레이션 할 때가 생각나는거같긴한데...좋은기억이아니라서..


http://www.labbookpages.co.uk/software/imgProc/otsuThreshold.html

자바로 된 예제와 설명(영어의 압박은 논외로 치자)이 있다.


사용 예시.

원본, 이진화시킨 결과.

개요

 VFW는 마이크로소프트에서 개발한 라이브러리로, Video For Windows의 줄임말입니다. 이번 실험(?)의 목표는 VFW 함수를 이용하여 간단하게 웹캠의 영상을 추출해 화면에 보여주는 것입니다.


준비물

개발 환경 : VS2010

개발 언어 : C+Win32 API



코딩하기

1. VFW 라이브러리 추가하기

 라이브러리를 추가하는 방법은 두가지입니다. 하나는 소스 상단에 #pragma comment( lib, "vfw32.lib" )를 추가해 주는 것이고, 다른 하나는 프로젝트 링커 옵션에 추가하는 것입니다. VS2010에서 pragma를 쓰지 않고 라이브러리를 추가하는 법은 아래와 같습니다. 영문판 VS2010이기 때문에 한글판과 이름 등에 차이가 있을 수 있습니다. 굳이 vfw가 아니더라도, 모든 라이브러리가 같은 방법을 사용하니 참고하시기 바랍니다.




우선 vfw.h를 인클루드합니다.




프로젝트폴더 위에서 오른쪽 마우스를 클릭해 메뉴를 연 뒤, Properties 메뉴를 엽니다.




Linker->Input을 선택합니다.




Additional Dependencies의 라이브러리 목록을 클릭하면 ▼버튼이 생기는데, 이를 클릭합니다.




vfw32.lib를 추가합니다.




빌드에 성공했습니다.



2. WM_CREATE에서 설정하기



우선 캡쳐 윈도우 핸들을 생성하여 저장합니다.




웹캠과 연결하는 함수를 호출합니다.




이미지를 갱신하는 속도를 지정합니다.




비디오 포맷에 대한 정보를 biVFW 변수에 저장합니다.




직접 설정하면 번거롭기 때문에 기존 값에서 필요한 값만 수정합니다.




수정된 값을 반영합니다.




미리보기를 할 수 있게 해줍니다.




화면이 출력됩니다.

(실행시 Debug모드로 실행하면 에러가 뜨므로, Start without debugging(Ctrl+F5)으로 실행해 주어야 합니다.)


+. Win7에서는 정상작동을 하지 않는 것 같음...



소스 및 실행파일


main.c


VFWExample.exe


개요

 비트맵이란 래스터 그래픽스라고도 하며, 각 점 하나하나의 정보를 갖고 있습니다. 제일 기본적인 이미지 형식으로 모든 운영체제에서 사용할 수 있습니다.[각주:1] 이번에는 Win32 API의 구조체를 이용해 비트맵 파일의 구성을 알아내고 화면에 출력하는 것이 주된 목표입니다. 우선 비트맵 구조체들을 이용해 비트맵의 내용을 분석하는 것으로 시작합니다.



비트맵 파일의 구조[각주:2]

 제일 우선적으로 파일 시그니쳐인 BM이 앞에 옵니다. 매직 넘버라고도 하며, 이를 통해 이 파일이 bmp파일인지 아닌지를 알 수 있습니다. 그다음에는 파일의 크기와, 예약된 공간 두 개, 각 픽셀의 RGB값이 저장된 위치의 오프셋을 알 수 있는 값이 저장되어있습니다. 이를 구조체로 쉽게 접근할 수 있도록 한 것이 BITMAPFILEHEADER 입니다.

 BITMAPHEADER의 바로 뒤에 오는 것이 DIB 헤더입니다. BITMAPINFO 구조체를 이용하면 되며 이곳에는 이미지의 크기(파일크기가 아닌), 폭, 높이, 압축 형식, 색 등을 알 수 있습니다.

 BITMAPFILEHEADER에서 RGB값이 저장된 오프셋을 원래 위치에서 더하면 RGB값들이 나오는데, 3바이트가 한 픽셀의 색을 갖고 있고, BGR순으로 들어 있습니다. 만약 가로 300px, 세로 300px의 비트맵 이미지가 있다면 300*300 = 90000, 90000*3 = 270000Bytes의 RGB 정보가 저장되어 있겠네요.


<비트맵의 상세한 구조 그림[각주:3]>



코딩하기

 여기서 쓸 비트맵 구조체는 BITMAPFILEHEADER, BITMAPINFO의 두가지가 있으며, 이 두 구조체는 Windows.h를 인클루드하여 사용할 수 있습니다. 이 프로그램은 화면에 비트맵 이미지를 출력해야 하므로 기본적인 WinAPI 창 프로그램 소스가 필요합니다. 프로그램의 기본 흐름은 아래와 같습니다.


1. BITMAPFILEHEADER와 BITMAPINFO 각각의 포인터를 만들어 준다(인스턴스가 아닌 포인터이다)

2. 파일을 연다

3. 파일을 버퍼에 읽어들인다(BITMAPFILEHEADER와 BITMAPINFO의 크기를 합한 정도가 적당하다)

4. 앞에서 만들어 둔 BITMAPFILEHEADER의 포인터가 버퍼의 시작지점을 가르키고, BITMAPINFO는 버퍼의 시작지점에서 sizeof( BITMAPFILEHEADER )만큼 떨어진 곳을 가르키도록 한다

5. 파일의 커서를 BITMAPFILEHEADER의 멤버 변수인 bfOffBits의 값만큼 이동시킨다.

6. BITMAPINFO의 bmiHeader.biSizeImage만큼 malloc을 이용해 공간을 확보한다

7. 확보한 공간에 파일을 읽어들여 RGB정보를 메모리 내부에 저장한다.

8. WM_PAINT문에서 반복문과 SetPixel함수등의 함수를 이용해 화면에 출력한다. (여기서는 SetPixel사용)

-> 값은 unsigned char *형 변수를 선언하여 위치를 이동하며 찍는 것이 좋으며, 해당 변수의 이름을 x라고 했을 때 RGB( x+2, x+1, x )식으로 색을 지정해야 한다.

9. 확보된 공간을 해제시켜준다.


전체적인 절차는 이와 같으며, 자세한 정보는 각주의 msdn문서를 참고하여 작성할 수 있습니다.

이 때, 화면에 찍힌 사진은 아래와 같을 것입니다.(출력이 이상하게 되거나 터진다면 이미지를 300*300으로 하시기 바랍니다)


<원본 이미지>


<출력된 이미지>


왜 이렇게 나오는 것일까!? 이유는 비트맵이 거꾸로 저장되어 있기 때문입니다. 만약 출력을 아래와 같은 방법으로 했다면


ucpData = ucBitmap;

for( y=0; y>uiYSize; y++ )
{
  for( x=0; x<uiXSize; x++ )
  {
    SetPixel( hDC, x, y, RGB( *(ucpData+2), *(ucpData+1), *(ucpData) ) );
    ucpData = ucpData + 3;
  }
}


for( y=uiYSize; y>0; y-- )


로 고쳐보기 바랍니다.. 아래부터 출력이 되어 출력이 정상적으로 될 것입니다.


 그런데... 이 상태에서 가로의 길이를 299나 298, 297 등으로 지정하면 미친듯이 깨지거나 대각선으로 쪼개져 나오는 등의 이상 현상을 발견할 수 있습니다. 이는 컴퓨터가 최적화를 위해 4바이트 단위로 데이터를 저장하여 생기는 문제인데요, 실제로는 존재하지 않는 픽셀이지만 남는 공간을 00으로 채워넣어 비트가 밀려나버려 이상한 출력이 되는 것입니다.(RGB값은 3바이트로 저장되고 3바이트가 실제로 하나의 픽셀므로, 1바이트만 미끌어져도 대형 참사가 일어납니다.)

 이것은 가로 길이에서 4를 나눈 나머지 값을 구하여 x의 반복문이 끝난 직후에 커서를 저 만큼 더 이동해주면 됩니다.


ucpData = ucBitmap;

for( y=uiYSize; y>0; y-- )
{
  for( x=0; x<uiXSize; x++ )
  {
    SetPixel( hDC, x, y, RGB( *(ucpData+2), *(ucpData+1), *(ucpData) ) );
    ucpData = ucpData + 3;
  }
  ucpData = ucpData + ( uiXSize%4 );//이동해주었다!!
}



이미지 조작하기

 저장된 RGB값을 조금씩조금씩 조작하여 독특하게 띄울 수 있습니다. 비트맵도 영상의 하나이니, 영상처리의 기초가 될 수 있겠습니다. 자주 보는 이미지 조작 중 '흑백'과 '역상' 등등을 들 수 있는데요, 이를 한번 직접 구현하여 봅시다.


흑백



 우선 흑백은 절대 검정~흰색 사이의 색 외에 다른 색이 쓰이지 않습니다. 그러므로 원래 갖고 있는 RGB값의 어두운 정도를 구해야 하는데요, 제일 쉽게 생각해낸 방법은 '평균내기'입니다. ( R의 값+B의 값+G의 값 ) / 3으로 구해진 값을 R,G,B부분에 넣어서 출력하는 것입니다.


빨강 색조



 아까의 흑백을 조금 더 응용하여 빨간 색조로 출력하도록 바꾸어 보았습니다. 단순히 평균을 내어 R부분에만 값을 넣고 나머지 부분에는 0을 넣었습니다. 하지만 색이 다소 어두워져 보정을 해주어야 하는 것 같습니다.


특정 색 강조



 흑백을 또 조금조금 꼬아서.... 노란색만 추출(?)해본 것입니다. 어정쩡하게 추출한 것이라 군데군데 오류가 보이네요.. R+G는 노랑색이다보니 이 두 값의 평균을 내어 100이상이면 노란색으로 간주, 출력하도록 하고 나머지는 흑백처리를 하도록 하였습니다. 원본과 비교해 보면 푸른 계열인 멜빵은 회색계열로, 노란색인 살색은 그대로 나오는 것을 볼 수 있습니다.


역상



 재미들려서 막 하게 되는군요. 하지만 사실 제일 처음 시도한 건 역상이었습니다.ㅋㅋ;;

역상은 모든 색을 반전시켜 출력하는 것으로, 간단히 1을 비트연산 XOR하거나 틸트시켜주면 됩니다.

깜찍한 캐릭터들이 좀 무서워졌네요; 이로써 모든 정리를 끝마칩니다.



※이번에 적은 내용은 순수 제 주관으로 작성한 내용이기 때문에 실제 영상처리와 틀린 점이 있을 수 있습니다만, 연습삼아 작성하게 되었습니다.



참고자료

  1. http://ko.wikipedia.org/wiki/%EB%B9%84%ED%8A%B8%EB%A7%B5 [본문으로]
  2. http://msdn.microsoft.com/en-us/library/windows/desktop/dd183392(v=vs.85).aspx [본문으로]
  3. http://www.yaldex.com/games-programming/0672323699_ch07lev1sec7.html [본문으로]

+ Recent posts