๐ ๊นํ๋ธ ๋ ํฌ์งํ ๋ฆฌ ๋งํฌ
https://github.com/jiholee0/2022-IDPCD
๐ก ํ๋ก์ ํธ ๊ฐ์
์ ์ ์ ํฐ์นํจ๋๋ก ๋์ํ๋ ๊ธฐ์กด์ ํค์ค์คํฌ๋ ์๋ ฅ ์ ํ์ ๊ฐ์ ์ ์ฒด์ ๋ ธํ, ๊ธฐ๊ณ์ ๋ํ ๋ถ์ ์ ์ธ์ ๋ฑ์ผ๋ก ์ธํด ๋ ธ์ธ์๊ฒ ๋ถํธํจ์ ์ด๋ํ๋ค. ๋ฐ๋ผ์ ์ ์ ์ค์ฒ๋ก ํค์ค์คํฌ๋ฅผ ์ ์ดํจ์ผ๋ก์จ ๋ ธ์ธ์ด๋ ์๊ฐ์ฅ์ ์ธ์ ํฌํจํ ๋ชจ๋ ์ฌ์ฉ์๊ฐ ํค์ค์คํฌ๋ฅผ ๋ถ๋ด ์์ด ์ด์ฉํ ์ ์๋ ์์คํ ์ ์ค๊ณํ๊ณ ์ ํ์๋ค. ํค์ค์คํฌ๋ง๋ค ์ฃผ๋ฌธ ์ ํ์ํ ๋์์ด ์ฒ์ฐจ๋ง๋ณ์ด๋ฏ๋ก, ๋ ธ์ธ์๊ฒ ์ ํฉํ ํค์ค์คํฌ ๋ชจ๋ํฐ ํ๋ฉด์ ๊ฐ์์ผ๋ก ๊ตฌ์ฑํ๊ณ , ๊ฐ ๋์์ ์ ์ค์ฒ์ ๋งค์นญํ์๋ค. ํค์ค์คํฌ ์ ์ด์ ๊ฒฝ์ฐ ์์ผ ํต์ ๊ณผ openCV ์ด๋ฏธ์ง ์ถ๋ ฅ ํจ์๋ฅผ ์ด์ฉํ์ฌ ์ธ์ํ ์ ์ค์ฒ์ ๋ฐ๋ผ ์ด๋ฏธ์ง(ํค์ค์คํฌ ํ๋ฉด)๊ฐ ์ถ๋ ฅ๋๋ ํ๊ฒฝ์ ๊ฐ์์ผ๋ก ๊ตฌ์ฑํ์๋ค.
๐ก ์ฌ์ฉ ๊ธฐ์
Python, MediaPipe, OpenCV, K-NN ์๊ณ ๋ฆฌ์ฆ
๐ก ์ฐธ๊ณ ์คํ์์ค
https://github.com/kairess/Rock-Paper-Scissors-Machine
๐ก ๊ตฌํ
1. ๋ฐ์ดํฐ ์์ง ๋จ๊ณ
import cv2
import mediapipe as mp
import numpy as np
max_num_hands = 1
gesture = {
0:'zero', 1:'one', 2:'two', 3:'five', 4:'ok', 5:'good'
}
# MediaPipe hands model
mp_hands = mp.solutions.hands
mp_drawing = mp.solutions.drawing_utils
hands = mp_hands.Hands(
max_num_hands=max_num_hands,
min_detection_confidence=0.5,
min_tracking_confidence=0.5)
# Gesture recognition data
file = np.genfromtxt('C:/Users/LJH/Desktop/2022-IDPCD/project/data/my_gesture_train.csv', delimiter=',')
print(file.shape)
cap = cv2.VideoCapture(0, cv2.CAP_DSHOW)
def click(event, x, y, flags, param):
global data, file
if event == cv2.EVENT_LBUTTONDOWN:
file = np.vstack((file, data))
print(file.shape)
cv2.namedWindow('Dataset')
cv2.setMouseCallback('Dataset', click)
while cap.isOpened():
ret, img = cap.read()
if not ret:
continue
img = cv2.flip(img, 1)
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
result = hands.process(img)
img = cv2.cvtColor(img, cv2.COLOR_RGB2BGR)
if result.multi_hand_landmarks is not None:
for res in result.multi_hand_landmarks:
joint = np.zeros((21, 3))
for j, lm in enumerate(res.landmark):
joint[j] = [lm.x, lm.y, lm.z]
# Compute angles between joints
v1 = joint[[0,1,2,3,0,5,6,7,0,9,10,11,0,13,14,15,0,17,18,19],:] # Parent joint
v2 = joint[[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20],:] # Child joint
v = v2 - v1 # [20,3]
# Normalize v
v = v / np.linalg.norm(v, axis=1)[:, np.newaxis]
# Get angle using arcos of dot product
angle = np.arccos(np.einsum('nt,nt->n',
v[[0,1,2,4,5,6,8,9,10,12,13,14,16,17,18],:],
v[[1,2,3,5,6,7,9,10,11,13,14,15,17,18,19],:])) # [15,]
angle = np.degrees(angle) # Convert radian to degree
data = np.array([angle], dtype=np.float32)
data = np.append(data, 5) # ์์งํ ์ ์ค์ฒ์ INDEX
mp_drawing.draw_landmarks(img, res, mp_hands.HAND_CONNECTIONS)
resize = cv2.resize(img, (200, 150), interpolation=cv2.INTER_CUBIC)
cv2.imshow('Dataset', resize)
if cv2.waitKey(1) == ord('q'):
break
np.savetxt('C:/Users/LJH/Desktop/2022-IDPCD/project/data/my_gesture_train.csv', file, delimiter=',')
2. ์ ์ค์ฒ ์ธ์ ๋จ๊ณ
import cv2 # ์น์บ ์ ์ด ๋ฐ ML ์ฌ์ฉ
import mediapipe as mp # ์ ์ธ์์ ํ ๊ฒ
import numpy as np
import time
from scipy import stats
def recog_gesture() -> str :
result_list = []
# ์ ์ค์ฒ ์ธ์
max_num_hands = 1 # ์์ ์ต๋ 1๊ฐ๋ง ์ธ์
kiosk_gesture = {
0:'zero', 1:'one', 2:'two', 3:'five', 4:'ok', 5:'good'
}
# MediaPipe hands model
mp_hands = mp.solutions.hands # ์น์บ ์์์์ ์๊ฐ๋ฝ ๋ง๋์ ํฌ์ธํธ๋ฅผ ๊ทธ๋ฆด ์ ์๊ฒ ๋์์ฃผ๋ ์ ํธ๋ฆฌํฐ1
mp_drawing = mp.solutions.drawing_utils # ์น์บ ์์์์ ์๊ฐ๋ฝ ๋ง๋์ ํฌ์ธํธ๋ฅผ ๊ทธ๋ฆด ์ ์๊ฒ ๋์์ฃผ๋ ์ ํธ๋ฆฌํฐ2
# ์๊ฐ๋ฝ detection ๋ชจ๋์ ์ด๊ธฐํ
hands = mp_hands.Hands(
max_num_hands=max_num_hands, # ์ต๋ ๋ช ๊ฐ์ ์์ ์ธ์?
min_detection_confidence=0.5, # 0.5๋ก ํด๋๋ ๊ฒ ์ข๋ค!
min_tracking_confidence=0.5)
# ์ ์ค์ฒ ์ธ์ ๋ชจ๋ธ
file = np.genfromtxt('C:/Users/LJH/Desktop/2022-IDPCD/project/data/my_gesture_train.csv', delimiter=',') # ๊ฐ ์ ์ค์ฒ๋ค์ ๋ผ๋ฒจ๊ณผ ๊ฐ๋๊ฐ ์ ์ฅ๋์ด ์์, ์ ํ๋๋ฅผ ๋์ด๊ณ ์ถ์ผ๋ฉด ๋ฐ์ดํฐ๋ฅผ ์ถ๊ฐํด๋ณด์!**
angle = file[:,:-1].astype(np.float32) # ๊ฐ๋
label = file[:, -1].astype(np.float32) # ๋ผ๋ฒจ
knn = cv2.ml.KNearest_create() # knn(k-์ต๊ทผ์ ์๊ณ ๋ฆฌ์ฆ)์ผ๋ก
knn.train(angle, cv2.ml.ROW_SAMPLE, label) # ํ์ต!
cap = cv2.VideoCapture(1, cv2.CAP_DSHOW)
if cap.isOpened():
number = 0
# target_tick = time.time() + 2
# while time.time() < target_tick :
while True :
if number > 50 : break
ret, img = cap.read()
if not ret:
continue
img = cv2.flip(img, 1)
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
result = hands.process(img)
img = cv2.cvtColor(img, cv2.COLOR_RGB2BGR)
# ๊ฐ๋๋ฅผ ์ธ์ํ๊ณ ์ ์ค์ฒ๋ฅผ ์ธ์ํ๋ ๋ถ๋ถ
if result.multi_hand_landmarks is not None: # ๋ง์ฝ ์์ ์ธ์ํ๋ฉด
number += 1
for res in result.multi_hand_landmarks:
joint = np.zeros((21, 3)) # joint == ๋๋๋งํฌ์์ ๋นจ๊ฐ ์ , joint๋ 21๊ฐ๊ฐ ์๊ณ x,y,z ์ขํ๋๊น 21,3
for j, lm in enumerate(res.landmark):
joint[j] = [lm.x, lm.y, lm.z] # ๊ฐ joint๋ง๋ค x,y,z ์ขํ ์ ์ฅ
# Compute angles between joints joint๋ง๋ค ๊ฐ๋ ๊ณ์ฐ
# **๊ณต์๋ฌธ์ ๋ค์ด๊ฐ๋ณด๋ฉด ๊ฐ joint ๋ฒํธ์ ์ธ๋ฑ์ค๊ฐ ๋์ด**
v1 = joint[[0,1,2,3,0,5,6,7,0,9,10,11,0,13,14,15,0,17,18,19],:] # Parent joint
v2 = joint[[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20],:] # Child joint
v = v2 - v1 # [20,3]๊ด์ ๋ฒกํฐ
# Normalize v
v = v / np.linalg.norm(v, axis=1)[:, np.newaxis] # ๋ฒกํฐ ์ ๊ทํ(ํฌ๊ธฐ 1 ๋ฒกํฐ) = v / ๋ฒกํฐ์ ํฌ๊ธฐ
# Get angle using arcos of dot product **๋ด์ ํ arcos์ผ๋ก ๊ฐ๋๋ฅผ ๊ตฌํด์ค**
angle = np.arccos(np.einsum('nt,nt->n',
v[[0,1,2,4,5,6,8,9,10,12,13,14,16,17,18],:],
v[[1,2,3,5,6,7,9,10,11,13,14,15,17,18,19],:])) # [15,]
angle = np.degrees(angle) # Convert radian to degree
# Inference gesture ํ์ต์ํจ ์ ์ค์ฒ ๋ชจ๋ธ์ ์ฐธ์กฐ๋ฅผ ํ๋ค.
data = np.array([angle], dtype=np.float32)
ret, results, neighbours, dist = knn.findNearest(data, 3) # k๊ฐ 3์ผ ๋ ๊ฐ์ ๊ตฌํ๋ค!
idx = int(results[0][0]) # ์ธ๋ฑ์ค๋ฅผ ์ ์ฅ!
# Draw gesture result
if idx in kiosk_gesture.keys():
cv2.putText(img, text=kiosk_gesture[idx].upper(), org=(int(res.landmark[0].x * img.shape[1]), int(res.landmark[0].y * img.shape[0] + 20)), fontFace=cv2.FONT_HERSHEY_SIMPLEX, fontScale=1, color=(255, 255, 255), thickness=2)
rst = kiosk_gesture[idx].upper()
result_list.append(rst)
# Other gestures ๋ชจ๋ ์ ์ค์ฒ๋ฅผ ํ์ํ๋ค๋ฉด
# cv2.putText(img, text=gesture[idx].upper(), org=(int(res.landmark[0].x * img.shape[1]), int(res.landmark[0].y * img.shape[0] + 20)), fontFace=cv2.FONT_HERSHEY_SIMPLEX, fontScale=1, color=(255, 255, 255), thickness=2)
mp_drawing.draw_landmarks(img, res, mp_hands.HAND_CONNECTIONS) # ์์ ๋๋๋งํฌ๋ฅผ ๊ทธ๋ ค์ค
resize = cv2.resize(img, (200, 150), interpolation=cv2.INTER_CUBIC)
cv2.imshow('gesture', resize)
if cv2.waitKey(1) == ord('q'):
break
# print('--------------\n total number : ', number)
# print('result list : ', result_list, '\n')
mode = stats.mode(result_list)[0]
if len(mode) == 0 : return 'fail'
else : return mode[0]
# print(recog_gesture())
3. ์์ผ ํต์ ๋จ๊ณ
- ์๋ฒ
# server
# ์ญํ : client๋ก๋ถํฐ ์ ์ค์ฒ ์ธ์ ๊ฒฐ๊ณผ๋ฅผ ๋ฐ์ ํค์ค์คํฌ ํ๋ฉด์ ์ ์ดํ ํ ๊ฒฐ๊ณผ๋ฅผ ์ ์กํ๋ค.
import socket, threading
from flask import render_template
import json, datetime, cv2
orders = []
pickUpYn = False
cashYn = False
def ctrlKiosk(recvData) -> json :
global orders
global pickUpYn
global cashYn
global openImage
curPageNum = int(recvData.get('curPageNum'))
nextPageNum = curPageNum + 1
result = recvData.get('result')
src = ""
if curPageNum == 0 : # ์ด๊ธฐ ํ๋ฉด ๋์ฐ๊ธฐ
if result == 'SERVER' :
src = "C:/Users/LJH/Desktop/2022-IDPCD/project/kiosk/image/001.png"
elif curPageNum == 1 : # ์ด๊ธฐ ํ๋ฉด
if result == 'OK' : # ๋ค์ ํ๋ฉด์ผ๋ก
src = "C:/Users/LJH/Desktop/2022-IDPCD/project/kiosk/image/002.png"
elif curPageNum == 2 : # ์ฃผ๋ฌธ ๋ฉ๋ด์ ์ข
๋ฅ ์ ํ
if result == 'ZERO' :
src = "C:/Users/LJH/Desktop/2022-IDPCD/project/kiosk/image/003.png"
elif curPageNum == 3 :
if result == 'ZERO' :
order = {
'menu': '์น์ฆ๋ฒ๊ฑฐ',
'count' : 1,
'price' : 3900
}
orders.append(order)
src = "C:/Users/LJH/Desktop/2022-IDPCD/project/kiosk/image/004.png"
elif curPageNum == 4 :
if result == 'OK' :
order = {
'menu': '์ฝ๋ผ, ๊ฐ์ํ๊น',
'count' : 1,
'price' : 2000
}
orders.append(order)
src = "C:/Users/LJH/Desktop/2022-IDPCD/project/kiosk/image/005.png"
elif result == 'FIVE' :
src = "C:/Users/LJH/Desktop/2022-IDPCD/project/kiosk/image/005.png"
elif curPageNum == 5 :
if result == 'OK' :
src = "C:/Users/LJH/Desktop/2022-IDPCD/project/kiosk/image/002.png"
nextPageNum = 2
elif result == 'FIVE' :
src = "C:/Users/LJH/Desktop/2022-IDPCD/project/kiosk/image/006.png"
elif curPageNum == 6 :
if result == 'ONE' :
src = "C:/Users/LJH/Desktop/2022-IDPCD/project/kiosk/image/007.png"
elif result == 'TWO' :
pickUpYn = True
src = "C:/Users/LJH/Desktop/2022-IDPCD/project/kiosk/image/007.png"
elif curPageNum == 7 :
if result == 'ONE' :
src = "C:/Users/LJH/Desktop/2022-IDPCD/project/kiosk/image/008.png"
elif result == 'TWO' :
cashYn = True
src = "C:/Users/LJH/Desktop/2022-IDPCD/project/kiosk/image/008.png"
elif curPageNum == 8 :
if result == 'OK' :
print('์ฃผ๋ฌธ ๋ด์ญ :',orders)
recvData.update(isComplete=True)
image = cv2.imread(src, cv2.IMREAD_UNCHANGED)
if image is None :
print('Image load failed...Please gesture one more time.')
recvData.update(result='FAIL')
else :
cv2.destroyAllWindows
openImage = cv2.resize(image, dsize=(300,420), interpolation=cv2.INTER_AREA)
recvData.update(curPageNum=int(nextPageNum))
cv2.imshow("kiosk", openImage)
cv2.waitKey(1)
return recvData
def binder(client_socket, addr):
# ์ปค๋ฅ์
์ด ๋๋ฉด ์ ์ ์ฃผ์๊ฐ ๋์จ๋ค.
print('Connected by', addr)
try:
initData = {
'isComplete' : False,
'curPageNum' : 0,
'result' : 'SERVER'
}
ctrlKiosk(initData)
while True :
# ํด๋ผ์ด์ธํธ์๊ฒ ๋ฐ์ดํฐ ๋ฐ์
data = client_socket.recv(1024)
recvData = json.loads(data)
print('recvData : ',recvData, datetime.datetime.now())
# ํค์ค์คํฌ ํ๋ฉด ์ ์ด
servData = ctrlKiosk(recvData)
# ํด๋ผ์ด์ธํธ์๊ฒ ๋ฐ์ดํฐ ์ ์ก
sendData = json.dumps(servData)
client_socket.send(bytes(sendData, 'utf-8'))
except:
# ์ ์ ํด์ ์ except
print("except : " , addr)
finally:
# ์ข
๋ฃ
client_socket.close()
# ์์ผ ์์ฑ
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
# 10000๋ฒ ํฌํธ ์ฌ์ฉ
server_socket.bind(('127.0.0.1',10000))
server_socket.listen(1)
try:
# ํด๋ผ์ด์ธํธ๊ฐ ์ ์ํ๊ธฐ ์ ๊น์ง ์๋ฒ๋ ์คํ๋์ผํ๊ธฐ ๋๋ฌธ์ ๋ฌดํ ๋ฃจํ ์ฌ์ฉ
while True:
client_socket, addr = server_socket.accept()
# ์ฐ๋ ๋ ์ฌ์ฉํด์ ๋๊ธฐ
th = threading.Thread(target=binder, args = (client_socket,addr))
th.start()
except:
print("server")
finally:
# ์ข
๋ฃ
server_socket.close()
- ํด๋ผ์ด์ธํธ
# client
# ์ญํ : server๋ก๋ถํฐ ํ์ด์ง ๋ฒํธ์ ์ ํ ์ฌ๋ถ๋ฅผ ๋ฐ์ ์ฌ๋ฐ๋ฅธ ์ ์ค์ฒ๋ฅผ ์ธ์ํ ํ ๊ฒฐ๊ณผ๋ฅผ ์ ์กํ๋ค.
import socket, datetime, json
import sys
sys.path.append("/recog_gesture")
import recog_gesture as recog_gesture
HOST = '127.0.0.1' # local ํธ์คํธ ์ฌ์ฉ
PORT = 10000 # 10000๋ฒ ํฌํธ ์ฌ์ฉ
# ์์ผ ์์ฑ
client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# ์ ์
client_socket.connect((HOST, PORT))
cliData = {
'isComplete' : False,
'curPageNum' : 1,
'result' : ''
}
while cliData.get('isComplete') == False :
# ์ ์ค์ฒ ์ธ์ ๊ฒฐ๊ณผ ๊ฐ์ ธ์ค๊ธฐ
result = recog_gesture.recog_gesture()
# ๋ฐ์ดํฐ ์๋ฒ์ ์ ์ก
cliData.update(result=result)
sendData = json.dumps(cliData)
client_socket.send(bytes(sendData, 'utf-8'))
# ์๋ฒ์๊ฒ ๋ฐ์ดํฐ ๋ฐ์
data = client_socket.recv(1024)
print(data)
recvData = json.loads(data)
print('recvData : ',recvData, datetime.datetime.now())
cliData.update( isComplete=bool(recvData.get('isComplete')), curPageNum=int(recvData.get('curPageNum')))
client_socket.close()