DamoFD 모델 아키텍처 및 핵심 특성
DamoFD는 제한된 컴퓨팅 자원 환경에서도 높은 정밀도를 유지하며 실시간 처리가 가능하도록 설계된 경량 얼굴 감지 아키텍처입니다. 전체 모델의 크기는 약 0.5GB에 불과하지만, 복잡한 배경과 다양한 조명 조건에서도 강건한 성능을 발휘합니다. 또한, 얼굴의 바운딩 박스뿐만 아니라 양눈, 코끝, 양쪽 입꼬리 등 5개의 주요 얼굴 랜드마크(Facial Landmarks)를 동시에 추정할 수 있어 후속 인식 파이프라인에 직접 연동하기 용이합니다.
실행 환경 구성 및 의존성 설치
모델을 안정적으로 구동하기 위해 격리된 가상 환경을 구성하는 것이 권장됩니다. 아래 명령어를 통해 작업 디렉터리를 설정하고 필요한 라이브러리를 활성화합니다.
# 작업 공간 디렉터리 생성 및 이동
mkdir -p ~/workspace/face_detection && cd ~/workspace/face_detection
# DamoFD 소스 코드 복제
git clone https://github.com/example/DamoFD.git .
# Conda 가상 환경 생성 및 활성화
conda create -n fd_env python=3.9 -y
conda activate fd_env
# 필수 의존성 패키지 설치
pip install torch==1.11.0 torchvision==0.12.0 --extra-index-url https://download.pytorch.org/whl/cu113
pip install modelscope==1.6.1 opencv-python numpy
추론 파이프라인 구현
1. 스크립트 기반 일괄 처리 (Batch Processing)
서버 환경이나 자동화 파이프라인에서는 독립적인 Python 스크립트를 통한 추론이 효율적입니다. 아래 코드는 입력 이미지를 로드하고 감지 결과를 바운딩 박스와 함께 저장하는 로직을 보여줍니다.
import cv2
import numpy as np
from modelscope.pipelines import pipeline
from modelscope.utils.constant import Tasks
def initialize_detector():
"""DamoFD 추론 파이프라인 초기화"""
return pipeline(Tasks.face_detection, model='damo/cv_ddsar_face-detection')
def process_image(detector, source_path, output_path, confidence_threshold=0.6):
"""단일 이미지에 대한 얼굴 감지 및 시각화 수행"""
frame = cv2.imread(source_path)
if frame is None:
raise ValueError(f"이미지를 로드할 수 없습니다: {source_path}")
# 모델 추론 실행
inference_result = detector(frame)
# 결과 파싱 및 시각화
detected_faces = inference_result.get('scores', [])
boxes = inference_result.get('boxes', [])
landmarks = inference_result.get('keypoints', [])
for idx, score in enumerate(detected_faces):
if score < confidence_threshold:
continue
# 바운딩 박스 그리기
x1, y1, x2, y2 = map(int, boxes[idx])
cv2.rectangle(frame, (x1, y1), (x2, y2), (0, 255, 0), 2)
# 5점 랜드마크 그리기
if landmarks is not None and len(landmarks) > idx:
for pt in landmarks[idx]:
cv2.circle(frame, (int(pt[0]), int(pt[1])), 3, (0, 0, 255), -1)
cv2.imwrite(output_path, frame)
return len([s for s in detected_faces if s >= confidence_threshold])
if __name__ == "__main__":
model_pipeline = initialize_detector()
target_img = "sample_input.jpg"
result_img = "annotated_output.jpg"
face_count = process_image(model_pipeline, target_img, result_img, confidence_threshold=0.5)
print(f"총 {face_count}개의 얼굴이 감지되었습니다.")
2. 인터랙티브 노트북 환경
Jupyter Notebook 환경에서는 셀 단위로 코드를 실행하며 중간 결과를 시각적으로 확인하기 적합합니다. 노트북 커널이 반드시 fd_env로 설정되어 있는지 확인한 후, 메모리 상의 이미지 배열을 직접 전달하여 추론을 수행할 수 있습니다.
import matplotlib.pyplot as plt
from PIL import Image
# PIL을 활용한 이미지 로드
input_image = Image.open("test_image.png")
# 파이프라인 실행
outputs = model_pipeline(input_image)
# Matplotlib을 통한 결과 시각화
plt.figure(figsize=(10, 8))
plt.imshow(outputs['drawed_image'])
plt.axis('off')
plt.title("Face Detection Results")
plt.show()
하이퍼파라미터 튜닝 및 성능 최적화
실제 서비스 환경에 맞게 모델의 민감도와 처리 속도를 조절할 수 있습니다.
- 신뢰도 임계값 (Confidence Threshold): 기본값 0.5에서 값을 낮추면(예: 0.3) 오탐지(False Positive)가 증가할 수 있으나 가려지거나 흐린 얼굴의 탐지율(Recall)이 향상됩니다. 반대로 0.7 이상으로 설정하면 정밀도(Precision)가 우선시됩니다.
- 입력 해상도 다운스케일링: 실시간 비디오 스트림 처리 시, 원본 프레임을 모델에 전달하기 전에 너비를 640px 또는 320px로 리사이즈하면 추론 지연 시간(Latency)을 획기적으로 단축할 수 있습니다.
- 메모리 관리: 대용량 이미지 배치를 처리할 때는
torch.no_grad()컨텍스트 매니저를 사용하여 그래디언트 계산을 비활성화하고 VRAM 사용량을 줄여야 합니다.
실시간 애플리케이션 통합 아키텍처
RESTful API 서버 구축 (FastAPI)
마이크로서비스 아키텍처에 통합하기 위해 FastAPI를 사용한 비동기 엔드포인트를 구현합니다.
from fastapi import FastAPI, UploadFile, File
from fastapi.responses import JSONResponse
import cv2
import numpy as np
app = FastAPI(title="Face Detection API")
detector = initialize_detector()
@app.post("/api/v1/detect")
async def analyze_image(file: UploadFile = File(...)):
# 업로드된 바이트 스트림을 OpenCV 형식으로 디코딩
contents = await file.read()
np_array = np.frombuffer(contents, np.uint8)
image_matrix = cv2.imdecode(np_array, cv2.IMREAD_COLOR)
if image_matrix is None:
return JSONResponse(status_code=400, content={"error": "Invalid image format"})
# 추론 및 결과 포맷팅
raw_result = detector(image_matrix)
formatted_faces = []
for score, box in zip(raw_result['scores'], raw_result['boxes']):
if score >= 0.5:
formatted_faces.append({
"confidence": float(score),
"bounding_box": {
"x_min": float(box[0]), "y_min": float(box[1]),
"x_max": float(box[2]), "y_max": float(box[3])
}
})
return {"total_faces": len(formatted_faces), "detections": formatted_faces}
웹캠 스트림 실시간 처리
로컬 디바이스의 카메라 입력을 받아 실시간으로 오버레이를 렌더링하는 루프 구조입니다.
import time
def run_realtime_inference():
video_capture = cv2.VideoCapture(0)
video_capture.set(cv2.CAP_PROP_FRAME_WIDTH, 640)
video_capture.set(cv2.CAP_PROP_FRAME_HEIGHT, 480)
while video_capture.isOpened():
is_success, current_frame = video_capture.read()
if not is_success:
break
# FPS 계산을 위한 타임스탬프 기록
tick = time.perf_counter()
# 프레임 추론
predictions = detector(current_frame)
# 처리 시간 계산 및 FPS 표시
elapsed_ms = (time.perf_counter() - tick) * 1000
cv2.putText(current_frame, f"Latency: {elapsed_ms:.1f}ms", (10, 30),
cv2.FONT_HERSHEY_SIMPLEX, 0.7, (255, 255, 0), 2)
# 감지된 박스 렌더링 (시각화 로직 생략)
# render_boxes(current_frame, predictions)
cv2.imshow("Live Feed", current_frame)
if cv2.waitKey(1) & 0xFF == 27: # ESC 키로 종료
break
video_capture.release()
cv2.destroyAllWindows()
if __name__ == "__main__":
run_realtime_inference()
디버깅 및 예외 처리 전략
프로덕션 환경에서 발생할 수 있는 주요 예외 상황을 처리하기 위한 방어적 코딩이 필요합니다.
- OOM (Out of Memory) 오류: 고해상도 이미지 처리 시 발생하며, 입력 텐서의 크기를 제한하거나 배치 사이즈를 1로 고정하여 해결합니다.
- 모델 가중치 로드 실패: 네트워크 단절이나 로컬 캐시 손상으로 인해 발생합니다.
modelscope snapshot_download명령어를 통해 사전에 가중치를 로컬에 다운로드하고 오프라인 모드로 실행하는 것이 안정적입니다. - 추론 속도 저하: CPU 환경에서 OpenMP 스레드 충돌이 발생할 수 있으므로,
export OMP_NUM_THREADS=1환경 변수를 설정하여 스레드 오버헤드를 방지합니다.