728x90
반응형
1. 아두이노 코드
#include <Servo.h>
Servo neck;
Servo mouth;
void setup() {
Serial.begin(9600);
neck.attach(6); // 목 서보모터 연결 핀 번호 6번
mouth.attach(7); // 입 서보모터 연결 핀 번호 7번
neck.write(90); // 정면을 90도로 셋팅
mouth.write(0); // 입은 0도로 셋팅
}
void loop() {
while (Serial.available() > 0) {
char data = Serial.read();
if (data == 'a') {
neck.write(50);
}
if (data == 'b') {
neck.write(130);
}
if (data == 'c') {
neck.write(90);
}
if (data == 'd') {
mouth.write(90);
}
if (data == 'e') {
mouth.write(0);
}
}
}
2. 파이썬 코드 (mediapipe 매쉬)
![]() |
![]() |
![]() |
![]() |
파이썬 5번째 줄 시리얼 포트 설정은 아두이노 보드 포트 번호와 속도는 꼭 확인하여 바꿔야 함.
import cv2
import mediapipe as mp
import math
import serial
# 시리얼 포트 설정 (포트 이름과 속도는 환경에 맞게 변경)
ser = serial.Serial('COM9', 9600)
mp_drawing = mp.solutions.drawing_utils
mp_face_mesh = mp.solutions.face_mesh
# 랜드마크 인덱스
NOSE_TIP = 4
LEFT_FACE = 234
RIGHT_FACE = 454
MOUTH_LEFT = 61
MOUTH_RIGHT = 291
MOUTH_UP_INNER = 13
MOUTH_DOWN_INNER = 14
def px(pt, w, h):
return (pt.x * w, pt.y * h)
def dist_xy(a, b):
return math.hypot(a[0] - b[0], a[1] - b[1])
cap = cv2.VideoCapture(0)
# 입 벌림 임계값
OPEN_TH = 0.065
CLOSE_TH = 0.055
mouth_is_open = False
with mp_face_mesh.FaceMesh(
static_image_mode=False,
refine_landmarks=True,
max_num_faces=1,
min_detection_confidence=0.5,
min_tracking_confidence=0.5
) as face_mesh:
while cap.isOpened():
ret, frame = cap.read()
if not ret:
break
frame = cv2.flip(frame, 1)
rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
results = face_mesh.process(rgb)
h, w = frame.shape[:2]
direction_text = ""
mouth_text = ""
if results.multi_face_landmarks:
for face_landmarks in results.multi_face_landmarks:
lm = face_landmarks.landmark
# 방향 판별
nose_x = lm[NOSE_TIP].x * w
left_face_x = lm[LEFT_FACE].x * w
right_face_x = lm[RIGHT_FACE].x * w
face_center_x = (left_face_x + right_face_x) / 2
diff = nose_x - face_center_x
if diff < -10:
direction_text = "LEFT"
ser.write(b'a')
elif diff > 10:
direction_text = "RIGHT"
ser.write(b'b')
else:
direction_text = "CENTER"
ser.write(b'c')
# 입 벌림 판별
p_left = px(lm[MOUTH_LEFT], w, h)
p_right = px(lm[MOUTH_RIGHT], w, h)
p_up = px(lm[MOUTH_UP_INNER], w, h)
p_down = px(lm[MOUTH_DOWN_INNER], w, h)
mouth_width = dist_xy(p_left, p_right) + 1e-6
mouth_opening = dist_xy(p_up, p_down)
mar = mouth_opening / mouth_width
if not mouth_is_open and mar > OPEN_TH:
mouth_is_open = True
elif mouth_is_open and mar < CLOSE_TH:
mouth_is_open = False
if mouth_is_open:
mouth_text = "MOUTH OPEN"
ser.write(b'd')
else:
mouth_text = "MOUTH CLOSE"
ser.write(b'e')
# 흰색 얼굴 메쉬
mp_drawing.draw_landmarks(
image=frame,
landmark_list=face_landmarks,
connections=mp_face_mesh.FACEMESH_TESSELATION,
landmark_drawing_spec=None,
connection_drawing_spec=mp_drawing.DrawingSpec(color=(255, 255, 255), thickness=1, circle_radius=0)
)
# 화면 표시
if direction_text:
cv2.putText(frame, direction_text, (30, 50),
cv2.FONT_HERSHEY_SIMPLEX, 1.5, (0, 255, 0), 3, cv2.LINE_AA)
if mouth_text:
(tw, th), _ = cv2.getTextSize(mouth_text, cv2.FONT_HERSHEY_SIMPLEX, 1.0, 3)
x = w - tw - 30
y = 50
cv2.putText(frame, mouth_text, (x, y),
cv2.FONT_HERSHEY_SIMPLEX, 1.0, (0, 0, 255), 3, cv2.LINE_AA)
cv2.imshow("Face Direction + Mouth Open/Close", frame)
if cv2.waitKey(1) & 0xFF == 27:
break
cap.release()
cv2.destroyAllWindows()
ser.close()
KakaoTalk_20250814_175148280.mp4
3.91MB
728x90
반응형
'인공지능 기초 수업' 카테고리의 다른 글
포즈모델을 활용한 AI 구글 공룡 게임 (1) | 2025.06.23 |
---|---|
Mediapipe hands 모델 왼손, 오른손 구별 하기. (0) | 2025.04.15 |
Thonny CV, 미디어파이프 활용을 위한 라이브러리 설치 (0) | 2025.03.19 |
Thonny 인터페이스 설치와 소개 (0) | 2025.03.19 |
아두이노 컨베이너벨트 코드 (0) | 2024.10.25 |