오늘은 리더님께서 몸이 안 좋으셔서 수업 대신 OpenCV의 얼굴 인식을 이용한 여러 실습 문제를 풀어보는 시간을 가졌다.
face_cascade = cv.CascadeClassifier(FACE_CASCADE)
eye_cascade = cv.CascadeClassifier(EYE_CASCADE)
cap = cv.VideoCapture()
SCALE = 1
THICKNESS = 2
COLOR = (255,255,255)
while cap.isOpened():
ret, frame = cap.read()
if not ret:
break
gray = cv.cvtColor(frame, cv.COLOR_BGR2GRAY)
faces = face_cascade.detectMultiScale(
gray, scaleFactor=1.1, minNeighbors=10, minSize=(10,10)
)
eyes = eye_cascade.detectMultiScale(
gray, scaleFactor=1.1, minNeighbors=30, minSize=(30,30)
)
if len(faces):
for face in faces:
x, y, width, height = face
cv.rectangle(frame, (x,y), (x+width, y+height), (0,0,255), 2, cv.LINE_AA)
cv.putText(frame, "Face", (x,y), cv.FONT_HERSHEY_DUPLEX, SCALE, COLOR, THICKNESS)
if len(eyes):
for eye in eyes:
x, y, width, height = eye
cv.rectangle(frame, (x,y), (x+width, y+height), (0,255,0), 2, cv.LINE_AA)
cv.putText(frame, "Eye", (x,y), cv.FONT_HERSHEY_DUPLEX, SCALE, COLOR, THICKNESS)
cv.imshow("Camera", frame)
if cv.waitKey(1) == ord("q"):
break
cap.release()
cv.destroyAllWindows()
cv.waitKey(1)
eye_cascade = cv.CascadeClassifier(EYE_CASCADE)
img = cv.imread(FACE)
eye_icon = cv.imread("../images/eye.png")
gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
eyes = eye_cascade.detectMultiScale(
gray, scaleFactor=1.1, minNeighbors=15, minSize=(10,10)
)
if len(eyes):
for eye in eyes:
x, y, width, height = eye
img[y:y+height, x:x+width] = cv.resize(eye_icon, (width, height))
cv.imshow("img", img)
cv.waitKey(0)
cv.destroyAllWindows()
cv.waitKey(1)
cv2.createBackgroundSubtractorMOG2()history=500 : 배경 학습에 사용할 프레임 수 / 값이 크면 배경 모델이 천천히 변함(안정적), 작으면 빠르게 변함(민감)varThreshold=16 : 전경/배경 분류 임계값 / 값이 작으면 작은 변화에도 움직임으로 판단, 값이 크면 큰 변화가 있어야 움직임으로 판단detectShadows=True : 그림자 검출 여부import time
import os
# 폴더 생성
SAVE_DIR = "../output"
# 최소 영역
MIN_AREA = 1200
# 저장 간격(초단위)
COOLDOWN = 1.0
# 폴더가 없으면 생성
os.makedirs(SAVE_DIR, exist_ok=True)
cap = cv.VideoCapture(0)
if not cap.isOpened():
raise RuntimeError("웹캠 오류")
# 배경제거
backsub = cv.createBackgroundSubtractorMOG2(history=200, varThreshold=25, detectShadows=True)
last_saved = 0.0
while True:
ret, frame = cap.read()
if not ret:
break
# 전경 마스크 받아오기
fg = backsub.apply(frame) # 현재 프레임에서 움직임 부분만 추출
# 그림자 제거 : 200이상 부분만 남기고 나머지는 0으로 처리
_, fg = cv.threshold(fg, 200, 255, cv.THRESH_BINARY)
# 움직이는 영역을 키워서 구멍 메우기
fg = cv.dilate(fg, None, iterations=2)
# 움직임 영역 찾기
contours, _ = cv.findContours(fg, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE)
motion = False # 움직임이 있었는지 표시하는 변수
for c in contours:
# 너무 작은 영역 무시
if cv.contourArea(c) < MIN_AREA:
continue
# 움직임이 있는 부분에 사각형 표시
x, y, w, h = cv.boundingRect(c)
cv.rectangle(frame, (x,y), (x+w, y+h), (0,255,0), 2)
motion = True
# 움직인 사진 저장
now = time.time()
if motion and (now - last_saved > COOLDOWN):
filename = time.strftime("%Y%m%d_%H%M%S") + ".jpg" # 현재 시간으로 파일명 생성
cv.imwrite(os.path.join(SAVE_DIR, filename), frame) # 현재 프레임 저장
last_saved = now
# 화면 상단에 "Motion : ON/OFF" 글자 표시
cv.putText(frame, f"Motion : {'ON' if motion else 'OFF'}", (10, 30), cv.FONT_HERSHEY_DUPLEX, 1, (0,255,0) if motion else (0,0,255), 2)
# 원본 영싱과 마스크 영상 출력
cv.imshow("frame", frame)
cv.imshow("mask", fg)
# 종료
if cv.waitKey(1) == ord("q"):
break
# 카메라, 창 종료
cap.release()
cv.destroyAllWindows()
cv.waitKey(1)
여러 실습 문제들을 풀어보면서 OpenCV가 조금 더 익숙해진 것 같다. 그리고 배경 제거에 대한 내용도 꽤 유용해 보여서 더 연습해봐야 할 것 같다.