2025.02.17 - [๊ฐ๋ฐ Code/์ธ๊ณต์ง๋ฅ A.I.] - [Python][AI] OpenCV YuNet์ ํ์ฉํ ์ผ๊ตด ํ์ง
1. ๋์์ ์ผ๊ตด ๋ชจ์์ดํฌ ๊ฐ์
์ด์ ๊ธ์์๋ ์ ์ ์ธ ์ด๋ฏธ์ง ๋๋ ๋๋ ํ ๋ฆฌ์์ ์ผ๊ตด์ ํ์งํ๊ณ ๋ชจ์์ดํฌ๋ฅผ ์ ์ฉํ๋ ๋ฐฉ๋ฒ์ ๋ค๋ฃจ์๋ค. ์ด๋ฒ ๊ธ์์๋ ๋์์์์ ์ผ๊ตด์ ์๋์ผ๋ก ํ์งํ๊ณ ๋ชจ์์ดํฌ๋ฅผ ์ ์ฉํ๋ ๋ฐฉ๋ฒ์ ๋ค๋ฃฌ๋ค.
OpenCV์ YuNet ์ผ๊ตด ํ์ง ๋ชจ๋ธ์ ํ์ฉํ์ฌ ์ค์๊ฐ์ผ๋ก ์ผ๊ตด์ ๊ฐ์งํ๊ณ , ํด๋น ์์ญ์ ๋ชจ์์ดํฌ ์ฒ๋ฆฌํ ํ ์๋ก์ด ๋์์์ ์ ์ฅํ๋ ๋ฐฉ์์ผ๋ก ์งํํ๋ค.
2. ์คํ ํ๊ฒฝ ์ค๋น
(1) OpenCV ์ค์น
YuNet์ ํ์ฉํ๊ธฐ ์ํด opencv-python ์ค์น
pip install opencv-python
(2) ๋์์ ๋ค์ด๋ก๋
๋งํฌ : https://pixabay.com/ko/videos/ํ์-์ผ๋ณธ-์ผํ๋ค-์ฌ๋๋ค-34685/
3. YuNet ์ผ๊ตด ํ์ง ๋ชจ๋ธ ๋ค์ด๋ก๋
YuNet ๋ชจ๋ธ์ด ์์ ๊ฒฝ์ฐ ์๋์ผ๋ก ๋ค์ด๋ก๋ํ๋๋ก ์ค์ ํจ.
import os
import requests
# YuNet ๋ชจ๋ธ ํ์ผ ์ค์
model_filename = "face_detection_yunet_2023mar.onnx"
# ๋ชจ๋ธ ํ์ผ์ด ์์ ๊ฒฝ์ฐ ๋ค์ด๋ก๋
if not os.path.exists(model_filename):
url = "https://github.com/opencv/opencv_zoo/raw/main/models/face_detection_yunet/face_detection_yunet_2023mar.onnx"
print("YuNet ๋ชจ๋ธ ํ์ผ์ด ์์ต๋๋ค. ๋ค์ด๋ก๋ ์งํ...")
response = requests.get(url, allow_redirects=True)
if response.status_code == 200:
with open(model_filename, "wb") as f:
f.write(response.content)
print("๋ชจ๋ธ ๋ค์ด๋ก๋ ์๋ฃ.")
else:
raise RuntimeError(f"๋ชจ๋ธ ๋ค์ด๋ก๋ ์คํจ: HTTP ์ํ ์ฝ๋ {response.status_code}")
4. ์ผ๊ตด ํ์ง ๋ฐ ๋ชจ์์ดํฌ ์ฒ๋ฆฌ ํจ์
YuNet์ ์ฌ์ฉํ์ฌ ๋์์ ํ๋ ์์์ ์ผ๊ตด์ ํ์งํ๊ณ , ๋ชจ์์ดํฌ ํจ๊ณผ๋ฅผ ์ ์ฉํ๋ ํจ์๋ฅผ ๊ตฌํํจ.
import cv2 as cv
import numpy as np
# ์ผ๊ตด ๊ฒ์ถ๊ธฐ ์์ฑ ํจ์
def create_face_detector(frame_width, frame_height):
detector = cv.FaceDetectorYN.create(
model=model_filename,
config="",
input_size=(frame_width, frame_height), # ์
๋ ฅ ํฌ๊ธฐ ์ค์
score_threshold=0.9,
nms_threshold=0.3,
top_k=5000,
backend_id=cv.dnn.DNN_BACKEND_OPENCV,
target_id=cv.dnn.DNN_TARGET_CPU
)
return detector
# ๋ชจ์์ดํฌ ์ฒ๋ฆฌ ํจ์
def apply_mosaic(img, bbox, scale=0.1):
x, y, w_box, h_box = bbox[:4].astype(int)
x = max(0, x)
y = max(0, y)
w_box = min(w_box, img.shape[1] - x)
h_box = min(h_box, img.shape[0] - y)
if w_box <= 0 or h_box <= 0:
return img
face_region = img[y:y+h_box, x:x+w_box]
# downscale ํ upscaleํ์ฌ ๋ชจ์์ดํฌ ํจ๊ณผ ์ ์ฉ
small = cv.resize(face_region, None, fx=scale, fy=scale, interpolation=cv.INTER_LINEAR)
mosaic = cv.resize(small, (w_box, h_box), interpolation=cv.INTER_NEAREST)
img[y:y+h_box, x:x+w_box] = mosaic
return img
5. ๋์์ ์ผ๊ตด ๋ชจ์์ดํฌ ์ฒ๋ฆฌ
์ด์ ํน์ ๋์์์์ ์ผ๊ตด์ ํ์งํ๊ณ ๋ชจ์์ดํฌ๋ฅผ ์ ์ฉํ ํ ์๋ก์ด ๋์์์ผ๋ก ์ ์ฅํ๋ ์ฝ๋์.
# ๋์์ ์ผ๊ตด ๋ชจ์์ดํฌ ์ฒ๋ฆฌ ํจ์
def process_video(video_path, output_video_path, mosaic_scale=0.1):
cap = cv.VideoCapture(video_path)
if not cap.isOpened():
raise ValueError(f"๋์์์ ์ด ์ ์์ต๋๋ค: {video_path}")
# ๋์์ ์ ๋ณด ํ๋
frame_width = int(cap.get(cv.CAP_PROP_FRAME_WIDTH))
frame_height = int(cap.get(cv.CAP_PROP_FRAME_HEIGHT))
fps = cap.get(cv.CAP_PROP_FPS)
frame_count = int(cap.get(cv.CAP_PROP_FRAME_COUNT))
print(f"๋์์ ์ ๋ณด: {frame_width}x{frame_height}, {fps} FPS, ์ด {frame_count} ํ๋ ์")
# ๋์์ ์ ์ฅ ๊ฐ์ฒด ์์ฑ
fourcc = cv.VideoWriter_fourcc(*"mp4v")
out = cv.VideoWriter(output_video_path, fourcc, fps, (frame_width, frame_height))
# ์ผ๊ตด ํ์ง๊ธฐ ์์ฑ
face_detector = create_face_detector(frame_width, frame_height)
# ๋์์ ํ๋ ์ ์ฒ๋ฆฌ
frame_idx = 0
while True:
ret, frame = cap.read()
if not ret:
break
# ๊ฐ ํ๋ ์์ ๋ํด ์ผ๊ตด ๊ฒ์ถ ์ํ
face_detector.setInputSize((frame.shape[1], frame.shape[0]))
ret_detect, results = face_detector.detect(frame)
if results is None:
results = np.empty((0, 5))
# ๊ฒ์ถ๋ ์ผ๊ตด์ ๋ํด ๋ชจ์์ดํฌ ์ ์ฉ
for det in results:
frame = apply_mosaic(frame, det, mosaic_scale)
# ์ฒ๋ฆฌ๋ ํ๋ ์์ ์ ์ฅ
out.write(frame)
frame_idx += 1
if frame_idx % 30 == 0:
print(f"{frame_idx}/{frame_count} ํ๋ ์ ์ฒ๋ฆฌ๋จ.")
# ์์ ํด์
cap.release()
out.release()
cv.destroyAllWindows()
print(f"๋ชจ์์ดํฌ ์ฒ๋ฆฌ ๋์์ ์ ์ฅ ์๋ฃ: {output_video_path}")
# ์คํ (๋์์ ์ฒ๋ฆฌ)
video_path = "34685-403408160_small.mp4" # ์๋ณธ ๋์์ ํ์ผ
output_video_path = "output_video.mp4" # ๋ชจ์์ดํฌ ์ฒ๋ฆฌ๋ ๋์์ ํ์ผ
process_video(video_path, output_video_path)
์ค๋ช
- cv.VideoCapture(video_path): ๋์์ ํ์ผ์ ๋ถ๋ฌ์ด
- cv.VideoWriter(): ์๋ก์ด ๋์์ ํ์ผ์ ์์ฑ
- face_detector.detect(frame): ๊ฐ ํ๋ ์์์ ์ผ๊ตด ํ์ง ์ํ
- apply_mosaic(frame, det, mosaic_scale): ์ผ๊ตด์ ๋ชจ์์ดํฌ ์ ์ฉ
- out.write(frame): ์ฒ๋ฆฌ๋ ํ๋ ์์ ์ ์ฅ
6. ๊ฒฐ๊ณผ ํ์ธ
์ด์ ์คํํ๋ฉด ์๋ณธ ๋์์์ ์ผ๊ตด ๋ถ๋ถ์ด ์๋์ผ๋ก ๋ชจ์์ดํฌ ์ฒ๋ฆฌ๋์ด ์๋ก์ด ๋์์์ด ์์ฑ๋จ.
๊ฒฐ๊ณผ ํ์ผ
โ
์ถ๋ ฅ ๋์์: output_video.mp4
7. ๊ฒฐ๋ก
โ
YuNet์ ํ์ฉํ์ฌ ๋์์์์ ์ผ๊ตด์ ์๋์ผ๋ก ํ์งํ๊ณ ๋ชจ์์ดํฌ๋ฅผ ์ ์ฉํ ์ ์์.
โ
์ค์๊ฐ์ผ๋ก ๋์์์ ์ฒ๋ฆฌํ๋ฉด์๋ ๋น ๋ฅด๊ฒ ๋์ํจ.
โ
MP4 ํ์์ผ๋ก ์ ์ฅ ๊ฐ๋ฅํ๋ฉฐ, ์๋ณธ ๋์์์ ํด์๋์ FPS๋ฅผ ์ ์งํจ.