๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ

Computer Vision

[Unity] MediaPipe ์—ฐ๋™ํ•ด์„œ ์† ์ƒํ˜ธ์ž‘์šฉ ๊ตฌํ˜„ํ•˜๊ธฐ

728x90
๋ฐ˜์‘ํ˜•

์ด์ „ ๊ธ€์—์„œ CVZone ์™€ MediaPipe ํŒจํ‚ค์ง€๋“ค์„ ์„ค์น˜ํ•˜๊ณ , ์›น์บ ์„ ๋ถˆ๋Ÿฌ์™€ ์†์„ ์ถ”์ ํ•˜๋Š” ๊ฒƒ๊นŒ์ง€ ๋‹ค๋ค„๋ดค์Šต๋‹ˆ๋‹ค.

https://vrworld.tistory.com/12

 

MediaPipe๋ฅผ ์ด์šฉํ•˜์—ฌ Hand Tracking & ์† ๋™์ž‘ ์ธ์‹ ์‰ฝ๊ฒŒ ํ…Œ์ŠคํŠธ ํ•˜๊ธฐ

๊ตฌํ˜„ ๋™๊ธฐ ๋ชจ๋ฐ”์ผ ํ™˜๊ฒฝ์—์„œ์˜ AR ํ”„๋กœ์ ํŠธ๋ฅผ ์ง„ํ–‰ํ•˜๊ณ , ๋‹ค๋ฅธ AR ํ”„๋กœ์ ํŠธ๋ฅผ ๋งŽ์ด ๋ณด๋ฉด์„œ ๋Š๋‚€ ๊ฒŒ ํ•œ ๊ฐ€์ง€ ์žˆ๋‹ค๋ฉด ๋Œ€๊ฐœ ์‚ฌ์šฉ์žํ•œํ…Œ ์ž…๋ ฅ์„ ๋ฐ›์„ ๋•Œ ์Šค๋งˆํŠธํฐ ํ„ฐ์น˜๋กœ ๋ฐ›๋Š” ๊ฒฝ์šฐ๊ฐ€ ๋งŽ์•˜๋‹ค. ์ด๋Ÿฐ ์ ์—

vrworld.tistory.com

์ด๋ฒˆ ๊ธ€์€ ์š”์•ฝํ•˜๋ฉด ์•„๋ž˜ 5๊ฐ€์ง€๋กœ ์ •๋ฆฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

1. ์›น์บ ์œผ๋กœ ๊ฐ์ง€ํ•œ ์†์˜ 21๊ฐœ ๋žœ๋“œ๋งˆํฌ ๋ฐ์ดํ„ฐ๋ฅผ ๋ถˆ๋Ÿฌ์˜ต๋‹ˆ๋‹ค.
2. ๋žœ๋“œ๋งˆํฌ ๋ฐ์ดํ„ฐ ๋ฆฌ์ŠคํŠธ๋ฅผ ๋งŒ๋“ค์–ด ํ•ด๋‹น ๊ฐ’์„ UDP ์†Œ์ผ“์„ ํ†ตํ•ด Unity๋กœ ์ „์†กํ•ฉ๋‹ˆ๋‹ค.
3. ์ „์†ก๋ฐ›์€ ๋ฐ์ดํ„ฐ๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ Unity ๋‚ด์—์„œ๋„ "์†"์„ ๋งŒ๋“ญ๋‹ˆ๋‹ค.
4. ์‹ค์‹œ๊ฐ„์œผ๋กœ ์›น์บ ์—์„œ ์ถ”์ ํ•œ ์†์ด Unity ๋‚ด์—์„œ ๋งŒ๋“  "์†"์ด ์—ฐ๋™๋ฉ๋‹ˆ๋‹ค.
5. Unity ๋‚ด์—์„œ ์—ฐ๋™๋œ "์†" ์œผ๋กœ ์˜ค๋ธŒ์ ํŠธ ๋“ค๊ณผ ์ƒํ˜ธ์ž‘์šฉ(Interaction)ํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

 

 

 

 21๊ฐœ์˜ ๋žœ๋“œ๋งˆํฌ ๋ฐ์ดํ„ฐ

์šฐ๋ฆฌ๊ฐ€ ์ด์ „์— PyCharm ํ™˜๊ฒฝ์—์„œ MediaPipe ํŒจํ‚ค์ง€๋ฅผ ๋‹ค์šด๋ฐ›์•˜์„ ๊ฑด๋ฐ,  ์ด ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ํ†ตํ•ด ์ถ”์ ํ•œ ์†์˜ ๋žœ๋“œ๋งˆํฌ๋ฅผ ๊ฐ์ง€ํ•˜๊ฒŒ ๋˜๋Š”๋ฐ ๊ฐ ์†๋งˆ๋‹ค 21๊ฐœ์˜ ๋žœ๋“œ๋งˆํฌ ๋ฐ์ดํ„ฐ๋ฅผ ํ”„๋ ˆ์ž„๋งˆ๋‹ค ์ถ”๋ก ํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

 

