OpenCV 카메라/웹캠 실시간 처리

카메라/웹캠 개요

OpenCV의 VideoCapture 클래스를 사용하여 카메라나 웹캠에서 실시간으로 영상을 캡처하고 처리할 수 있습니다.

주요 사용 사례:

  • 실시간 비디오 스트리밍
  • 실시간 이미지 필터링 및 효과 적용
  • 객체 추적 및 감지
  • 얼굴 인식 및 포즈 추정
  • 모션 감지
  • AR(증강현실) 애플리케이션

1. 기본 카메라 캡처

1.1 웹캠 열기 및 프레임 읽기

import cv2

# 카메라 열기 (0은 기본 카메라, 1은 두 번째 카메라)
cap = cv2.VideoCapture(0)

# 카메라가 제대로 열렸는지 확인
if not cap.isOpened():
    print("카메라를 열 수 없습니다")
    exit()

while True:
    # 프레임 읽기
    # ret: 프레임 읽기 성공 여부 (True/False)
    # frame: 읽은 프레임 (numpy 배열)
    ret, frame = cap.read()
    
    if not ret:
        print("프레임을 읽을 수 없습니다")
        break
    
    # 프레임 표시
    cv2.imshow('Camera', frame)
    
    # 키 입력 처리
    # cv2.waitKey(1): 1밀리초 동안 키 입력 대기 (0이면 무한 대기)
    # & 0xFF: 비트 마스킹으로 하위 8비트만 추출 (64비트 시스템 호환성)
    # ord('q'): 'q' 문자의 ASCII 코드 값 (113)
    # 'q' 키를 누르면 루프 종료
    if cv2.waitKey(1) & 0xFF == ord('q'):
        break

# 리소스 해제
cap.release()
cv2.destroyAllWindows()

1.2 카메라 속성 설정

import cv2

cap = cv2.VideoCapture(0)

# 카메라 속성 설정
# 너비 설정
cap.set(cv2.CAP_PROP_FRAME_WIDTH, 1280)
# 높이 설정
cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 720)
# FPS 설정
cap.set(cv2.CAP_PROP_FPS, 30)
# 밝기 설정 (0-100)
cap.set(cv2.CAP_PROP_BRIGHTNESS, 50)
# 대비 설정
cap.set(cv2.CAP_PROP_CONTRAST, 50)

# 설정된 속성 확인
width = cap.get(cv2.CAP_PROP_FRAME_WIDTH)
height = cap.get(cv2.CAP_PROP_FRAME_HEIGHT)
fps = cap.get(cv2.CAP_PROP_FPS)
print(f"해상도: {int(width)}x{int(height)}, FPS: {fps}")

주요 카메라 속성:

  • cv2.CAP_PROP_FRAME_WIDTH: 프레임 너비
  • cv2.CAP_PROP_FRAME_HEIGHT: 프레임 높이
  • cv2.CAP_PROP_FPS: 초당 프레임 수
  • cv2.CAP_PROP_BRIGHTNESS: 밝기
  • cv2.CAP_PROP_CONTRAST: 대비
  • cv2.CAP_PROP_SATURATION: 채도
  • cv2.CAP_PROP_GAIN: 영상 밝기 증폭률 (받은 빛을 뻥튀기)
  • cv2.CAP_PROP_EXPOSURE: 노출 (빛을 받는 시간을 늘림)

2. 실시간 이미지 처리

2.1 그레이스케일 변환

import cv2

cap = cv2.VideoCapture(0)

while True:
    ret, frame = cap.read()
    if not ret:
        break
    
    # 그레이스케일 변환
    gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
    
    # 원본과 그레이스케일 동시 표시
    cv2.imshow('Original', frame)
    cv2.imshow('Grayscale', gray)
    
    if cv2.waitKey(1) & 0xFF == ord('q'):
        break

cap.release()
cv2.destroyAllWindows()

2.2 실시간 필터링

import cv2
import numpy as np

cap = cv2.VideoCapture(0)

while True:
    ret, frame = cap.read()
    if not ret:
        break
    
    # 가우시안 블러
    blurred = cv2.GaussianBlur(frame, (15, 15), 0)
    
    # 엣지 검출
    gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
    edges = cv2.Canny(gray, 100, 200)
    
    # 결과 표시
    cv2.imshow('Original', frame)
    cv2.imshow('Blurred', blurred)
    cv2.imshow('Edges', edges)
    
    if cv2.waitKey(1) & 0xFF == ord('q'):
        break

cap.release()
cv2.destroyAllWindows()

