티스토리 뷰

Processing

[kinect with processing] 3D Button in Point Cloud

생각많은 소심남 2013. 7. 12. 00:15

그동안 kinect에 관한 포스트를 올리지 못했습니다. 아시는 분이나 최근에 블로그에 올라오는 글들을 보신 분들은 아시겠지만 학교에서 전공하는 내용이 키넥트와는 상관이 없습니다. 하지만 제 생각에는 언젠가는 키넥트는 미래 기술의 대세가 될 거라고 생각하고 조그만한 거부터 다시 해보고자 합니다.


이번 포스트에서 다뤄볼 내용은 depthImage와 ColorImage을 mapping한 Point Cloud 상에 3D 버튼을 삽입해보는 예제를 해보고자 합니다. 뭐 Point Cloud를 뽑아내는 코드야 이 블로그에서도 소개했던 적이 있고, SimpleOpenNI 예제로도 있기 때문에 그걸 참고하시면 쉽게 구현하실 수 있을 겁니다.

 자 그럼 일단 Point cloud가 3차원으로 보여지기 때문에 우리가 만들 버튼도 3D면 조금더 효과적으로 나타날 겁니다. Processing 상에서 box를 생성하는 함수는 box() 입니다. 그안에 인자로 가로와 세로, 높이를 지정해주게 되면 원하는 상자를 만들 수 있는 것이지요. 그런데 단순히 box()를 이용해서 만들면 box가 생성되는 위치는 항상 (0, 0, 0)이 됩니다. 그런데 이 문제는 조금 고려해봐야 합니다. Point Cloud는 앞에서 말한 것처럼 키넥트가 바라본 Depth를 3D로 표현해주는 건데, 기본적인 전제가 키넥트가 항상 (0,0,0)에 있다는 겁니다. box()를 이용해서 만들면 바로 키넥트가 존재하는 위치에 box가 생성된다는 소리인데, 아마 눈치가 빠르신 분은 아시겠지만 다음과 같은 문제가 발생합니다.



바로 키넥트가 기본적으로 인식할 수 있는 최소 거리로 인해서 depth가 box가 있는 위치까지 표현되지 않는다는 겁니다. 따라서 box의 위치를 바꿔줘야 하겠지요. 이때 사용하는 함수가 translate라는 함수인데 이 부분은 실제로 예제를 통해서 보여드리도록 하겠습니다.


우선 Point Cloud부터 그려봅시다. 이번에 사용할 라이브러리는 3D를 표현하기 위한 opengl 라이브러리인데 processing에 기본적으로 구현되어있습니다. 그리고 키넥트 관련 라이브러리가 필요하겠지요? 그리고 필요한 기본적인 변수 및 setup()구성을 해줍니다.



그다음 draw를 채워주면 되는데요. 항상 그랬듯이 검정색 바탕에 흰점으로 표현을 해보도록 하겠습니다. 물론 색깔을 바꿔보고 싶으신 분은 background값과 stroke값을 적절히 바꿔보시면 됩니다.


중간에 translate와 rotateX 가 들어있어서 조금 생소할 수 있으나 저 부분없이 실행시 Depth가 sketch의 반대편에 생성되기 때문에 180도 회전해주는 효과를 줍니다. 그러고 각 프레임의 depth point의 position을 저장할 수 있는 PVector 변수를 사용해서 화면상에 for 루프를 통해서 뿌려주게 됩니다. 그러면 결과가 다음과 같이 나오게 됩니다.



지금 손을 뻗은 모습인데 잘 안보이시지요. 위처럼 회전을 해서 일단 화면상에 표현하는데까지는 성공했습니다. 그러면 rotateY를 사용해서도 조절을 해볼 수 있겠지요. 이때는 이렇게 해주면 됩니다.



마우스를 통해서 조절할 수 있게 하자는 거지요. 그런데 radians로 줄수 있는 값과 mouse의 위치로 줄 수 있는 값이 대칭을 이뤄야 하므로 안에 map() 함수를 써서 정확히 값이 -180과 180이 나오게끔 해줍니다. 그러면 이제 마우스의 위치에 따라서 마음대로 Point Cloud가 표현되는 위치를 조절할 수 있겠됩니다.

 참 한가지 더, 지금 depth가 잘 안보이는데 조금더 촘촘히 하고 싶다 하시는 분은 이 부분을 수정하시면 됩니다.