while True:
# ์›น์ผ์—์„œ ํ”„๋ ˆ์ž„ ๊ฐ€์ ธ์˜ค๊ธฐ
success, img = cap.read()

# Hands
hands, img = detector.findHands(img)

data = []

# 21๊ฐœ์˜ ๋žœ๋“œ๋งˆํฌ ๊ฐ’๋“ค์„ UDP ํ”„๋กœํ† ์ฝœ์„ ์‚ฌ์šฉํ•˜์—ฌ Unity์— ๋ณด๋ƒ„.
# Landmark values - (x,y,z) * 21
if hands:
# Get the first hand detected
hand = hands[0]
# Get the landmark list
lmList = hand['lmList']
print(lmList)
for lm in lmList:
data.extend([lm[0], height - lm[1], lm[2]])
print(data)
sock.sendto(str.encode(str(data)), serverAddressPort)

์ฝ”๋“œ์˜ ํ๋ฆ„์€ while ๋ฐ˜๋ณต๋ฌธ์œผ๋กœ ์›น์บ ์„ ํ”„๋ ˆ์ž„๋งˆ๋‹ค๊ฐ€ ๊ฐ€์ ธ์˜ค๊ณ , fineHands() ๋ฉ”์†Œ๋“œ๋ฅผ ํ†ตํ•ด ์›น์บ ์—์„œ ์†์„ ๊ฐ์ง€ํ•ฉ๋‹ˆ๋‹ค.

if ๋ฌธ์„ ํ†ตํ•ด ๋งŒ์•ฝ ์†์„ ๊ฐ์ง€ํ–ˆ๋‹ค๋ฉด, ์†์˜ ๋žœ๋“œ๋งˆํฌ ๋ฆฌ์ŠคํŠธ๋ฅผ ์ถœ๋ ฅํ•ฉ๋‹ˆ๋‹ค. (lmList)

๋˜ํ•œ, ์šฐ๋ฆฌ๊ฐ€ ๋ฐ›์•„์˜จ ๋žœ๋“œ๋งˆํฌ ๋ฆฌ์ŠคํŠธ ๋งŒํผ ๋ฐ˜๋ณตํ•˜์—ฌ Unity์— ๋ณด๋‚ผ ๋ฐ์ดํ„ฐ๋ฅผ "data" ๋ณ€์ˆ˜์— ์ฐจ๊ณก์ฐจ๊ณก ๋„ฃ์–ด์ฃผ๊ฒŒ ๋˜๋Š” ๊ฑฐ์ฃ .

 

์—ฌ๊ธฐ์„œ data.extend() ๋ถ€๋ถ„์— lm[0], lm[1], lm[2] ๋Š” ๊ฐ๊ฐ x, y, z ๊ฐ’์„ ๊ฐ€๋ฆฌํ‚ต๋‹ˆ๋‹ค. 

height - lm[1]๋Š”  OpenCV ์™€ Unity ๊ฐ€ Y ๋ฐฉํ–ฅ์ด ์„œ๋กœ ๋ฐ˜๋Œ€์ด๊ธฐ์— Height(์„ธ๋กœ ํ•ด์ƒ๋„)์—์„œ Y๊ฐ’์„ ๋นผ์คŒ์œผ๋กœ์จ ์˜ฌ๋ฐ”๋ฅธ ๋ฐ์ดํ„ฐ๋ฅผ Unity์— ๋ณด๋‚ด์ฃผ๊ธฐ ์œ„ํ•จ๋„ Key Point๊ฐ€ ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

 

 

์‹คํ–‰ ํ™”๋ฉด

์‹คํ–‰ ํ™”๋ฉด์ž…๋‹ˆ๋‹ค. ์‹ค์‹œ๊ฐ„์œผ๋กœ ๋žœ๋“œ๋งˆํฌ ๋ฐ์ดํ„ฐ๋ฅผ ๋ถˆ๋Ÿฌ์˜ค๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ํ™”์‚ดํ‘œ ๋ชจ์–‘์„ ์ž์„ธํžˆ ๋ณด์‹œ๋ฉด lmList ๋ณ€์ˆ˜์™€ Data ๋ณ€์ˆ˜ ์ˆœ์„œ๋กœ ์œ„ ์•„๋ž˜ ์ถœ๋ ฅ๋˜๊ณ  ์žˆ๋Š”๋ฐ, Data ๋ณ€์ˆ˜์— Helight๋งŒํผ Y ๊ฐ’์ด ๊ฐ์†Œํ•˜์—ฌ ์žˆ๊ณ ,  Unity์— ๋ณด๋‚ด๊ธฐ ์œ„ํ•ด ๊ฐ๊ฐ์˜ ๋Œ€๊ด„ํ˜ธ๋„ ์‚ฌ๋ผ์ง„ ๊ฑธ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

import cv2
from cvzone.HandTrackingModule import HandDetector
import socket

# Parameters
width, height = 1280, 720

# IP WebCam
cap = cv2.VideoCapture("http://Your IP Address/video")

# ์ผ๋ฐ˜ WebCam ์„ ์‚ฌ์šฉํ•  ๊ฒฝ์šฐ
#cap = cv2.VideoCapture(0)