2.3 실시간 색상 필터링

import cv2
import numpy as np

cap = cv2.VideoCapture(0)

while True:
    ret, frame = cap.read()
    if not ret:
        break
    
    # HSV 색공간으로 변환
    hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
    
    # 빨간색 범위 정의 (BGR이 아닌 HSV)
    lower_red = np.array([0, 50, 50])
    upper_red = np.array([10, 255, 255])
    lower_red2 = np.array([170, 50, 50])
    upper_red2 = np.array([180, 255, 255])
    
    # 빨간색 마스크 생성
    mask1 = cv2.inRange(hsv, lower_red, upper_red)
    mask2 = cv2.inRange(hsv, lower_red2, upper_red2)
    mask = mask1 + mask2
    
    # 마스크 적용
    result = cv2.bitwise_and(frame, frame, mask=mask)
    
    cv2.imshow('Original', frame)
    cv2.imshow('Red Filter', result)
    cv2.imshow('Mask', mask)
    
    if cv2.waitKey(1) & 0xFF == ord('q'):
        break

cap.release()
cv2.destroyAllWindows()

3. 프레임 저장 및 재생

3.1 비디오 파일로 저장

import cv2

cap = cv2.VideoCapture(0)

# 비디오 코덱 및 Writer 설정
fourcc = cv2.VideoWriter_fourcc(*'XVID')  # 또는 'mp4v', 'X264'
out = cv2.VideoWriter('output.avi', fourcc, 20.0, (640, 480))

while True:
    ret, frame = cap.read()
    if not ret:
        break
    
    # 프레임 저장
    out.write(frame)
    
    cv2.imshow('Recording', frame)
    if cv2.waitKey(1) & 0xFF == ord('q'):
        break

# 리소스 해제
cap.release()
out.release()
cv2.destroyAllWindows()

3.2 스크린샷 저장

import cv2
import datetime

cap = cv2.VideoCapture(0)
frame_count = 0

while True:
    ret, frame = cap.read()
    if not ret:
        break
    
    cv2.imshow('Camera', frame)
    
    key = cv2.waitKey(1) & 0xFF
    
    if key == ord('q'):
        break
    elif key == ord('s'):  # 's' 키를 누르면 스크린샷 저장
        timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
        filename = f'screenshot_{timestamp}.jpg'
        cv2.imwrite(filename, frame)
        print(f"스크린샷 저장: {filename}")
        frame_count += 1

cap.release()
cv2.destroyAllWindows()

4. 성능 최적화

4.1 프레임 크기 조정으로 성능 향상

import cv2

cap = cv2.VideoCapture(0)

# 작은 해상도로 설정 (처리 속도 향상)
cap.set(cv2.CAP_PROP_FRAME_WIDTH, 640)
cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 480)

while True:
    ret, frame = cap.read()
    if not ret:
        break
    
    # 프레임 크기 조정 (더 작게)
    small_frame = cv2.resize(frame, (320, 240))
    
    # 작은 프레임에서 처리 (더 빠름)
    gray = cv2.cvtColor(small_frame, cv2.COLOR_BGR2GRAY)
    edges = cv2.Canny(gray, 100, 200)
    
    # 결과를 원본 크기로 확대하여 표시
    edges_large = cv2.resize(edges, (640, 480))
    
    cv2.imshow('Original', frame)
    cv2.imshow('Edges', edges_large)
    
    if cv2.waitKey(1) & 0xFF == ord('q'):
        break

cap.release()
cv2.destroyAllWindows()

4.2 FPS 측정 및 표시

import cv2
import time

cap = cv2.VideoCapture(0)

prev_time = 0
fps = 0

while True:
    ret, frame = cap.read()
    if not ret:
        break
    
    # FPS 계산
    current_time = time.time()
    fps = 1 / (current_time - prev_time) if prev_time > 0 else 0
    prev_time = current_time
    
    # FPS 텍스트 표시
    fps_text = f'FPS: {fps:.2f}'
    cv2.putText(frame, fps_text, (10, 30), 
                cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 2)
    
    cv2.imshow('Camera', frame)
    if cv2.waitKey(1) & 0xFF == ord('q'):
        break

cap.release()
cv2.destroyAllWindows()

4.3 스레드를 사용한 비동기 처리

import cv2
import threading
import queue

