YUVPlayer 업데이트…

사실 저나 제 주위 사람들 말고는 쓰는 사람이 거의 없는거 같긴 하지만 하여튼 메모리 릭을 일으키는 몇 가지 버그를 잡았습니다.

  1. ::GetDC(hWnd) 후 ::ReleaseDC(hWnd,dc) 를 호출 하지 않아서 생기는 메모리 릭
  2. gdTexImage2D 를 반복 호출해서 생기게 되는 메모리 릭

정확하게 설명하면 위와 같구요. ::GetDC 로 받아온 Device Context 는 “꼭” ::ReleaseDC 를 호출해줘야 한다는 msdn 님의 가르침에 따라, 약간의 코드를 추가해줬습니다.

또한 gdTexImage2D 를 반복해서 호출하면 이전 텍스쳐 데이타가 사용하던 메모리 영역은 해제가 될 줄 알았는데, 실제로는 그렇지가 않네요. 텍스쳐 사이즈가 달라지는 경우엔 glDestroyTexture 후 glGenTexture, glBindTexture, glTexImage2D 를 차례로 호출해줘야 하고, 사이즈가 달라질 필요가 없는 경우라면 gdTexSubImage2D 를 사용하면 된답니다. 어려운 openGL 세상이에요.

자세한 수정 사항은 제 trac 페이지에서 확인하심 될 듯~

http://trac.unfix.net/browser/yuvplayer/win/yuvplayer/OpenGLView.cpp

p.s) trac 이 ajax 를 활용하도록 업데이트 되었네요.

CG: 3D Image Rotation

며칠 전 Perspective Projection 을 정리해놓은 김에 3D Image Rotation 도 정리를 해볼까 싶습니다.

Rotation Matrix

3D 이미지 회전은 아래와 같은 행렬을 통해 새로운 좌표를 계산할 수 있습니다. 또한 이 행렬들은 모두 unitary matrix 이기 때문에 Transpose 를 취해줌으로 역행렬을 쉽게 구할 수 있습니다.

z축 기준: (xy 평면에서의 회전)

y축 기준: (zx 평면에서의 회전)

x축 기준: (yz 평면에서의 회전)

Implementation of Image Rotation

처음에는 3차원 공간을 3차원 배열을 사용하여 모델링한 뒤 실제 3차원 좌표를 모두 뒤지며 forward/backward mapping 하는 방법을 통해 3D image rotation 을 구현해보았습니다. 3차원 배열을 이용 512×512 사이즈의 lena image 를 회전시키려면 (512*1.414)^3 만큼의 공간이 필요하게 되고, 저 공간을 모두 뒤지려면 계산 복잡도가 엄청나더군요.

실제 이 방법을 통해 이미지를 회전 시키는 데 ‘분’ 단위 시간이 필요했던 것으로 기억합니다. 게다가 변환을 반복할 수록 이미지의 디그라데이션이 심해졌기 때문에 이건 아니라는 생각이 들더군요. 이런 경우 이미 잘 설계되어 있는 그래픽 라이브러리를 참고하는 것이 좋기 때문에 OpenGL 의 인터페이스를 살펴보며 어떤 식으로 구현하면 좋을 지 생각하기 시작했습니다.

뭐 어짜피 화면이나 이미지로 보여주기 위해선 2D 평면에 projection 하는 것이 필요하므로 매 번 이미지 자체를 돌리기 보다 축을 회전시키고, 마지막에 그 축을 이용해서 원래 이미지를 새로운 좌표 공간으로 매핑시켜주면 되겠다는 결론을 얻었습니다.

우선 x, y, z 좌표를 identity matrix 로 표현한 뒤 R^T * AXIS 를 통해 새로운 축 AXIS’ 를 구할 수 있고, 회전을 시키고 싶은 만큼 위 연산을 반복해준 뒤 forward mapping 을 해주는 것으로 빠르고 훌륭한 품질을 보여주도록 구현하는걸 성공했습니다.

위 이미지는 512×512 사이즈의 lena 이미지를 z축을 기준으로 45도만큼 회전시킨 결과입니다. 왼 쪽은 단순히 forward mapping 을 해준 것이고 오른쪽은 weighted sum 을 이용해서 forward mapping 을 개선해준 것입니다.

결과적으로 Photoshop 등을 이용한 만큼 훌륭한 이미지를 얻어낼 수 있는 것을 확인할 수 있습니다.

Other Results

아래 이미지는 x 축, y축, z축을 기준으로 순서대로 30도씩 회전시킨 이미지입니다. 이런 식으로 계산을 하려면 순서를 뒤집어서 z축, y축, x 축 기준으로 30도씩 회전을 시켜주면 됩니다.

다음은 x축으로 30도, y축으로 60도 만큼 돌린 결과