cap.set(3, width)
cap.set(4, height)

# ์†์„ ๊ฐ์ง€
detector = HandDetector(maxHands=1, detectionCon=0.8)

# ๋„คํŠธ์›Œํฌ
sock = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
serverAddressPort = ("127.0.0.1", 5052)


while True:
# ์›น์ผ์—์„œ ํ”„๋ ˆ์ž„ ๊ฐ€์ ธ์˜ค๊ธฐ
success, img = cap.read()

# Hands
hands, img = detector.findHands(img)

data = []

# 21๊ฐœ์˜ ๋žœ๋“œ๋งˆํฌ ๊ฐ’๋“ค์„ UDP ํ”„๋กœํ† ์ฝœ์„ ์‚ฌ์šฉํ•˜์—ฌ Unity์— ๋ณด๋ƒ„.
# Landmark values - (x,y,z) * 21
if hands:
# Get the first hand detected
hand = hands[0]
# Get the landmark list
lmList = hand['lmList']
print(lmList)
for lm in lmList:
data.extend([lm[0], height - lm[1], lm[2]])
print(data)
sock.sendto(str.encode(str(data)), serverAddressPort)


img = cv2.resize(img, (0,0), None, 0.5, 0.5)
cv2.imshow("Image", img)

if cv2.waitKey(1) == ord("q"): # q ๋ˆ„๋ฅผ ์‹œ ์›น์ผ ์ข…๋ฃŒ
break

 

์œ„ ์ „์ฒด ์ฝ”๋“œ์— ๋Œ€ํ•œ ์„ค๋ช…์ž…๋‹ˆ๋‹ค. ์ฐธ๊ณ ํ•˜์‹œ๊ธฐ ๋ฐ”๋ž๋‹ˆ๋‹ค. 
1. ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์ž„ํฌํŠธ:
- `import cv2`: OpenCV ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์ž„ํฌํŠธํ•ฉ๋‹ˆ๋‹ค. OpenCV๋Š” ์˜์ƒ ์ฒ˜๋ฆฌ์™€ ์ปดํ“จํ„ฐ ๋น„์ „ ์ž‘์—…์„ ์œ„ํ•œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ž…๋‹ˆ๋‹ค.
- `from cvzone.HandTrackingModule import HandDetector`: cvzone ํŒจํ‚ค์ง€์—์„œ HandDetector ๋ชจ๋“ˆ์„ ์ž„ํฌํŠธํ•ฉ๋‹ˆ๋‹ค. ์ด ๋ชจ๋“ˆ์€ ์†์„ ๊ฐ์ง€ํ•˜๊ณ  ์ถ”์ ํ•˜๋Š” ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.
- `import socket`: ์†Œ์ผ“ ํ†ต์‹ ์„ ์œ„ํ•œ socket ๋ชจ๋“ˆ์„ ์ž„ํฌํŠธํ•ฉ๋‹ˆ๋‹ค.

2. ๋งค๊ฐœ๋ณ€์ˆ˜ ์„ค์ •:
- `width, height = 1280, 720`: ์˜์ƒ์˜ ๊ฐ€๋กœ์™€ ์„ธ๋กœ ํ•ด์ƒ๋„๋ฅผ ์ง€์ •ํ•ฉ๋‹ˆ๋‹ค. ์ด ๊ฐ’์€ ์›น์บ ์˜ ํ•ด์ƒ๋„ ์„ค์ •์— ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค.

3. IP ์›น์บ  ์„ค์ •:
- `cap = cv2.VideoCapture("http://Your IP Address//video")`: IP ์›น์บ ์œผ๋กœ๋ถ€ํ„ฐ ์˜์ƒ์„ ๋ฐ›์•„์˜ค๊ธฐ ์œ„ํ•ด `cv2.VideoCapture` ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค. `"http://Your IP Address//video"`์€ ์›น์บ ์˜ ์ฃผ์†Œ๋ฅผ ๋‚˜ํƒ€๋‚ด๋ฉฐ, ํ•ด๋‹น ์ฃผ์†Œ์—์„œ ์˜์ƒ์„ ๋ฐ›์•„์˜ต๋‹ˆ๋‹ค.

4. ์›น์บ  ์„ค์ •:
- `cap.set(3, width)`: ์›น์บ ์˜ ๊ฐ€๋กœ ํ•ด์ƒ๋„๋ฅผ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.
- `cap.set(4, height)`: ์›น์บ ์˜ ์„ธ๋กœ ํ•ด์ƒ๋„๋ฅผ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.

5. ์† ๊ฐ์ง€ ๊ฐ์ฒด ์ƒ์„ฑ:
- `detector = HandDetector(maxHands=1, detectionCon=0.8)`: `HandDetector` ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค. `maxHands`๋Š” ๊ฐ์ง€ํ•  ์†์˜ ์ตœ๋Œ€ ์ˆ˜๋ฅผ ์ง€์ •ํ•˜๋ฉฐ, `detectionCon`์€ ์†์„ ๊ฐ์ง€ํ•˜๊ธฐ ์œ„ํ•œ ์‹ ๋ขฐ๋„ ์ž„๊ณ„๊ฐ’์„ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.