for 루프내에서 얼마나 자주 sampling을 해줄지를 결정해주는 값입니다. 당연히 이 값이 작을수록 더 촘촘히 값을 인식하겠지요. 각 값에 대한 차이는  다음과 같이 표현됩니다.


그런데 막연하게 조금더 정밀하게 표현하고 싶어서 값을 막 내리면 안되는게, 그만큼 키넥트로부터 데이터를 많이 가져오기 때문에 한 프레임을 뽑아내는데 시간이 많이 걸리게 됩니다. 즉 framerate가 낮아지는 거지요. 제 경험상 약 5정도가 적절한 듯 합니다. 


여기까지하면 일단 Point Cloud를 뽑아내게 됩니다. 이제 상자를 표현해야 되겠지요.

우선 global 변수로 box의 size와 Center를 표현해주는게 좋겠습니다.



그리고 이 box를 그려주려면 실질적으로 draw()안에 구현되어야 하겠지요. 다음과 같이 작성합니다.


그런데 생각해보니까 boxPosition 값을 너무 적게 준거 같네요. z 값을 800으로 바꿔주고 Test해보면 다음과 같이 이쁜 box가 나오게 됩니다.


손으로 건드리고 있는 모습도 보이지요. 그런데 지금은 접촉했을 때 어떤 인터렉션을 주도록 설정하지 않았기 때문에 건드려도 아무 변화가 없습니다. 그러면 이제 접촉했을 때 뭔가를 해주면 좋겠지요. 

 사실 이런 역할을 해주는 것을 바로 이벤트 핸들러라고 합니다. 그래서 접촉했을때를 조건으로 이벤트가 발생할지의 여부가 결정되게끔 구현된 것이 이벤트 핸들러인데 사실 지금 box를 3d 평면상에 가상으로 만든것이기 때문에 box에 대한 이벤트 핸들러를 사용자가 직접 만들어줘야 합니다. 그런데 가장 간단한 방법이 바로 앞에서 지정한 size와 Position을 이용하는 겁니다. 아마 다음과 같이 생각하면 좋을 듯 하네요.


앞에서 언급한 것처럼 Position과 size는 정해줬습니다. 그러면 depthPoint가 하나라도 이 안에 들어가 있으면 당연히 이벤트가 활성화되야 할 겁니다. 그걸 구현해주면 됩니다. 그에 대한 결과는 box의 투명도로 해주면 좋겠네요. 일단 다음과 같이 들어가 있는지를 확인해주는 조건문을 작성합니다.


*오타가 났네요. z값 비교하는데서도 currentPoint입니다.

당연한 이야기이겠지만 우리는 depthPoint가 하나라도 box안에 들어가있는지를 확인해야 되기 때문에 앞에서 Point Cloud를 만들때 작성한 for 루프 안에 조건문을 삽입해줍니다. 그리고 중심에 가까울 수록 값이 커지게 하는 어떤 변수를 집어넣어주면 좋겠지요. 


자 그런데 사실 이 값이 우리가 Alpha라고 알고 있는 색의 기준과 일치하는지를 장담할 수 없습니다. 그렇기 때문에 여기서도 map 함수를 써서 저렇게 구한 값이 0~255 사이의 값을 가지게끔 합니다. 그 후에 안을 채워주는 fill() 함수의 네번째 변수로 넣어주게 되면 투명도를 조절할 수 있게 됩니다.


결과는 다음과 같습니다.



이렇게 간단하게 3D Box를 활용해서 Button을 만들고 누르면 반응이 일어나는 예제를 해보았습니다. 이렇게 하면 응용할 수 있는 부분이 몇군데 있지요. 가령 댄스 프로그램을 만들어서 box를 건드리면 점수가 올라가게 한다던가 노래가 나오게 할 수도 있습니다. Making Things See에 언급된 바로는 Processing에 첨부되어 있는 minim 라이브러리를 사용하면 음원을 재생할 수 있다는데 아마 다음 포스트에서는 그것과 관련된 내용을 다루지 않을까 싶네요.


sketch_3DBoxInPC.zip


댓글