본문 바로가기

PY(Python Image Processing)

hand detect & handling cube image - opencv python mediapipe pygame PyOpenGL PyOpenGL_accelerate numpy

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)

# 랜덤한 내부 돌덩이 형태 생성
def generate_inner_shape():
    """큐브 내부의 울퉁불퉁한 도형을 위한 랜덤한 정점 생성"""
    return [[random.uniform(-0.3, 0.3) for _ in range(3)] for _ in range(12)]  # 12개의 랜덤 점 생성

# 내부 도형 정점 데이터 생성
inner_shape_vertices = generate_inner_shape()

def draw_cube(scale_factor, x_offset, y_offset, angle_x, angle_y):
    """모서리 색상 변경 + 반투명 큐브 + 내부 복잡한 도형 추가"""

    # 서서히 밝아졌다 어두워지는 네온 효과 (sin 기반)
    time_factor = pygame.time.get_ticks() / 1000.0  
    glow_intensity = 0.3 + 0.7 * (math.sin(time_factor * 2) ** 2)  

    glPushMatrix()
    glTranslatef(x_offset, y_offset, -3.0)
    glScalef(scale_factor, scale_factor, scale_factor)
    glRotatef(angle_x, 1, 0, 0)
    glRotatef(angle_y, 0, 1, 0)

    # 기존 재질 속성 저장 (배경 영향 없음)
    glPushAttrib(GL_LIGHTING_BIT | GL_CURRENT_BIT)

    # 내부 발광 설정 (네온 효과)
    glow_color = [0.0, glow_intensity, 1.5 * glow_intensity, 1.0]  
    glMaterialfv(GL_FRONT_AND_BACK, GL_EMISSION, glow_color)

    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.3)  # 투명한 네온 블루 색상 적용
        for vertex in face:
            glVertex3fv(vertices[vertex])
    glEnd()

    # 큐브 모서리 강조 (선 색상 변경)
    glLineWidth(2.5)
    glColor3f(1.0, 1.0, 1.0)  # 흰색 테두리 적용
    glBegin(GL_LINES)
    for face in faces:
        for i in range(4):
            glVertex3fv(vertices[face[i]])
            glVertex3fv(vertices[face[(i + 1) % 4]])
    glEnd()

    # 내부 복잡한 도형 그리기 (울퉁불퉁한 돌덩이 느낌)
    glBegin(GL_TRIANGLES)
    for i in range(len(inner_shape_vertices)):
        glColor4f(0.0, 1.5 * glow_intensity, 1.0, 0.5)  # 내부 도형은 더 밝은 색상 적용
        glVertex3fv(inner_shape_vertices[i])
    glEnd()

    # 원래 재질 속성 복원 (배경 영향 없음)
    glPopAttrib()

    glDisable(GL_BLEND)
    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))
            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