728x90
import cv2
import mediapipe as mp
import numpy as np
import pygame
from pygame.locals import *
from OpenGL.GL import *
from OpenGL.GLU import *
import math
import random
# MediaPipe 손 검출 초기화
mp_hands = mp.solutions.hands
hands = mp_hands.Hands(static_image_mode=False, max_num_hands=1, min_detection_confidence=0.5)
# OpenGL 텍스처 ID 저장
texture_id = None
# 3D 큐브 데이터
vertices = [
[-0.5, -0.5, -0.5], [0.5, -0.5, -0.5], [0.5, 0.5, -0.5], [-0.5, 0.5, -0.5],
[-0.5, -0.5, 0.5], [0.5, -0.5, 0.5], [0.5, 0.5, 0.5], [-0.5, 0.5, 0.5]
]
faces = [
(0, 1, 2, 3), (4, 5, 6, 7),
(0, 1, 5, 4), (2, 3, 7, 6),
(0, 3, 7, 4), (1, 2, 6, 5)
]
colors = [(1, 0, 0), (0, 1, 0), (0, 0, 1), (1, 1, 0), (1, 0, 1), (0, 1, 1)]
def lerp(a, b, t):
"""선형 보간 함수 (부드러운 크기 변화)"""
return a + (b - a) * t
def init_texture(width, height):
"""OpenGL 텍스처 초기화 (웹캠 배경)"""
global texture_id
texture_id = glGenTextures(1)
glBindTexture(GL_TEXTURE_2D, texture_id)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, None)
def update_texture(frame):
"""웹캠 프레임을 OpenGL 텍스처로 업데이트"""
if frame is None or frame.shape[0] == 0 or frame.shape[1] == 0:
return # 프레임이 비어있으면 업데이트 안함
glBindTexture(GL_TEXTURE_2D, texture_id)
frame = cv2.flip(frame, 0) # OpenGL 좌표계에 맞추기 위해 상하 반전
frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) # BGR → RGB 변환
frame = cv2.resize(frame, (640, 480)) # OpenGL과 맞추기 위해 크기 조정
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 640, 480, GL_RGB, GL_UNSIGNED_BYTE, frame)
def detect_hand(frame):
"""손 검출 및 손가락 간 거리 계산"""
if frame is None:
return None # 프레임이 없을 경우
frame_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
results = hands.process(frame_rgb)
if results.multi_hand_landmarks:
for hand_landmarks in results.multi_hand_landmarks:
# 손바닥 중심점
x = int(hand_landmarks.landmark[9].x * frame.shape[1])
y = int(hand_landmarks.landmark[9].y * frame.shape[0])
# 손가락 간 거리 계산 (손을 쥐었는지 여부)
distances = [abs(hand_landmarks.landmark[i].y - hand_landmarks.landmark[9].y) for i in [8, 12, 16, 20]]
avg_distance = sum(distances) / len(distances)
# 주먹 감지 (손가락이 손바닥에 가까운 경우)
is_fist = avg_distance < 0.07
return x, y, avg_distance, is_fist
return None
def draw_background():
"""웹캠 영상을 OpenGL 배경으로 렌더링"""
glEnable(GL_TEXTURE_2D)
glBindTexture(GL_TEXTURE_2D, texture_id)
glBegin(GL_QUADS)
glTexCoord2f(0, 0); glVertex3f(-3, -2, -5)
glTexCoord2f(1, 0); glVertex3f(3, -2, -5)
glTexCoord2f(1, 1); glVertex3f(3, 2, -5)
glTexCoord2f(0, 1); glVertex3f(-3, 2, -5)
glEnd()
glDisable(GL_TEXTURE_2D)
# 📌 정이십면체(20면체) 정점 생성
PHI = (1 + np.sqrt(5)) / 2 # 황금비율
icosahedron_vertices = [
(-1, PHI, 0), (1, PHI, 0), (-1, -PHI, 0), (1, -PHI, 0),
(0, -1, PHI), (0, 1, PHI), (0, -1, -PHI), (0, 1, -PHI),
(PHI, 0, -1), (PHI, 0, 1), (-PHI, 0, -1), (-PHI, 0, 1)
]
icosahedron_vertices = np.array(icosahedron_vertices) / np.linalg.norm(icosahedron_vertices[0]) # 정규화
# 20개의 삼각형 면 정의
icosahedron_faces = [
(0, 11, 5), (0, 5, 1), (0, 1, 7), (0, 7, 10), (0, 10, 11),
(1, 5, 9), (5, 11, 4), (11, 10, 2), (10, 7, 6), (7, 1, 8),
(3, 9, 4), (3, 4, 2), (3, 2, 6), (3, 6, 8), (3, 8, 9),
(4, 9, 5), (2, 4, 11), (6, 2, 10), (8, 6, 7), (9, 8, 1)
]
def draw_cube(scale_factor, x_offset, y_offset, angle_x, angle_y):
"""💡 외부 투명 큐브 + 내부 정이십면체(20면체) + 네온 효과"""
# 🌀 **네온 광채 효과 (시간에 따라 서서히 밝아졌다가 어두워짐)**
time_factor = pygame.time.get_ticks() / 1000.0
glow_intensity = 0.3 + 0.7 * (math.sin(time_factor * 2) ** 2) # 외부 밝기 조절
inner_glow_intensity = 1.0 - glow_intensity # 내부 밝기를 외부와 반대로 설정
# 🔥 **OpenGL 변환 시작 (행렬 스택 푸시)**
glPushMatrix()
glTranslatef(x_offset, y_offset, -3.0) # 큐브 위치 조정
glScalef(scale_factor, scale_factor, scale_factor) # 크기 조정
glRotatef(angle_x, 1, 0, 0) # X축 회전
glRotatef(angle_y, 0, 1, 0) # Y축 회전
# 🌟 **깊이 버퍼를 변경하여 내부 도형이 더 잘 보이도록 설정**
glDepthMask(GL_FALSE) # 깊이 버퍼 업데이트 방지 (배경 영향 최소화)
glEnable(GL_BLEND) # 블렌딩 활성화 (투명도 조절 가능)
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) # 투명도 설정
# **🔷 외부 큐브 (반투명 네온 블루)**
glBegin(GL_QUADS)
for face in faces:
glColor4f(0.0, glow_intensity, 1.0, 0.2) # 네온 블루 색상 + 반투명 효과
for vertex in face:
glVertex3fv(vertices[vertex])
glEnd()
# **🌟 큐브 모서리 강조 (얇은 흰색 선)**
glEnable(GL_LINE_SMOOTH) # 부드러운 선 설정
glLineWidth(2.0) # 선의 두께 조정
glColor4f(1.0, 1.0, 1.0, 0.3) # 흰색 반투명 모서리
glBegin(GL_LINES)
for face in faces:
for i in range(4):
glVertex3fv(vertices[face[i]])
glVertex3fv(vertices[face[(i + 1) % 4]])
glEnd()
# 🌟 **깊이 버퍼 다시 활성화 (내부 도형이 정상적으로 보이게)**
glDepthMask(GL_TRUE)
# 🔺 **내부 정이십면체 (20면체) 추가**
glPushMatrix() # 내부 도형을 위한 추가 변환 시작
glScalef(0.6, 0.6, 0.6) # 내부 도형 크기를 외부 큐브의 60%로 축소
# 🌀 **조명 및 광원 설정 (난반사 효과 적용)**
glEnable(GL_LIGHTING) # 조명 활성화
glEnable(GL_LIGHT0) # 기본 광원 활성화
# 💡 **광원 속성 설정 (난반사 효과)**
light_diffuse = [0.8, 0.8, 0.8, 1.0] # 확산광 (난반사 효과 증가)
light_specular = [0.3, 0.3, 0.3, 1.0] # 반사광 (광택 효과 감소)
light_position = [0.0, 0.0, 2.0, 1.0] # 광원 위치 설정
glLightfv(GL_LIGHT0, GL_DIFFUSE, light_diffuse)
glLightfv(GL_LIGHT0, GL_SPECULAR, light_specular)
glLightfv(GL_LIGHT0, GL_POSITION, light_position)
# ✨ **정이십면체 표면 재질(Material) 설정**
glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, [0.1, 0.1, 0.1, 1.0]) # 주변광 (어두운 톤 추가)
glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, [0.3, 0.5, 1.0, 0.8]) # 확산광 (빛이 퍼지는 효과 증가)
glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, [0.5, 0.5, 0.5, 1.0]) # 반사광 (너무 밝지 않게 설정)
glMaterialfv(GL_FRONT_AND_BACK, GL_SHININESS, [10]) # 🔥 난반사 강도 (5 → 20으로 조정하여 난반사 효과 증가)
# **🌟 내부 정이십면체(20면체) 그리기**
glBegin(GL_TRIANGLES)
# 🌀 내부 색상을 시간에 따라 변화하는 네온 효과 적용
time_factor = pygame.time.get_ticks() / 1000.0
r = (math.sin(time_factor * 3) + 1) / 3 # 붉은 계열
g = (math.sin(time_factor * 3 + 2) + 1) / 3 # 녹색 계열
b = (math.sin(time_factor * 3 + 4) + 1) / 2 # 푸른 계열
for face in icosahedron_faces:
glColor4f(r, g, b, 0.4) # 🔥 투명도를 0.4로 낮춰 내부가 더 잘 보이게 설정
for vertex in face:
glVertex3fv(icosahedron_vertices[vertex])
glEnd()
# **🌟 내부 다각형 모서리 강조 (반투명 흰색 선)**
glLineWidth(2.0) # 선 두께 감소하여 자연스럽게
glColor4f(1.0, 1.0, 1.0, 0.5) # 🔥 모서리를 반투명하게 설정하여 빛 반사 효과 증가
glBegin(GL_LINES)
for face in icosahedron_faces:
for i in range(3):
glVertex3fv(icosahedron_vertices[face[i]])
glVertex3fv(icosahedron_vertices[face[(i + 1) % 3]])
glEnd()
# 🚀 **내부 정이십면체 변환 종료 (스택 정리)**
glDisable(GL_LIGHTING) # 조명 해제
# 🔥 2️⃣ 내부 도형 끝나면 POP
glPopMatrix() # 내부 정이십면체 변환 종료
# 🔥 1️⃣ 큐브 전체 끝나면 POP (이거 없으면 스택 오버플로우 발생!)
# 🚀 **전체 큐브 변환 종료 (스택 정리)**
glPopMatrix() # 큐브 변환 종료 #
def main():
cap = cv2.VideoCapture(0)
if not cap.isOpened():
print("🚨 웹캠을 열 수 없습니다!")
return
pygame.init()
display = (640, 480)
pygame.display.set_mode(display, DOUBLEBUF | OPENGL)
glEnable(GL_DEPTH_TEST)
glMatrixMode(GL_PROJECTION)
glLoadIdentity()
gluPerspective(45, (640 / 480), 0.1, 50.0)
glMatrixMode(GL_MODELVIEW)
init_texture(640, 480)
scale_factor = 0.3
x_offset, y_offset = 0.0, 0.0
angle_x, angle_y = 0, 0
running = True
while running:
ret, frame = cap.read()
if not ret or frame is None:
pygame.time.wait(50) # 손이 감지되지 않을 때 부하 감소
continue # 프레임이 없으면 루프 유지
update_texture(frame)
hand = detect_hand(frame)
if hand:
center_x, center_y, avg_distance, is_fist = hand
x_offset = (center_x - 640 / 2) / (640 / 2)
y_offset = -(center_y - 480 / 2) / (480 / 2)
#target_scale = 0.1 if is_fist else max(0.2, min(0.5, avg_distance * 8))
screen_max_size = 2.0 # 🔥 화면 크기에 맞춰 자동 조정
target_scale = 0.1 if is_fist else max(0.2, min(screen_max_size, avg_distance * 8))
scale_factor = lerp(scale_factor, target_scale, 0.2)
else:
pygame.time.wait(50) # 손이 없으면 루프 속도 낮춤
angle_x += y_offset * 30
angle_y += x_offset * 30
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
glLoadIdentity()
draw_background()
draw_cube(scale_factor, x_offset, y_offset, angle_x, angle_y)
pygame.display.flip()
for event in pygame.event.get():
if event.type == pygame.QUIT or (event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE):
running = False
cap.release()
pygame.quit()
if __name__ == "__main__":
main()
📌 필수 패키지 목록
패키지역할설치 명령
| opencv-python | OpenCV: 카메라 영상 처리 및 실시간 스트리밍 | pip install opencv-python |
| mediapipe | MediaPipe: 손 추적 및 랜드마크 감지 | pip install mediapipe |
| pygame | Pygame: OpenGL과 함께 창 생성 및 이벤트 핸들링 | pip install pygame |
| PyOpenGL | OpenGL을 통한 3D 그래픽 렌더링 | pip install PyOpenGL |
| PyOpenGL_accelerate | OpenGL 속도 가속화 (성능 향상) | pip install PyOpenGL_accelerate |
| numpy | 벡터 연산 및 좌표 데이터 처리 | pip install numpy |
| math | 기본 수학 연산 (Python 내장) | 설치 불필요 |
| random | 랜덤 값 생성 (Python 내장) | 설치 불필요 |

728x90