실제 구현 코드에 관심이 있으신 분들은 아래 링크를 방문하시면 되겠습니다. 이런 걸 하나하나 구현해볼 때마다 느끼는 거지만 openGL 같은 라이브러리를 설계하신 분들은 상상하기 힘들 정도로 똑똑한 것 같아요.

소스:
https://github.com/Tee0125/snippet/tree/master/rotation3d

YUVplayer for MS Windows

지난 이틀간 작업한 내 YUVPlayer for windows~! MFC + OpenGL 기반으로 작성되었고, 정말 아무 문제 없이 사용할 수 있을만큼 완성도 높게 작업해보기는 처음이 아닐까 싶다. (CUI 기반으로는 공개를 하고 있지는 않았지만 혼자 유용하게 쓰는게 몇 가지 있는데… GUI 기반으로는 정말 처음인 것 같다.)

단축키를 통해 모든 액션을 취할 수 있고, 드래그앤 드롭을 통한 파일 오픈 또한 가능하다. 게다가 _lseeki64 같이 64bit offset 을 사용하는 시스템 콜을 사용하고 있기 때문에 2GB 를 넘어가는 파일들도 문제 없이 플레이가 가능하다. (31GB 짜리 파일도 문제 없이 플레이가 가능한 것을 확인했음.)

위 스크린 샷은 기본적인 플레이 화면! CBitmapButton 을 통해 이쁜 플레이어 버튼을 만들었고, 여러가지 편법을 통해 –;; 사이즈가 조절되더라도 저 레이아웃이 그대로 유지되도록 만들었다.

Continue reading YUVplayer for MS Windows

OpenGL: texture vs glDrawPixels

openGL 을 사용해서 2D 이미지 데이타를 화면에 뿌려주는 방법은 대강 다음과 같이 세가지로 분류할 수 있는 것 같다.

  1. glBegin(GL_POINTS); glColor3i(…);glVertex3d(x0,y0,0); …반복; glEnd();
  2. texture 로 올려주고, 해당 texture 가 입혀진 quad 을 그려줌
  3. glDrawPixels 를 이용

첫번째 방법이야 그냥 저렇게도 가능하다는거지 실제 저렇게 사용할 일은 없다고 생각되고, 실제 빠르게 화면에 2D 이미지를 그려주기 위해서는 2번째 방법이나 3번째 방법을 사용해야할텐데, 저 중에 어떤 걸 사용하는게 더 좋은 방법인지 확신이 들질 않는다.

우선 화면이 확대되었을 때 texture 를 사용할 경우 GL_LINEAR 등의 기본으로 제공되는 interpolation method 들이 있기 때문에 (약간 Blur 된 결과일지는 모르지만) 더 좋은 품질의 이미지를 얻을 수 있겠고, 화면이 다시 그려질 일이 있을 때 texture 데이타가 다시 전송될 필요가 없다는 장점이 있는 듯 싶지만, (texture 로 등록할 때 이미지 데이타는 비디오 메모리로 옮겨진다.) width 나 height 가 2^x 형태로 표현되어야 한다는 제약이 있다. 이게 만약 이미지가 크지 않다면 큰 문제가 되지 않겠지만 만약 HD Sequence 라면? 1920×1080 을 표현하기 위해 2048×2048 = 4MB 를 사용해야 하므로 반 정도의 공간이 낭비될 수밖에 없다.

glDrawPixels 는 다시 화면을 그려줘야할 때마다 이미지 데이타를 메인메모리->비디오메모리 로 복사해줘야 하는 문제가 있지만 만약 동영상 플레이어등을 만들 때 처럼 빠르게 화면이 전환되는 경우라면 이는 큰 문제가 되지 않을 듯 싶기는 하다. 물론 화면이 멈춰있는 상태라면 얘기가 다를 지 모르겠다. 또 이미지를 실제 크기보다 더 크게 표현할 경우 glPixelZoom 을 이용해 간단히 구현할 수 있지만 실제로는 픽셀 크기만 커지는 효과이지 interpolation 은 일어나지 않으므로 화질은 texture 를 사용할 때에 비해 떨어진다고 할 수 있을 듯…

뭐 하튼 뭘 사용하는게 더 좋은건지 인터넷을 열심히 찾아봤지만 뭐가 더 좋은지에 대한 정확한 답은 찾을 수가 없다. -_-!

p.s) yuv2rgb 변환 같은 것은 cg 를 이용해서 처리할 수 있는 것 같은데… 이 경우 texture 를 사용해야지만 가능 한 듯…

openGL 에 이미 4×4 matrix multiplication 은 구현되어 있으므로 color_matrix 를 사용해서 yuv2rgb 변환을 빨리할 수 있지 않을까 하는 생각도 해봤지만 실제 결과는 참담…