포스트

GestureClip (2) 손으로 오므리고 펼치는 것까지 이어붙이기

지난 글에서는 맥이랑 아이폰이 서로를 찾아서 텍스트 하나를 주고받을 수 있는지만 확인했다. 이번엔 진짜 하고 싶었던 부분, 웹캠으로 손을 보고 오므리면 보내고 펼치면 받는 것까지 이어붙였다.


맥에서 손 오므림 인식하기

Vision 프레임워크에 손 관절 좌표를 뽑아주는 VNDetectHumanHandPoseRequest가 있다. 웹캠 프레임마다 이걸 돌리면 엄지, 검지 같은 손가락 마디의 화면상 좌표가 나온다.

“오므렸다”를 판정하는 기준으로는 엄지 끝과 검지 끝 사이 거리를 썼다. 근데 이 거리를 그냥 쓰면 손이 카메라에 가까이 있을 때랑 멀리 있을 때 값이 완전히 달라진다. 그래서 손목과 검지 뿌리 사이 거리로 한 번 나눴다. 손이 가까이 오든 멀리 있든 두 거리가 같이 커지고 작아지니까, 이 비율은 거리에 거의 영향을 안 받는다.

1
2
3
4
// HandGestureDetector.swift (맥)
let pinchDistance = hypot(thumbTip.location.x - indexTip.location.x, thumbTip.location.y - indexTip.location.y)
let handScale = hypot(wrist.location.x - indexMCP.location.x, wrist.location.y - indexMCP.location.y)
let ratio = pinchDistance / handScale

비율이 기준(0.5) 아래로 5프레임 연속 유지돼야 오므림으로 확정하고, 확정된 다음엔 비율이 다시 크게(0.8 이상) 벌어져야 재발동하게 했다. 손을 오므린 채로 계속 들고 있어도 반복 전송되지 않게 하려는 거다. 오므림이 확정되면 클립보드의 텍스트를 읽어서 1단계에서 만든 통신 채널로 바로 보낸다.


아이폰 쪽, 그리고 왜 곧장 보여주면 안 되는지

아이폰도 똑같은 방식을 쓰되 전면 카메라로, 방향만 반대로(비율이 크면 펼침) 판정했다. 세로로 들었을 때 전면 카메라 프레임 방향이 후면과 반대라서, Vision에 넘길 때 방향 값도 다르게 줘야 했다.

만들고 나서 테스트해보다가 이상한 걸 발견했다. 맥에서 오므리자마자, 아이폰이 펼치기를 하기도 전에 화면에 텍스트가 떠버렸다. 원인은 간단했다. 데이터가 도착하면 곧바로 화면에 뿌리는 변수에 집어넣고 있었다. 통신 자체는 이미 됐으니 당연한 동작이긴 한데, 원래 하려던 건 “펼쳐야 받는” 거였지 “도착하면 바로 보이는” 게 아니었다.

그래서 도착한 데이터를 일단 안 보이는 곳에 잠깐 담아두고, 펼치기가 확정될 때만 화면에 보이는 값으로 옮기고 클립보드에도 쓰도록 나눴다. 펼치기 전에는 “새 항목 도착, 손을 펼치면 받습니다”라는 문구만 뜨고 내용은 안 보인다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// MPCReceiver.swift (iOS)
func session(_ session: MCSession, didReceive data: Data, fromPeer peerID: MCPeerID) {
    let text = String(data: data, encoding: .utf8) ?? ""
    DispatchQueue.main.async {
        self.pendingText = text
        self.hasPendingData = true
    }
}

func pasteLatestToClipboard() {
    guard let text = pendingText else { return }
    // 생략
    UIPasteboard.general.string = text
    receivedText = text
}

기준값은 추측 대신 화면에서 실측으로

펼침 판정 기준을 처음엔 1.4로 넣었는데 거의 안 잡혔다. 다시 숫자를 추측해서 바꿔봤자 또 틀릴 게 뻔해서, 아예 화면에 매 프레임 계산되는 비율 숫자를 그대로 띄워버렸다. 손을 평소처럼 폈을 때랑 최대한 쫙 폈을 때 숫자가 각각 얼마나 나오는지 직접 보면서 기준을 맞추는 식이다.

실제로 재보니 자연스럽게 편 손도 1.2 정도가 나왔다. 처음 추측했던 1.4는 거의 다다르지 못하는 값이었던 셈이다. 기준을 1.0으로 낮추고 나니 바로 잘 잡혔다.

마지막으로, 펼치기가 잘못 인식돼서 클립보드가 엉뚱하게 덮어써질 수 있으니 되돌리기 버튼도 넣었다. 붙여넣기 직전 클립보드에 있던 값을 따로 기억해뒀다가, 버튼을 누르면 그 값으로 복원한다.

이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.