6. ๋„คํŠธ์›Œํฌ ์„ค์ •:
- `sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)`: UDP ์†Œ์ผ“์„ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค. `socket.AF_INET`์€ IPv4๋ฅผ ์‚ฌ์šฉํ•˜๊ณ , `socket.SOCK_DGRAM`์€ ๋ฐ์ดํ„ฐ๊ทธ๋žจ ์†Œ์ผ“์„ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.
- `serverAddressPort = ("127.0.0.1", 5052)`: UDP ์„œ๋ฒ„์˜ ์ฃผ์†Œ์™€ ํฌํŠธ๋ฅผ ์ง€์ •ํ•ฉ๋‹ˆ๋‹ค.

7. ์˜์ƒ ์ฒ˜๋ฆฌ ๋ฃจํ”„:
- `while True:`: ๋ฌดํ•œ ๋ฃจํ”„๋ฅผ ์‹œ์ž‘ํ•ฉ๋‹ˆ๋‹ค.
- `success, img = cap.read()`: ์›น์บ ์—์„œ ํ”„๋ ˆ์ž„์„ ์ฝ์–ด์˜ต๋‹ˆ๋‹ค. `success`๋Š” ํ”„๋ ˆ์ž„ ์ฝ๊ธฐ๊ฐ€ ์„ฑ๊ณตํ–ˆ๋Š”์ง€ ๋ฅผ ๋‚˜ํƒ€๋‚ด๋Š” ๋ถˆ๋ฆฌ์–ธ ๊ฐ’์ด๊ณ , `img`๋Š” ์ฝ์–ด์˜จ ํ”„๋ ˆ์ž„ ์ด๋ฏธ์ง€์ž…๋‹ˆ๋‹ค.

8. ์† ๊ฐ์ง€:
- `hands, img = detector.findHands(img)`: `HandDetector` ๊ฐ์ฒด์˜ `findHands()` ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์†์„ ๊ฐ์ง€ํ•ฉ๋‹ˆ๋‹ค. `img`์—๋Š” ์† ๊ฐ์ง€ ๊ฒฐ๊ณผ๊ฐ€ ๊ทธ๋ ค์ง„ ์ด๋ฏธ์ง€๊ฐ€ ๋ฐ˜ํ™˜๋˜๊ณ , `hands`์—๋Š” ๊ฐ์ง€๋œ ์†์— ๋Œ€ํ•œ ์ •๋ณด๊ฐ€ ๋‹ด๊ธด ๋ฆฌ์ŠคํŠธ๊ฐ€ ๋ฐ˜ํ™˜๋ฉ๋‹ˆ๋‹ค.

9. ์†์˜ ๋žœ๋“œ๋งˆํฌ ๊ฐ’ ์ „์†ก:
- `if hands:`: ์†์ด ๊ฐ์ง€๋˜์—ˆ๋‹ค๋ฉด ์•„๋ž˜ ์ฝ”๋“œ๋ฅผ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค.
- `hand = hands[0]`: ์ฒซ ๋ฒˆ์งธ๋กœ ๊ฐ์ง€๋œ ์†์„ ์„ ํƒํ•ฉ๋‹ˆ๋‹ค. - `lmList = hand['lmList']`: ์„ ํƒ๋œ ์†์˜ ๋žœ๋“œ๋งˆํฌ ๋ฆฌ์ŠคํŠธ๋ฅผ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค.
- `for lm in lmList:`: ๊ฐ ๋žœ๋“œ๋งˆํฌ์— ๋Œ€ํ•ด ๋ฐ˜๋ณตํ•ฉ๋‹ˆ๋‹ค.
- `data.extend([lm[0], height - lm[1], lm[2]])`: `data` ๋ฆฌ์ŠคํŠธ์— ๋žœ๋“œ๋งˆํฌ์˜ x, y, z ์ขŒํ‘œ ๊ฐ’์„ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค. y ์ขŒํ‘œ๋Š” ํ™”๋ฉด ์ƒ์—์„œ ๋’ค์ง‘ํ˜€ ์žˆ์œผ๋ฏ€๋กœ `height - lm[1]`๋กœ ๋ณ€ํ™˜ํ•ฉ๋‹ˆ๋‹ค.
- `sock.sendto(str.encode(str(data)), serverAddressPort)`: `data` ๊ฐ’์„ ๋ฌธ์ž์—ด๋กœ ๋ณ€ํ™˜ํ•˜๊ณ , ํ•ด๋‹น ๊ฐ’์„ ๋ฐ”์ดํŠธ๋กœ ์ธ์ฝ”๋”ฉํ•˜์—ฌ UDP ์†Œ์ผ“์„ ํ†ตํ•ด Unity๋กœ ์ „์†กํ•ฉ๋‹ˆ๋‹ค.

