IT개발/IT공부

[IT공부] 노트북 웹 캠과 사람 얼굴 과의 거리 계산 측정(Python, YOLOv8)

Thompson 2025. 5. 20. 13:04
728x90
반응형
사용 언어 파이썬(Python)
사용 모델 사용자 학습 모델(yolov8)
사용 환경 VSCODE

 

노트북과 웹 캠과 사람 얼굴 과의 거리를 측정하기 위해서는 저는 어릴 때 배운 "비례식"개념을 사용하기로 했습니다. 비례식 개념은 다음과 같이 ex) 2:3 = 4:6 와 같이 비율이 같은 두 개의 비를 등호(=)로 붙여 표현하는 개념입니다.

 

그러하여 다음과 같은 식을 설정할 수 있습니다.

실제 얼굴 너비 / 화면에 보이는 얼굴 너비 = 실제 거리 / 초점 거리

 

그러면 식을 변형 시키면, 밑에와 같이 실제 거리를 구할 수 있습니다.

사용자 학습 모델로 진행할 거고 완벽히 일치하는 실제 거리이지는 않지만 어느정도 일치할 수 있음을 확인할 수 있습니다.

 

(실제 거리 : 줄자로 대략적인 거리를 측정)

(화면에 보이는 얼굴 너비 : bounding box의 x2 - x1을 하여 측정)

  • x1, y1 : 사각형 왼쪽 위 꼭짓점
  • x2, y2 : 사각형 오른쪽 아래 꼭짓점

 

실제 거리 = (실제 얼굴 너비 * 초점 거리) / 화면에 보이는 얼굴 너비
초점 거리 = (화면에 보이는 얼굴 너비 * 실제 거리) / 실제 얼굴 너비

 

지금 부터 "초점 거리"를 한 번 검증 해보겠습니다.

 

1단계 : 사람들의 평균적인 얼굴 너비는 / 남자 : 15.7cm, 여자 14.7cm으로 평균 15cm라고 임의로 값을 지정을 하고,

2단계 : 이는 제가 학습 시킨 모델이 측정한 값으로 438 - 276 = 162로 사진 속 얼굴 너비(얼굴 너비 픽셀)을 구할 수 있습니다.

3단계 : 줄 자로 카메라 렌즈와 저의 인중을 사이로 60CM를 고정(중요!)하고, 이를 테스트하면 초점 거리를 측정이 가능합니다.

4단계 : (162 * 60) / 15 = 648의 값을 나옴을 확인할 수 있고 이는 완벽한 값은 아니지만 대략 620 ~ 750 사이의 값을 나옴을 확인할 수 있습니다.

 

밑에 코드는 초점 거리를 증명하는 코드
from ultralytics import YOLO
import cv2

# 모델 로드
model = YOLO("@@@.pt")

# 실제 얼굴 너비(cm)와 측정용 거리(cm)
REAL_FACE_WIDTH = 15.0
KNOWN_DISTANCE = 60.0  # 측정용 기준 거리

# 초점 거리 저장용 변수
focal_length_computed = None

# 얼굴 너비 추출 함수
def get_face_width_px(results):
    for result in results:
        boxes = result.boxes
        if boxes is not None:
            for box in boxes:
                x1, y1, x2, y2 = map(int, box.xyxy[0])
                return x1, y1, x2, y2, x2 - x1
    return None

# 거리 계산
def calculate_distance(focal_length, real_width, width_px):
    return (real_width * focal_length) / width_px

# 웹캠 시작
cap = cv2.VideoCapture(0)

while True:
    ret, frame = cap.read()
    if not ret:
        break

    results = model(frame)
    face_info = get_face_width_px(results)

    if face_info:
        x1, y1, x2, y2, width_px = face_info

        # 초점 거리 계산 (기준 거리 100cm일 때만 1회 계산)
        if not focal_length_computed:
            focal_length_computed = (width_px * KNOWN_DISTANCE) / REAL_FACE_WIDTH

        # 예측 거리 계산
        predicted_distance = calculate_distance(focal_length_computed, REAL_FACE_WIDTH, width_px)

        # 시각화
        cv2.rectangle(frame, (x1, y1), (x2, y2), (0, 255, 0), 2)
        # 화면에 보이는 얼굴 픽셀
        cv2.putText(frame, f"Width(px): {width_px}", (x1, y2 + 20),
                    cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 255), 2)
        # 예상되는 초점 거리
        cv2.putText(frame, f"FocalLength: {focal_length_computed:.2f}px", (x1, y2 + 40),
                    cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 200, 0), 2)
        # 예측되는 거리
        cv2.putText(frame, f"Predicted Distance: {predicted_distance:.2f} cm", (x1, y2 + 60),
                    cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 0), 2)

    cv2.imshow("Distance Estimation", frame)
    if cv2.waitKey(1) & 0xFF == ord('q'):
        break

cap.release()
cv2.destroyAllWindows()