class VideoCaptureThread:
    def __init__(self, src=0):
        self.cap = cv2.VideoCapture(src)
        self.q = queue.Queue()
        self.running = True
        
        # 캡처 스레드 시작
        self.thread = threading.Thread(target=self._reader)
        self.thread.daemon = True
        self.thread.start()
    
    def _reader(self):
        while self.running:
            ret, frame = self.cap.read()
            if not ret:
                break
            if not self.q.empty():
                try:
                    self.q.get_nowait()
                except queue.Empty:
                    pass
            self.q.put((ret, frame))
    
    def read(self):
        return self.q.get()
    
    def release(self):
        self.running = False
        self.thread.join()
        self.cap.release()

# 사용 예시
cap = VideoCaptureThread(0)

while True:
    ret, frame = cap.read()
    if not ret:
        break
    
    # 이미지 처리
    gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
    
    cv2.imshow('Camera', frame)
    if cv2.waitKey(1) & 0xFF == ord('q'):
        break

cap.release()
cv2.destroyAllWindows()

5. 실전 예제

5.1 실시간 얼굴 감지 (Haar Cascade)

import cv2

# 얼굴 감지 모델 로드
face_cascade = cv2.CascadeClassifier(cv2.data.haarcascades + 'haarcascade_frontalface_default.xml')

cap = cv2.VideoCapture(0)

while True:
    ret, frame = cap.read()
    if not ret:
        break
    
    # 그레이스케일 변환
    gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
    
    # 얼굴 감지
    faces = face_cascade.detectMultiScale(gray, 1.3, 5)
    
    # 감지된 얼굴에 사각형 그리기
    for (x, y, w, h) in faces:
        cv2.rectangle(frame, (x, y), (x+w, y+h), (255, 0, 0), 2)
        cv2.putText(frame, 'Face', (x, y-10), 
                   cv2.FONT_HERSHEY_SIMPLEX, 0.9, (255, 0, 0), 2)
    
    cv2.imshow('Face Detection', frame)
    if cv2.waitKey(1) & 0xFF == ord('q'):
        break

cap.release()
cv2.destroyAllWindows()

5.2 실시간 모션 감지

import cv2
import numpy as np

cap = cv2.VideoCapture(0)

# 배경 추출기 생성
fgbg = cv2.createBackgroundSubtractorMOG2()

while True:
    ret, frame = cap.read()
    if not ret:
        break
    
    # 배경 제거 (움직이는 객체만 추출)
    fgmask = fgbg.apply(frame)
    
    # 노이즈 제거
    kernel = np.ones((5, 5), np.uint8)
    fgmask = cv2.morphologyEx(fgmask, cv2.MORPH_OPEN, kernel)
    fgmask = cv2.morphologyEx(fgmask, cv2.MORPH_CLOSE, kernel)
    
    # 컨투어 찾기
    contours, _ = cv2.findContours(fgmask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    
    # 움직이는 객체에 사각형 그리기
    for contour in contours:
        area = cv2.contourArea(contour)
        if area > 500:  # 작은 노이즈 제거
            x, y, w, h = cv2.boundingRect(contour)
            cv2.rectangle(frame, (x, y), (x+w, y+h), (0, 255, 0), 2)
    
    cv2.imshow('Original', frame)
    cv2.imshow('Motion', fgmask)
    
    if cv2.waitKey(1) & 0xFF == ord('q'):
        break

cap.release()
cv2.destroyAllWindows()

6. 문제 해결

프레임 지연 문제 해결

import cv2

cap = cv2.VideoCapture(0)

# 버퍼 크기 제한 (지연 방지)
cap.set(cv2.CAP_PROP_BUFFERSIZE, 1)

while True:
    ret, frame = cap.read()
    if not ret:
        break
    
    # 처리 시간이 오래 걸리는 작업은 최소화
    # 예: 작은 해상도로 처리
    small = cv2.resize(frame, (320, 240))
    # ... 처리 ...
    
    cv2.imshow('Camera', frame)
    if cv2.waitKey(1) & 0xFF == ord('q'):
        break

cap.release()
cv2.destroyAllWindows()

주요 팁

  1. 성능 최적화: 처리할 프레임 크기를 줄이면 성능이 향상됩니다
  2. 버퍼 관리: CAP_PROP_BUFFERSIZE를 1로 설정하여 지연을 줄일 수 있습니다
  3. 리소스 해제: 항상 cap.release()cv2.destroyAllWindows()를 호출하세요
  4. 에러 처리: ret 값을 항상 확인하여 프레임 읽기 성공 여부를 체크하세요
  5. 키 입력: cv2.waitKey(1)은 1밀리초 대기하며, 0으로 설정하면 무한 대기합니다