10. ์˜์ƒ ์ถœ๋ ฅ:
- `img = cv2.resize(img, (0,0), None, 0.4, 0.4)`: ์ถœ๋ ฅํ•  ์˜์ƒ์˜ ํฌ๊ธฐ๋ฅผ ์กฐ์ •ํ•ฉ๋‹ˆ๋‹ค.
- `cv2.imshow("Image", img)`: `img`์— ์ €์žฅ๋œ ์ด๋ฏธ์ง€๋ฅผ "Image"๋ผ๋Š” ์ฐฝ์— ์ถœ๋ ฅํ•ฉ๋‹ˆ๋‹ค.

11. ์ข…๋ฃŒ ์กฐ๊ฑด:
- `if cv2.waitKey(1) == ord("q"):`: ์‚ฌ์šฉ์ž๊ฐ€ "q" ํ‚ค๋ฅผ ๋ˆ„๋ฅด๋ฉด ๋ฃจํ”„๋ฅผ ์ข…๋ฃŒํ•ฉ๋‹ˆ๋‹ค.
- `cv2.waitKey(1)`: ์‚ฌ์šฉ์ž์˜ ํ‚ค ์ž…๋ ฅ์„ 1๋ฐ€๋ฆฌ์ดˆ ๋™์•ˆ ๋Œ€๊ธฐํ•˜๊ณ , ์ž…๋ ฅ์ด ์žˆ์œผ๋ฉด ํ•ด๋‹น ํ‚ค์˜ ์œ ๋‹ˆ์ฝ”๋“œ ๊ฐ’์„ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.
- `ord("q")`: ๋ฌธ์ž "q"์˜ ์œ ๋‹ˆ์ฝ”๋“œ ๊ฐ’์„ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.
- `break`: ๋ฃจํ”„๋ฅผ ์ข…๋ฃŒํ•ฉ๋‹ˆ๋‹ค. ์ด ์ฝ”๋“œ๋Š” IP ์›น์บ ์œผ๋กœ๋ถ€ํ„ฐ ์‹ค์‹œ๊ฐ„์œผ๋กœ ์˜์ƒ์„ ๋ฐ›์•„์™€ ์†์„ ๊ฐ์ง€ํ•˜๊ณ , ๊ฐ์ง€๋œ ์†์˜ ๋žœ๋“œ๋งˆํฌ ๊ฐ’์„ Unity๋กœ ์ „์†กํ•˜๋Š” ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค.

 

 

 

 

 

UDP ์†Œ์ผ“์„ ์ด์šฉํ•˜์—ฌ ๋žœ๋“œ๋งˆํฌ ๋ฐ์ดํ„ฐ๋ฅผ Unity์— ์ „์†ก

 

https://www.youtube.com/watch?v=RQ-2JWzNc6k 

์ด์ œ Unity ๋‚ด์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ์ „์†ก๋ฐ›์„ UDP ๊ด€๋ จ ์Šคํฌ๋ฆฝํŠธ๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.

 

UDP ๊ด€๋ จ ์Šคํฌ๋ฆฝํŠธ๋Š” ์ง€๊ธˆ๊นŒ์ง€ ์ฐธ๊ณ ํ–ˆ๋˜ ํŠœํ† ๋ฆฌ์–ผ ์˜์ƒ๊ฒŒ์‹œ์ž์˜ ์‚ฌ์ดํŠธ๋ฅผ ๋“ค์–ด๊ฐ€ ๊ฐ„๋‹จํ•œ ํšŒ์›๊ฐ€์ž… ํ›„ ๋ฌด๋ฃŒ๋กœ ๋‹ค์šด๋ฐ›์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

 

 

UDP ๊ด€๋ จ ์Šคํฌ๋ฆฝํŠธ๋ฅผ ๋‹ค์šด๋ฐ›๊ณ , Unity ์—๋””ํ„ฐ๋กœ ์˜ฎ๊ฒจ์ฃผ์‹  ๋‹ค์Œ, ๋ฐ์ดํ„ฐ ์ •๋ณด๋ฅผ ๋ฐ›์„ ๋นˆ ์˜ค๋ธŒ์ ํŠธ๋ฅผ ๋งŒ๋“ค์–ด ์Šคํฌ๋ฆฝํŠธ๋ฅผ ์ฒจ๋ถ€ํ•ด ์ฃผ์„ธ์š”.

 

์ง€๊ธˆ๊นŒ์ง€ ๊ณผ์ •์„ ๋งˆ์นœ ํ›„, ํ”„๋กœ์ ํŠธ๋ฅผ ์‹คํ–‰ํ•˜๋ฉด ์•„๊นŒ PyCharm ์ฝ˜์†” ์ฐฝ์—์„œ ํ™•์ธํ–ˆ๋˜ ๋žœ๋“œ๋งˆํฌ ๋ฐ์ดํ„ฐ๋“ค์ด Unity ๋‚ด์—์„œ๋„ ์ •์ƒ์ ์œผ๋กœ ์˜ฎ๊ฒจ์ง„ ๊ฑธ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

 

 

 

Unity์—์„œ ์ „์†ก ๋ฐ›์€ ๋ฐ์ดํ„ฐ๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ์‹ค์‹œ๊ฐ„ ์ƒํ˜ธ์ž‘์šฉ ๊ตฌํ˜„

