본문 바로가기

스마트팩토리?

[라즈베리 파이] opencv 카메라 버퍼

어제 라즈베리파이로 카메라 찍기 기능을 실습했었다. 그런데 해당 기능을 이용하다 보니, 마치 랙 걸린 것처럼 예전에 가리키던 화면을 저장하고 있다는 것을 알게 되었다.

서로 다른 화면을 보며 찍은 사진. 실제로는 거의 동일한 시점의 사진을 얻게 된다.

이 현상이 발생하는 이유는 opencv가 내부적으로 화면에 대한 버퍼를 가지고 있기 때문이다. imread 메서드가 현재 카메라가 보고 있는 화면을 반환하는 것이 아니라, 과거에 버퍼에 저장해뒀던 화면을 반환하기 때문에 내가 원하는 사진이 나오지 않았다.

따라서, "사진 찍기" 처럼 버퍼가 의미 없는 기능을 opencv로 구현하기 위해서는 버퍼 사이즈를 작게 유지해야 한다. 공식 문서에 따르면 CAP_PROP_BUFFERSIZE 옵션을 통해 버퍼 사이즈를 지정할 수 있다.

https://docs.opencv.org/3.4/d4/d15/group__videoio__flags__base.html#gaeb8dd9c89c10a5c63c139bf7c4f5704d

아래 예시는 CAP_PROP_BUFFERSIZE를 1로 지정했다. 버퍼 사이즈의 최소값은 1이므로, take 메서드를 실행할 때 프레임을 얻기 전에 1번의 imread를 먼저 실행하여 기존에 저장되어 있던 버퍼를 비워, 항상 새 화면 정보를 받을 수 있게 했다.

import cv2
from datetime import datetime
from gpiozero import Button
from signal import pause
from time import sleep

# 카메라 초기화
cap = cv2.VideoCapture(-1)
cap.set(cv2.CAP_PROP_FRAME_WIDTH, 640)
cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 480)
cap.set(cv2.CAP_PROP_BUFFERSIZE, 1)

def take():
  # 버퍼 내용 지우기
  cap.read()
  ret, frame = cap.read()
  if ret:
    filename = datetime.now().strftime("%Y-%m-%d|%H:%M:%S") + ".jpg"
    cv2.imwrite(filename, frame)
  else:
    print("wrong state")

button = Button(18)
button.when_activated = take

try:
  while not cap.isOpened():
    print("not opened")
    sleep(0.5)
  pause()
except:
  pass

# cam.stop()
cap.release()
cv2.destroyAllWindows()

연달아 찍은 사진

위 설정을 통해 연달아 사진을 찍어도 현재 카메라가 보고 있는 화면을 저장할 수 있게 되었다.