1. 미디어 파이프 스튜디오 확인
https://mediapipe-studio.webapps.google.com/home
2. 환경 구성하기 : VScode 에디터 설치 및 아나콘다 설치
1) vscode 설치 링크
https://code.visualstudio.com/download
2) 아나콘다 설치 링크
https://www.anaconda.com/download/success
설치할때 중간에 반드시 모두 체크
3. vscode 파이썬 기본 사용법
- 파이썬 설치
- 한국어 팩 설치
- 설치 후 자동 설정 변경
상단 메뉴 - 파일 - 폴더 열기 선택
새폴더 만들고 폴더 선택
새파일 만들기
기본 코드를 작성하고 컨트롤 에스를 눌러 반드시 저장하고 우측 상단의 재생 버튼을 눌러 코드를 동작시켜 보자
터미널에서 동작 확인
4. chatgpt로 부터 미디어 파이프 AI 구동 프로그램 받아오기
- gpt 접속 후 로그인
import cv2
import mediapipe as mp
# MediaPipe 손 인식 초기화
mp_hands = mp.solutions.hands
hands = mp_hands.Hands(
static_image_mode=False, # 연속된 이미지에서 손을 인식
max_num_hands=2, # 최대 손 인식 개수
min_detection_confidence=0.5, # 손 인식 확률 임계값
min_tracking_confidence=0.5) # 손 추적 확률 임계값
# 그리기 도구 초기화
mp_drawing = mp.solutions.drawing_utils
# 웹캠 캡처 초기화
cap = cv2.VideoCapture(0)
# 비디오 프레임 루프
while cap.isOpened():
success, image = cap.read()
if not success:
print("웹캠에서 이미지를 읽어오는 데 실패했습니다.")
continue
# 이미지를 RGB로 변환
image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
# 결과를 원래 이미지에 표시하기 위해 이미지 쓰기를 비활성화
image.flags.writeable = False
# 손 인식 수행
results = hands.process(image)
# 이미지 쓰기 활성화
image.flags.writeable = True
# 이미지를 BGR로 다시 변환
image = cv2.cvtColor(image, cv2.COLOR_RGB2BGR)
# 인식된 손가락 랜드마크 표시
if results.multi_hand_landmarks:
for hand_landmarks in results.multi_hand_landmarks:
mp_drawing.draw_landmarks(
image, hand_landmarks, mp_hands.HAND_CONNECTIONS)
# 결과 이미지 표시
cv2.imshow('MediaPipe Hands', image)
# 'q' 키를 누르면 종료
if cv2.waitKey(5) & 0xFF == 113:
break
# 웹캠 해제
cap.release()
우선 코드를 돌리기 전에 인터프린터(코드 해석기)가 아나콘다 base로 잡혀 있는지 확인해 준다.
gpt가 만들어준 코드를 돌리고 vscode에 붙여넣기 후 저장 한뒤 코드를 돌려 본다.
해당 코드를 동작시기키 위해 필요한
opencv-python 모듈과 mediapipe 모듈이 없다고 에러가 날것이다.
pip install mediapipe opencv-python
터미널에 위 명령어를 넣어서 해당 패키지들을 모두 설치하고 다시 동작 시켜 보자.
잘동작 할것이다.
5. AI 모델(파이썬) - 아두이노 시리얼 통신 연결
아두이노를 노트북에 연결하고 아두이노 프로그램을 연다
아두이노 연결 com번호 확인한다.
아두이노 코드를 업로드 한다. (기존에 이미 코드가 업로드 되어 있다면 다시 업로드 안해도 된다.)
#include <Servo.h>
// 서보모터 객체 생성
Servo servo1;
Servo servo2;
Servo servo3;
// 서보모터 초기 각도 설정
int angle1 = 90;
int angle2 = 90;
int angle3 = 90;
bool servo3At90 = true; // 서보모터3의 현재 위치를 추적
void setup() {
// 서보모터 핀 설정
servo1.attach(9);
servo2.attach(10);
servo3.attach(11);
// 초기 각도로 서보모터 설정
servo1.write(angle1);
servo2.write(angle2);
servo3.write(angle3);
// 시리얼 통신 시작
Serial.begin(9600);
}
void loop() {
// 시리얼 입력이 있는지 확인
if (Serial.available() > 0) {
char input = Serial.read();
// 서보모터1 제어
if (input == '4') {
angle1 += 5;
if (angle1 > 180) angle1 = 180;
servo1.write(angle1);
} else if (input == '6') {
angle1 -= 5;
if (angle1 < 0) angle1 = 0;
servo1.write(angle1);
}
// 서보모터2 제어
if (input == '8') {
angle2 -= 5;
if (angle2 < 0) angle2 = 0;
servo2.write(angle2);
} else if (input == '2') {
angle2 += 5;
if (angle2 > 180) angle2 = 180;
servo2.write(angle2);
}
// 서보모터3 제어
if (input == '5') {
if (servo3At90) {
angle3 = 135;
} else {
angle3 = 100;
}
servo3.write(angle3);
servo3At90 = !servo3At90;
}
}
}
시리얼 모니터를 열고 모터 제어 코드인 8,4,5,6,2 등을 넣어 본다. 모터 3개가 정상적으로 동작하는지 확인한다.
테스트를 마쳤으면 반드시 시리얼 모니터를 끄자. 나중에 파이썬 시리얼 통신과 충돌이 날수 도 있다.
챗지피티 명령을 준다.
결과
import cv2
import mediapipe as mp
import serial
import time
# 시리얼 포트 초기화 (COM 포트와 보드레이트는 환경에 맞게 설정)
ser = serial.Serial('COM3', 9600, timeout=1)
time.sleep(2) # 아두이노 리셋 방지를 위한 지연
# MediaPipe 초기화
mp_hands = mp.solutions.hands
hands = mp_hands.Hands(
static_image_mode=False,
max_num_hands=1,
min_detection_confidence=0.5,
min_tracking_confidence=0.5)
mp_drawing = mp.solutions.drawing_utils
# 웹캠 캡처
cap = cv2.VideoCapture(0)
def send_command(command):
ser.write(f'{command}\n'.encode('utf-8'))
while cap.isOpened():
ret, frame = cap.read()
if not ret:
break
height, width, _ = frame.shape
frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
frame.flags.writeable = False
results = hands.process(frame)
frame.flags.writeable = True
frame = cv2.cvtColor(frame, cv2.COLOR_RGB2BGR)
if results.multi_hand_landmarks:
for hand_landmarks in results.multi_hand_landmarks:
# 손 중심 위치 계산
cx = int(hand_landmarks.landmark[mp_hands.HandLandmark.WRIST].x * width)
cy = int(hand_landmarks.landmark[mp_hands.HandLandmark.WRIST].y * height)
# 화면 분할하여 위치 판단
if cx < width // 2 and cy < height // 2:
send_command(8) # 위
elif cx < width // 2 and cy >= height // 2:
send_command(4) # 왼쪽
elif cx >= width // 2 and cy < height // 2:
send_command(6) # 오른쪽
elif cx >= width // 2 and cy >= height // 2:
send_command(2) # 아래
# 주먹 쥠 판단
finger_tips = [mp_hands.HandLandmark.INDEX_FINGER_TIP, mp_hands.HandLandmark.MIDDLE_FINGER_TIP,
mp_hands.HandLandmark.RING_FINGER_TIP, mp_hands.HandLandmark.PINKY_TIP]
fist = True
for tip in finger_tips:
fingertip = hand_landmarks.landmark[tip]
mcp = hand_landmarks.landmark[tip - 2] # TIP에서 2번 뒤가 MCP
if ((fingertip.x - mcp.x) ** 2 + (fingertip.y - mcp.y) ** 2) ** 0.5 > 0.1:
fist = False
break
if fist:
send_command(5) # 주먹 쥠
mp_drawing.draw_landmarks(frame, hand_landmarks, mp_hands.HAND_CONNECTIONS)
cv2.imshow('MediaPipe Hands', frame)
if cv2.waitKey(5) & 0xFF == 113: # 'q' 키를 누르면 종료
break
cap.release()
cv
코드를 vscode에 넣고 pyserial 설치 하고, com숫자 바꾸고, 컨트롤 에스로 저장하고 동작을 시켜보자.
잘동작 할것이다.
하지만 동작이 불안정하다.
chatgpt와 함꼐 최적화 과정이 남았다..
6. chatgpt와 함께하는 AI 모델 최적화 작업 예시
최적화 작업에 정답은 없다. 하지만 여러번 시도를 통해 좀더 안정적인 동작을 구현할 수 있다. 아래는 내가 실습한 과정이다.
코드
import cv2
import mediapipe as mp
import serial
import time
# 시리얼 포트 초기화
ser = serial.Serial('COM3', 9600, timeout=1)
time.sleep(2) # 아두이노 리셋 방지를 위한 지연
# MediaPipe 초기화
mp_hands = mp.solutions.hands
hands = mp_hands.Hands(
static_image_mode=False,
max_num_hands=1,
min_detection_confidence=0.5,
min_tracking_confidence=0.5)
mp_drawing = mp.solutions.drawing_utils
# 웹캠 캡처
cap = cv2.VideoCapture(0)
def send_command(command):
ser.write(f'{command}\n'.encode('utf-8'))
return f'Sending: {command}'
while cap.isOpened():
ret, frame = cap.read()
if not ret:
break
height, width, _ = frame.shape
frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
frame.flags.writeable = False
results = hands.process(frame)
frame.flags.writeable = True
frame = cv2.cvtColor(frame, cv2.COLOR_RGB2BGR)
# 사분면 선 그리기
cv2.line(frame, (width // 2, 0), (width // 2, height), (255, 0, 0), 2)
cv2.line(frame, (0, height // 2), (width, height // 2), (255, 0, 0), 2)
current_command = "None"
if results.multi_hand_landmarks:
for hand_landmarks in results.multi_hand_landmarks:
cx = int(hand_landmarks.landmark[mp_hands.HandLandmark.WRIST].x * width)
cy = int(hand_landmarks.landmark[mp_hands.HandLandmark.WRIST].y * height)
# 위치에 따른 시리얼 커맨드 결정
if cx < width // 2 and cy < height // 2:
current_command = send_command(8) # 위
elif cx < width // 2 and cy >= height // 2:
current_command = send_command(4) # 왼쪽
elif cx >= width // 2 and cy < height // 2:
current_command = send_command(6) # 오른쪽
elif cx >= width // 2 and cy >= height // 2:
current_command = send_command(2) # 아래
# 주먹 쥠 검사
finger_tips = [mp_hands.HandLandmark.INDEX_FINGER_TIP, mp_hands.HandLandmark.MIDDLE_FINGER_TIP,
mp_hands.HandLandmark.RING_FINGER_TIP, mp_hands.HandLandmark.PINKY_TIP]
fist = True
for tip in finger_tips:
fingertip = hand_landmarks.landmark[tip]
mcp = hand_landmarks.landmark[tip - 2]
if ((fingertip.x - mcp.x) ** 2 + (fingertip.y - mcp.y) ** 2) ** 0.5 > 0.1:
fist = False
break
if fist:
current_command = send_command(5) # 주먹
mp_drawing.draw_landmarks(frame, hand_landmarks, mp_hands.HAND_CONNECTIONS)
# 현재 보내고 있는 시리얼 커맨드를 화면에 표시
cv2.putText(frame, current_command, (10, 30), cv2.FONT_HERSHEY_SIMPLEX, 1, (255, 255, 0), 2, cv2.LINE_AA)
cv2.imshow('MediaPipe Hands', frame)
if cv2.waitKey(5) & 0xFF == 113: # 'q' 키를 누르면 종료
break
com 숫자는 항상 변경시키든 gpt에게 코드 출력 할때 숫자를 알려주자.
동작 예시
import cv2
import mediapipe as mp
import serial
import time
# 시리얼 포트 초기화
ser = serial.Serial('COM3', 9600, timeout=1)
time.sleep(2) # 아두이노 리셋 방지를 위한 지연
# MediaPipe 초기화
mp_hands = mp.solutions.hands
hands = mp_hands.Hands(
static_image_mode=False,
max_num_hands=1,
min_detection_confidence=0.5,
min_tracking_confidence=0.5)
mp_drawing = mp.solutions.drawing_utils
# 웹캠 캡처
cap = cv2.VideoCapture(0)
def send_command(command):
ser.write(f'{command}\n'.encode('utf-8'))
return f'Sending: {command}'
# 방향키 그리기 함수
def draw_direction_keys(img, center_x, center_y, size):
cv2.rectangle(img, (center_x - size, center_y - 3 * size), (center_x + size, center_y - size), (200, 200, 200), -1) # Up
cv2.rectangle(img, (center_x - size, center_y + size), (center_x + size, center_y + 3 * size), (200, 200, 200), -1) # Down
cv2.rectangle(img, (center_x - 3 * size, center_y - size), (center_x - size, center_y + size), (200, 200, 200), -1) # Left
cv2.rectangle(img, (center_x + size, center_y - size), (center_x + 3 * size, center_y + size), (200, 200, 200), -1) # Right
while cap.isOpened():
ret, frame = cap.read()
if not ret:
break
height, width, _ = frame.shape
frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
frame.flags.writeable = False
results = hands.process(frame)
frame.flags.writeable = True
frame = cv2.cvtColor(frame, cv2.COLOR_RGB2BGR)
key_size = 50
center_x, center_y = width - 300, height // 2
draw_direction_keys(frame, center_x, center_y, key_size)
if results.multi_hand_landmarks:
for hand_landmarks in results.multi_hand_landmarks:
index_tip = hand_landmarks.landmark[mp_hands.HandLandmark.INDEX_FINGER_TIP]
index_x = int(index_tip.x * width)
index_y = int(index_tip.y * height)
# 방향키 위치 확인 및 커맨드 전송
if index_x > center_x - key_size and index_x < center_x + key_size:
if index_y > center_y - 3 * key_size and index_y < center_y - key_size:
current_command = send_command(8) # Up
elif index_y > center_y + key_size and index_y < center_y + 3 * key_size:
current_command = send_command(2) # Down
if index_y > center_y - key_size and index_y < center_y + key_size:
if index_x > center_x - 3 * key_size and index_x < center_x - key_size:
current_command = send_command(4) # Left
elif index_x > center_x + key_size and index_x < center_x + 3 * key_size:
current_command = send_command(6) # Right
mp_drawing.draw_landmarks(frame, hand_landmarks, mp_hands.HAND_CONNECTIONS)
cv2.imshow('MediaPipe Hands', frame)
if cv2.waitKey(5) & 0xFF == 113: # 'q' 키를 누르면 종료
break
cap.release()
cv2.destroyAllWindows()
동작
import cv2
import mediapipe as mp
import serial
import time
# 시리얼 포트 초기화
ser = serial.Serial('COM3', 9600, timeout=1)
time.sleep(2) # 아두이노 리셋 방지를 위한 지연
# MediaPipe 초기화
mp_hands = mp.solutions.hands
hands = mp_hands.Hands(
static_image_mode=False,
max_num_hands=1,
min_detection_confidence=0.5,
min_tracking_confidence=0.5)
mp_drawing = mp.solutions.drawing_utils
# 웹캠 캡처
cap = cv2.VideoCapture(0)
def send_command(command):
ser.write(f'{command}\n'.encode('utf-8'))
return f'Sending: {command}'
# 방향키 그리기 함수
def draw_direction_keys(img, center_x, center_y, size):
up_color = down_color = left_color = right_color = (200, 200, 200) # Gray color
center_color = (100, 200, 100) # Light green for center
# Draw keys
cv2.rectangle(img, (center_x - size, center_y - 3 * size), (center_x + size, center_y - size), up_color, -1) # Up
cv2.rectangle(img, (center_x - size, center_y + size), (center_x + size, center_y + 3 * size), down_color, -1) # Down
cv2.rectangle(img, (center_x - 3 * size, center_y - size), (center_x - size, center_y + size), left_color, -1) # Left
cv2.rectangle(img, (center_x + size, center_y - size), (center_x + 3 * size, center_y + size), right_color, -1) # Right
cv2.rectangle(img, (center_x - size, center_y - size), (center_x + size, center_y + size), center_color, -1) # Center
last_sent_time = time.time()
command_interval = 0.5 # command interval in seconds
while cap.isOpened():
ret, frame = cap.read()
if not ret:
break
height, width, _ = frame.shape
frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
frame.flags.writeable = False
results = hands.process(frame)
frame.flags.writeable = True
frame = cv2.cvtColor(frame, cv2.COLOR_RGB2BGR)
key_size = 50
center_x, center_y = width - 300, height // 2
draw_direction_keys(frame, center_x, center_y, key_size)
if results.multi_hand_landmarks:
for hand_landmarks in results.multi_hand_landmarks:
index_tip = hand_landmarks.landmark[mp_hands.HandLandmark.INDEX_FINGER_TIP]
index_x = int(index_tip.x * width)
index_y = int(index_tip.y * height)
current_command = None
# 방향키 위치 확인 및 커맨드 전송
if index_x > center_x - key_size and index_x < center_x + key_size:
if index_y > center_y - 3 * key_size and index_y < center_y - key_size:
current_command = 8 # Up
elif index_y > center_y + key_size and index_y < center_y + 3 * key_size:
current_command = 2 # Down
if index_y > center_y - key_size and index_y < center_y + key_size:
if index_x > center_x - 3 * key_size and index_x < center_x - key_size:
current_command = 4 # Left
elif index_x > center_x + key_size and index_x < center_x + 3 * key_size:
current_command = 6 # Right
if index_x > center_x - key_size and index_x < center_x + key_size and index_y > center_y - key_size and index_y < center_y + key_size:
current_command = 5 # Center
if current_command and time.time() - last_sent_time > command_interval:
send_command(current_command)
last_sent_time = time.time()
mp_drawing.draw_landmarks(frame, hand_landmarks, mp_hands.HAND_CONNECTIONS)
cv2.imshow('MediaPipe Hands', frame)
if cv2.waitKey(5) & 0xFF == 113: # 'q' 키를 누르면 종료
break
cap.release()
cv2.destroyAllWindows()
동작
import cv2
import mediapipe as mp
import serial
import time
# 시리얼 포트 초기화
ser = serial.Serial('COM12', 9600, timeout=1)
time.sleep(2) # 아두이노 리셋 방지를 위한 지연
# MediaPipe 초기화
mp_hands = mp.solutions.hands
hands = mp_hands.Hands(
static_image_mode=False,
max_num_hands=1,
min_detection_confidence=0.5,
min_tracking_confidence=0.5)
mp_drawing = mp.solutions.drawing_utils
# 웹캠 캡처
cap = cv2.VideoCapture(0)
def send_command(command):
ser.write(f'{command}\n'.encode('utf-8'))
return f'Sending: {command}'
# 방향키 그리기 함수
def draw_direction_keys(img, size):
height, width, _ = img.shape
up_color = down_color = left_color = right_color = (200, 200, 200) # Gray color
center_color = (100, 200, 100) # Light green for center
# 각 방향의 중앙 끝에 사각형 그리기
cv2.rectangle(img, (width // 2 - size, 0), (width // 2 + size, size * 2), up_color, -1) # Up
cv2.rectangle(img, (width // 2 - size, height - size * 2), (width // 2 + size, height), down_color, -1) # Down
cv2.rectangle(img, (0, height // 2 - size), (size * 2, height // 2 + size), left_color, -1) # Left
cv2.rectangle(img, (width - size * 2, height // 2 - size), (width, height // 2 + size), right_color, -1) # Right
cv2.rectangle(img, (width // 2 - size, height // 2 - size), (width // 2 + size, height // 2 + size), center_color, -1) # Center
last_sent_time = time.time()
command_interval = 0.5 # command interval in seconds
while cap.isOpened():
ret, frame = cap.read()
if not ret:
break
height, width, _ = frame.shape
frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
frame.flags.writeable = False
results = hands.process(frame)
frame.flags.writeable = True
frame = cv2.cvtColor(frame, cv2.COLOR_RGB2BGR)
key_size = 50
draw_direction_keys(frame, key_size)
if results.multi_hand_landmarks:
for hand_landmarks in results.multi_hand_landmarks:
index_tip = hand_landmarks.landmark[mp_hands.HandLandmark.INDEX_FINGER_TIP]
index_x = int(index_tip.x * width)
index_y = int(index_tip.y * height)
current_command = None
# 방향키 위치 확인 및 커맨드 전송
if width // 2 - key_size < index_x < width // 2 + key_size:
if index_y < key_size * 2:
current_command = 8 # Up
elif index_y > height - key_size * 2:
current_command = 2 # Down
if height // 2 - key_size < index_y < height // 2 + key_size:
if index_x < key_size * 2:
current_command = 4 # Left
elif index_x > width - key_size * 2:
current_command = 6 # Right
if width // 2 - key_size < index_x < width // 2 + key_size and height // 2 - key_size < index_y < height // 2 + key_size:
current_command = 5 # Center
if current_command and time.time() - last_sent_time > command_interval:
send_command(current_command)
last_sent_time = time.time()
mp_drawing.draw_landmarks(frame, hand_landmarks, mp_hands.HAND_CONNECTIONS)
cv2.imshow('MediaPipe Hands', frame)
if cv2.waitKey(5) & 0xFF == 113: # 'q' 키를 누르면 종료
break
cap.release()
cv2.destroyAllWindows()
이런식으로 원한는 형태로 동작의 디테일을 잡아 줄 수 있다.
최종 동작 영상이다.
미디어파이프에는 다양한 AI 모델이 있고, 아두이노에 다양한 전자부품을 물리면 다양한 작품들을 생성 할 수 있다.
끝.
'Python > 인공지능' 카테고리의 다른 글
마이크로비트 와 티처블 머신 AI 연동 하기 (0) | 2024.06.25 |
---|---|
인공지능과 메이커 프로젝트 교과서 다운로드 (0) | 2023.07.03 |
인공지능과 미래사회 교육자료 모음. (2) | 2022.04.14 |
티쳐블 머신과 웹페이지, TTS 연동 실습 자료 (5) | 2021.06.07 |
인공지능 use case (1) | 2020.12.09 |