์ด์ œ ์ด ์ „์†ก๋ฐ›์€ ๋ฐ์ดํ„ฐ๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ Unity ๋‚ด์—์„œ ์† ๋ชจ๋ธ์„ ๋งŒ๋“ค์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์† ๋ชจ๋ธ ๋งŒ๋“œ๋Š” ๊ณผ์ •์„ ํ•˜๋‚˜ํ•˜๋‚˜ ์ ์–ด ๋‚˜๊ฐ€๊ธฐ์—๋Š” ๊ธ€์˜ ์–‘์ด ๋งŽ์•„์ ธ ์ฝ”๋“œ ์„ค๋ช…๊ณผ ํ•ต์‹ฌ์ ์ธ ๋ถ€๋ถ„๋งŒ ์งš๊ณ , ๋‚˜๋จธ์ง€๋Š” ์œ„ ํŠœํ† ๋ฆฌ์–ผ ๋งํฌ๋ฅผ ๋”ฐ๋ผ๊ฐ€์…”์„œ ์ฐจ๊ทผ์ฐจ๊ทผ ๋”ฐ๋ผ ํ•ด ๋ณด์‹œ๋ฉด ๋  ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค. 

 

HandTracking.cs ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜์—ฌ Unity์—์„œ ์šฐ๋ฆฌ๊ฐ€ ๋ฐ›์•„์˜จ ์†์˜ ๋žœ๋“œ๋งˆํฌ ๋ฐ์ดํ„ฐ๋ฅผ ์ด์šฉํ•˜์—ฌ ์† ๋ชจ๋ธ์„ ํ‘œํ˜„ํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

21๊ฐ€์ง€์— ๋Œ€ํ•œ ๋žœ๋“œ๋งˆํฌ Point๋“ค๊ณผ ๋ฐ›์•„์˜ฌ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์˜ค๊ธฐ ์œ„ํ•œ UDP ์†Œ์ผ“์„ HandTracking  ์ปดํฌ๋„ŒํŠธ์— ์—ฐ๊ฒฐํ•ด ์ค๋‹ˆ๋‹ค.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
 
public class HandTracking : MonoBehaviour
{
    // Start is called before the first frame update
 
    public UDPReceive udpReceive;
    public GameObject[] handPoints;
 
    void Start()
    {
        
    }
 
