logo

OpenCV 동영상과 웹캠

실습 준비

pip install opencv-python pillow
  • 실습 동영상: 자전거.mp4를 현재 폴더에 준비
from pathlib import Path

import cv2 as cv
from yeom import to_pil

동영상은 프레임의 연속

동영상은 시간 순서대로 저장된 이미지 프레임의 연속.

frame 0 → frame 1 → frame 2 → ···
  • 프레임(frame): 동영상을 구성하는 이미지 한 장
  • FPS(frames per second): 1초 동안 재생되는 프레임 수
  • 해상도: 각 프레임의 가로·세로 픽셀 수
  • 코덱(codec): 프레임을 압축하고 해제하는 방식
  • 컨테이너(container): 영상과 부가 정보를 담는 파일 형식

동영상 파일 열기

video_path = Path("자전거.mp4")
video = cv.VideoCapture(str(video_path))

VideoCapture: 동영상 파일, 이미지 시퀀스, 카메라에서 프레임을 읽는 공통 인터페이스.

동영상 정보 확인

fps = video.get(cv.CAP_PROP_FPS)
frame_count = int(video.get(cv.CAP_PROP_FRAME_COUNT))
frame_width = int(video.get(cv.CAP_PROP_FRAME_WIDTH))
frame_height = int(video.get(cv.CAP_PROP_FRAME_HEIGHT))

{
    "FPS": fps,
    "프레임 수": frame_count,
    "프레임 크기": (frame_width, frame_height),
}

속성값은 동영상 코덱과 운영체제의 비디오 백엔드에 따라 일부 제공되지 않거나 근사값일 수 있음.

프레임 한 장 읽기

ret, frame = video.read()
  • ret: 프레임을 성공적으로 읽었는지 나타내는 불리언 값
  • frame: BGR 순서의 NumPy 이미지 배열
  • 동영상 끝에 도달하거나 프레임을 읽지 못하면 retFalse
if not ret:
    raise RuntimeError("첫 프레임을 읽지 못했습니다.")

frame.shape
to_pil(frame)
video.release()

release(): 파일이나 카메라 장치 해제.

동영상 재생

동영상 파일을 다시 열고 프레임을 한 장씩 읽어 데스크톱 창에 표시.

video = cv.VideoCapture(str(video_path))
fps = video.get(cv.CAP_PROP_FPS)
delay = max(1, round(1000 / fps)) if fps > 0 else 33  # FPS를 읽지 못하면 약 30 FPS 사용

try:
    while video.isOpened():
        ret, frame = video.read()
        if not ret:
            break

        cv.imshow("video", frame)

        key = cv.waitKey(delay) & 0xFF  # 하위 8비트 키 코드만 사용
        if key == ord("q"):
            break
finally:
    video.release()
    cv.destroyAllWindows()
  • delay: 다음 프레임을 표시하기 전 기다릴 밀리초
  • q: 반복 재생 중단
  • tryfinally: 오류나 중단이 발생해도 장치와 창을 해제

GUI를 사용할 수 없는 원격 노트북에서는 한 프레임씩 to_pil(frame)로 확인하거나 별도의 노트북용 동영상 표시 기능을 사용.

동영상 파일 저장

VideoWriter: 프레임 배열을 순서대로 인코딩하여 동영상 파일로 저장.

reader = cv.VideoCapture(str(video_path))

if not reader.isOpened():
    raise FileNotFoundError(f"동영상을 열 수 없습니다: {video_path}")

fps = reader.get(cv.CAP_PROP_FPS)
width = int(reader.get(cv.CAP_PROP_FRAME_WIDTH))
height = int(reader.get(cv.CAP_PROP_FRAME_HEIGHT))

fourcc = cv.VideoWriter_fourcc(*"mp4v")  # MP4 예제 코덱
writer = cv.VideoWriter(
    "자전거_복사본.mp4",
    fourcc,
    fps,
    (width, height),
)

if not writer.isOpened():
    reader.release()
    raise RuntimeError("동영상 저장 장치를 열지 못했습니다.")

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

        writer.write(frame)
finally:
    reader.release()
    writer.release()
  • fourcc: 사용할 코덱을 나타내는 네 문자 코드
  • fps: 출력 동영상의 재생 속도
  • (width, height): 저장할 모든 프레임의 크기
  • writer.write(frame): BGR 프레임을 출력 동영상에 추가

사용 가능한 코덱은 운영체제와 OpenCV 설치 환경에 따라 다름. VideoWriter는 영상 프레임을 저장하며 원본의 오디오 트랙은 복사하지 않음.

웹캠에서 한 프레임 읽기

동영상 파일 경로 대신 카메라 장치 번호를 전달. 일반적으로 0은 기본 카메라.

camera = cv.VideoCapture(0)  # 기본 카메라 장치

if not camera.isOpened():
    raise RuntimeError("카메라를 열 수 없습니다.")

원하는 프레임 크기를 요청한 뒤 실제로 적용된 값을 확인. 카메라가 지원하지 않는 크기라면 가장 가까운 값이 사용될 수 있음.

camera.set(cv.CAP_PROP_FRAME_WIDTH, 640)  # VGA 가로 크기 요청
camera.set(cv.CAP_PROP_FRAME_HEIGHT, 480)  # VGA 세로 크기 요청

actual_width = int(camera.get(cv.CAP_PROP_FRAME_WIDTH))
actual_height = int(camera.get(cv.CAP_PROP_FRAME_HEIGHT))
actual_width, actual_height

try:
    ret, webcam_frame = camera.read()
    if not ret:
        raise RuntimeError("웹캠 프레임을 읽지 못했습니다.")
finally:
    camera.release()

to_pil(webcam_frame)

웹캠 접근에는 로컬 실행 환경과 운영체제의 카메라 권한이 필요.

웹캠 영상 표시

camera = cv.VideoCapture(0)  # 기본 카메라 장치

if not camera.isOpened():
    raise RuntimeError("카메라를 열 수 없습니다.")

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

        cv.imshow("webcam", frame)

        key = cv.waitKey(1) & 0xFF  # 하위 8비트 키 코드만 사용
        if key == ord("q"):
            break
finally:
    camera.release()
    cv.destroyAllWindows()

cv.waitKey(1): 잠깐 키 입력을 확인하면서 화면 갱신. 원격 서버나 일반적인 Colab 환경에서는 로컬 웹캠과 데스크톱 창을 같은 방식으로 사용할 수 없음.

Previous
NumPy 이미지 배열과 PIL 변환