    // Update is called once per frame
    void Update()
    {
        // UDP ํ”„๋กœํ† ์ฝœ๋กœ ์ „์†ก๋œ ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ›์•„์˜จ๋‹ค.
        string data = udpReceive.data;
 
        // ๊ฐ€์ง€๊ณ  ์˜จ ๋ฐ์ดํ„ฐ์—์„œ ๋Œ€๊ด„ํ˜ธ([ ])๋ฅผ ๋บ€๋‹ค.
        data = data.Remove(01);
        data = data.Remove(data.Length-11);
 
        // ์‰ผํ‘œ๋ฅผ ๊ธฐ์ค€์œผ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ๋ถ„ํ™œ
        string[] points = data.Split(',');
 
        for ( int i = 0; i < 21; i++)
        {
            float x = 5 - float.Parse(points[i * 3])/100;
            float y = float.Parse(points[i * 3 + 1]) / 100;
            float z = float.Parse(points[i * 3 + 2]) / 100;
 
            handPoints[i].transform.localPosition = new Vector3(x, y, z);
 
        }
 
    }
}
c

 

  1. ๋ณ€์ˆ˜ ์„ ์–ธ:
    • public UDPReceive udpReceive;: UDPReceive ์Šคํฌ๋ฆฝํŠธ์— ์ ‘๊ทผํ•˜๊ธฐ ์œ„ํ•œ ๋ณ€์ˆ˜๋ฅผ ์„ ์–ธํ•ฉ๋‹ˆ๋‹ค. ์ด ์Šคํฌ๋ฆฝํŠธ๋Š” UDP ํ”„๋กœํ† ์ฝœ์„ ํ†ตํ•ด ๋ฐ์ดํ„ฐ๋ฅผ ์ˆ˜์‹ ํ•˜๋Š” ๊ธฐ๋Šฅ์„ ๋‹ด๋‹นํ•ฉ๋‹ˆ๋‹ค.
    • public GameObject[] handPoints;: ์† ๋ชจ๋ธ์˜ ๋žœ๋“œ๋งˆํฌ ์œ„์น˜๋ฅผ ๋‚˜ํƒ€๋‚ด๋Š” ๊ฒŒ์ž„ ์˜ค๋ธŒ์ ํŠธ ๋ฐฐ์—ด์„ ์„ ์–ธํ•ฉ๋‹ˆ๋‹ค. ์ด ๋ฐฐ์—ด์€ ์† ๋ชจ๋ธ์˜ ๊ฐ ๋žœ๋“œ๋งˆํฌ์— ๋Œ€ํ•œ ์œ„์น˜๋ฅผ ์„ค์ •ํ•˜๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค.
  2. UDP ๋ฐ์ดํ„ฐ ์ˆ˜์‹ :
    • string data = udpReceive.data;: UDPReceive ์Šคํฌ๋ฆฝํŠธ์—์„œ ๋ฐ›์•„์˜จ UDP๋กœ ์ „์†ก๋œ ๋ฐ์ดํ„ฐ๋ฅผ ๋ฌธ์ž์—ด ํ˜•ํƒœ๋กœ data ๋ณ€์ˆ˜์— ์ €์žฅํ•ฉ๋‹ˆ๋‹ค.
  3. ๋ฐ์ดํ„ฐ ๊ฐ€๊ณต:
    • data = data.Remove(0, 1);: data ๋ฌธ์ž์—ด์—์„œ ์ฒซ ๋ฒˆ์งธ ๋ฌธ์ž์ธ ๋Œ€๊ด„ํ˜ธ [๋ฅผ ์ œ๊ฑฐํ•ฉ๋‹ˆ๋‹ค.
    • data = data.Remove(data.Length-1, 1);: data ๋ฌธ์ž์—ด์—์„œ ๋งˆ์ง€๋ง‰ ๋ฌธ์ž์ธ ๋Œ€๊ด„ํ˜ธ ]๋ฅผ ์ œ๊ฑฐํ•ฉ๋‹ˆ๋‹ค.
    • string[] points = data.Split(',');: data ๋ฌธ์ž์—ด์„ ์‰ผํ‘œ ,๋ฅผ ๊ธฐ์ค€์œผ๋กœ ๋ถ„ํ• ํ•˜์—ฌ ๋ฌธ์ž์—ด ๋ฐฐ์—ด์ธ points์— ์ €์žฅํ•ฉ๋‹ˆ๋‹ค.
  4. ๋žœ๋“œ๋งˆํฌ ์œ„์น˜ ์—…๋ฐ์ดํŠธ:
    • for (int i = 0; i < 21; i++): 21๊ฐœ์˜ ๋žœ๋“œ๋งˆํฌ์— ๋Œ€ํ•ด ๋ฐ˜๋ณตํ•ฉ๋‹ˆ๋‹ค.
    • float x = 5 - float.Parse(points[i * 3])/100;: x ์ขŒํ‘œ ๊ฐ’์„ ๊ฐ€์ ธ์™€์„œ float ํ˜•์œผ๋กœ ๋ณ€ํ™˜ํ•˜๊ณ , ๋ฒ”์œ„๋ฅผ ์กฐ์ •ํ•ฉ๋‹ˆ๋‹ค.
    • float y = float.Parse(points[i * 3 + 1]) / 100;: y ์ขŒํ‘œ ๊ฐ’์„ ๊ฐ€์ ธ์™€์„œ float ํ˜•์œผ๋กœ ๋ณ€ํ™˜ํ•˜๊ณ , ๋ฒ”์œ„๋ฅผ ์กฐ์ •ํ•ฉ๋‹ˆ๋‹ค.
    • float z = float.Parse(points[i * 3 + 2]) / 100;: z ์ขŒํ‘œ ๊ฐ’์„ ๊ฐ€์ ธ์™€์„œ float ํ˜•์œผ๋กœ ๋ณ€ํ™˜ํ•˜๊ณ , ๋ฒ”์œ„๋ฅผ ์กฐ์ •ํ•ฉ๋‹ˆ๋‹ค.
    • handPoints[i].transform.localPosition = new Vector3(x, y, z);: handPoints ๋ฐฐ์—ด์—์„œ i๋ฒˆ์งธ ์ธ๋ฑ์Šค์— ํ•ด๋‹นํ•˜๋Š” ์†์˜ ๋žœ๋“œ๋งˆํฌ ์œ„์น˜๋ฅผ ์—…๋ฐ์ดํŠธํ•ฉ๋‹ˆ๋‹ค. localPosition ์†์„ฑ์„ ์‚ฌ์šฉํ•˜์—ฌ ์œ„์น˜๋ฅผ ์ง€์ •ํ•ฉ๋‹ˆ๋‹ค.

 

 

์ด์ œ ๋žœ๋“œ๋งˆํฌ 21๊ฐ€์ง€ Point๋“ค์„ ๋งŒ๋“ค์—ˆ๋‹ค๋ฉด ๊ฐ๊ฐ์˜ Point๋“ค์„ ์‹œ์ž‘์ (Origin)๊ณผ ๋„์ฐฉ์ (Destination)์„ ์ •ํ•ด ๋ผ์ธ(Line)์„ ๊ทธ๋ ค์ค˜ "์†"์˜ ํ˜•ํƒœ๋ฅผ ๋งŒ๋“ค์–ด ์ฃผ๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. 

LineCode.cs๋Š” ์‹œ์ž‘์ ๊ณผ ๋„์ฐฉ์  Point๋“ค์„ ๋ฐ›์•„์™€ Update() ํ•จ์ˆ˜๋ฅผ ๊ฑฐ์ณ ํ”„๋ ˆ์ž„๋งˆ๋‹ค ๋ผ์ธ์˜ ์œ„์น˜๋ฅผ ์—…๋ฐ์ดํŠธํ•˜์—ฌ ๊ทธ๋ ค์ฃผ๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
 
public class LineCode : MonoBehaviour
{
    LineRenderer lineRenderer;
 
    public Transform origin;
    public Transform destination;
 
    // Start is called before the first frame update
    void Start()
    {
        lineRenderer = GetComponent<LineRenderer>();
        lineRenderer.startWidth = 0.1f;
        lineRenderer.endWidth = 0.1f;
    }
 
    // Update is called once per frame
    void Update()
    {
        lineRenderer.SetPosition(0, origin.position);
        lineRenderer.SetPosition(1, destination.position);
    }
}
cs

 

  1. ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์ž„ํฌํŠธ:
    • using System.Collections;: Unity์—์„œ ์ œ๊ณตํ•˜๋Š” ๊ธฐ๋ณธ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ธ Collections์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.
    • using System.Collections.Generic;: Unity์—์„œ ์ œ๊ณตํ•˜๋Š” ๊ธฐ๋ณธ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ธ Generic Collections์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.
    • using UnityEngine;: Unity์˜ ๊ธฐ๋ณธ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ธ UnityEngine์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.
  2. ๋ณ€์ˆ˜ ์„ ์–ธ:
    • LineRenderer lineRenderer;: LineRenderer ์ปดํฌ๋„ŒํŠธ๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•œ ๋ณ€์ˆ˜๋ฅผ ์„ ์–ธํ•ฉ๋‹ˆ๋‹ค.
  3. Transform ๋ณ€์ˆ˜ ์„ ์–ธ:
    • public Transform origin;: ์‹œ์ž‘์ ์„ ๋‚˜ํƒ€๋‚ด๋Š” Transform ๋ณ€์ˆ˜๋ฅผ ์„ ์–ธํ•ฉ๋‹ˆ๋‹ค.
    • public Transform destination;: ๋„์ฐฉ์ ์„ ๋‚˜ํƒ€๋‚ด๋Š” Transform ๋ณ€์ˆ˜๋ฅผ ์„ ์–ธํ•ฉ๋‹ˆ๋‹ค.
  4. Start ํ•จ์ˆ˜:
    • void Start(): ์Šคํฌ๋ฆฝํŠธ๊ฐ€ ์‹คํ–‰๋  ๋•Œ ํ•œ ๋ฒˆ ํ˜ธ์ถœ๋˜๋Š” Start ํ•จ์ˆ˜์ž…๋‹ˆ๋‹ค.
    • lineRenderer = GetComponent<LineRenderer>();: LineRenderer ์ปดํฌ๋„ŒํŠธ๋ฅผ ๊ฐ€์ ธ์™€ lineRenderer ๋ณ€์ˆ˜์— ํ• ๋‹นํ•ฉ๋‹ˆ๋‹ค.
    • lineRenderer.startWidth = 0.1f;: ๋ผ์ธ์˜ ์‹œ์ž‘์  ๋„ˆ๋น„๋ฅผ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.
    • lineRenderer.endWidth = 0.1f;: ๋ผ์ธ์˜ ๋„์ฐฉ์  ๋„ˆ๋น„๋ฅผ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.
  5. Update ํ•จ์ˆ˜:
    • void Update(): ํ”„๋ ˆ์ž„๋งˆ๋‹ค ํ˜ธ์ถœ๋˜๋Š” Update ํ•จ์ˆ˜์ž…๋‹ˆ๋‹ค.
    • lineRenderer.SetPosition(0, origin.position);: ๋ผ์ธ์˜ ์‹œ์ž‘์ ์„ origin์˜ ์œ„์น˜๋กœ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค. origin.position์€ ์‹œ์ž‘์ ์˜ 3D ์ขŒํ‘œ๋ฅผ ๋‚˜ํƒ€๋ƒ…๋‹ˆ๋‹ค.
    • lineRenderer.SetPosition(1, destination.position);: ๋ผ์ธ์˜ ๋„์ฐฉ์ ์„ destination์˜ ์œ„์น˜๋กœ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค. destination.position์€ ๋„์ฐฉ์ ์˜ 3D ์ขŒํ‘œ๋ฅผ ๋‚˜ํƒ€๋ƒ…๋‹ˆ๋‹ค.

 

 


 

์‹คํ–‰ ์˜์ƒ

 

https://youtu.be/HNpTHFwbNy8

IP WebCam ํœด๋Œ€ํฐ ํ›„๋ฉด ์นด๋ฉ”๋ผ๋ฅผ ์ด์šฉํ–ˆ๊ธฐ์— "์†"์ด ์ขŒ์šฐ ๋ฐ˜์ „๋˜์—ˆ์ง€๋งŒ, ์ •์ƒ์ ์œผ๋กœ ์˜ค๋ธŒ์ ํŠธ๋“ค๊ณผ ์ƒํ˜ธ์ž‘์šฉํ•˜๊ณ  ์žˆ๋Š” ๋ชจ์Šต์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

 

728x90
๋ฐ˜์‘ํ˜•