본문 바로가기

생활코딩

라즈베리파이 카메라 모듈 - 모니터링 웹앱 만들기

라즈베리파이의 카메라 모듈을 사용할 기회가 생겨 연결해 보고 모니터링 웹앱을 만들어 봤습니다. 기록을 위해 간단히 정리해 봅니다.

 

카메라 모듈 연결 및 동작 확인

준비 사항

동작 확인

Pi OS 최신 버전에는 라즈베리파이 카메라 모듈 동작을 위한 CLI용 프로그램과 파이썬 패키지가 모두 설치돼 있습니다. 그래서, 카메라 모듈을 연결한 후에 다음과 같은 명령을 실행 시키면 잘 연결됐는지 확인할 수있습니다.

rpicam-hello

 

아래와 같은 내용이 나오면 정상적으로 동작하는 것입니다. 만약에 GUI(x-windows) 환경에서 쉘창을 열어 실행했다면 프리뷰 창을 볼 수 있습니다.

 

이미지를 저장해서 보고 싶은 경우에는 다음과 같이 합니다.

rpicam-jpeg -o 파일이름.jpg

python으로 이미지 캡처하기

이제 PI OS 최신 버전에 기본으로 설치돼 있는 picamera2 라는 파이썬 라이브러리를 사용하여 이미지를 캡처해 보겠습니다. picamera2가 설치돼 있는지 확인하려면 다음과 같이 합니다.

sudo apt show python3-picamera2

 

만약, "No packages found"라고 나오면 다음과 같이 picamera2를 설치할 수 있습니다.

sudo apt install -y python3-picamera2

 

※ picamera2에 대한 상세 설명은 https://datasheets.raspberrypi.com/camera/picamera2-manual.pdf 에서 확인할 수 있습니다.

간단한 이미지 캡처 예제

camera = Picamera2()
camera_still_config = self.camera.create_still_configuration(
    main={
        'size': (1024, 768)
    }
)
camera.configure(self.camera_still_config)
camera.start()
time.sleep(2)
camera.capture_file('test.jpg')

 

라즈베리파이에서 다음의 코드를 camera_test.py로 저장하고 python camera_test.py 식으로 실행하면 test.jpg라는 이름으로 캡처된 이미지가 저장됩니다. sleep을 2초간 해 주는 이유는 카메라가 기동되는데 약 2초 정도의 시간이 필요하기 때문입니다.

라즈베리파이 원격 접속 개발

라즈베리파이에서 실행되는 프로그램이 일반적으로 PC에서도 동작하는 것이라면 PC에서 코딩 및 실행 테스트를 완료한 후에 라즈베리파이로 업로드하여 사용하면 됩니다. 하지만, 카메라나 센스류처럼 라즈베리파이에만 있는 장치를 위해서는 코딩 후 라즈베리파이에서 직접 테스트를 해야만 합니다.
이런 경우 라즈베리파이에 neovim 등을 설치 및 설정하여 쉘 상에서 코딩할 수도 있지만, 가장 편리한 방법 중에 하나는 VSCode의 Remote Explorer 플러그인을 사용하는 것입니다. 이를 사용하면 PC에서 ssh를 통해 라즈베리파이에 연결하고 파일들을 조회하여 PC의 VSCode로 불러와서 편집 및 저장할 수 있습니다. 이렇게 사용하는 경우 라즈베리파이쪽에는 VSCode server가 (자동으로) 실행되고 python 플러그인도 설치되어(설치할지 자동으로 묻습니다) 어느 정도 코드 자동완성도 지원하게 됩니다.

카메라 모니터링 웹앱

이제 카메라도 라즈베리파이에 연결됐고 카메라 사용을 위한 라이브러리들도 모두 준비를 마쳤습니다. 그러면, 캡처 이미지를 지속적으로 스트리밍하고 이를 확인하는 웹앱을 만들어 보겠습니다. 웹앱은 FastAPI 기반 프로젝트를 위한 템플릿인 summer를 사용하겠습니다(summer는 제가 개인적으로 사용하려고 만들어 놓은 것입니다. 물론 오픈소스로 공개돼 있습니다).

라즈베리파이에 ssh로 접속하여 다음과 같이 summer를 클론합니다.

git clone https://github.com/intotherealworld/summer.git

 

클론 후에 여기에서 설명하는대로 자신이 원하는 프로젝트 이름을 지정합니다. 여기서는 프로젝트 이름을 picam이라고 해서 진행해 보겠습니다.

프로젝트의 디렉토리로 이동하고, python 가상 환경을 구성합니다.

cd picam
python3 -m venv .venv --system-site-packages
source .venv/bin/activate

 

필요한 패키지를 설치하고, 잘 설치가 됐는지 실행을 한 번 해 봅니다.

pip install -r requirements.txt
python local_server.py

 

웹브라우저로 라즈베리파이의 ip 주소 5000번으로 접속해 봅니다. 다음과 같이 나오면 제대로 설치 및 설정된 것입니다.

이제 카메라로 이미지를 계속 캡처하고 스트리밍하여 카메라가 향하는 곳을 모니터링하는 기능을 추가해 보겠습니다.

monitor라는 디렉토리를 만들고 monitor_router.py 만듭니다. 전체 경로는 picam/picam/monitor/monitor_router.py가 됩니다.

from fastapi import APIRouter, Request
from fastapi.responses import StreamingResponse
from picamera2 import Picamera2

from picam.monitor.camera_agent import CameraAgent


monitor_router = APIRouter(tags=['monitor'], prefix='/monitor')
camera_agent = CameraAgent()


def generate_image():
    while True:
        img_bytes = camera_agent.capture()
        yield (
            b'--frame\r\n'
            b'Content-Type: image/jpeg\r\n\r\n' + img_bytes + b'\r\n\r\n'
        )


@monitor_router.get('', include_in_schema=False)
def respond_root(request: Request):
    return StreamingResponse(
        generate_image(),
        media_type="multipart/x-mixed-replace; boundary=frame",
    )

 

URI 패스 /monitor로 GET 요청(브라우저에서 일반적으로 접속)을 하는 경우 지속적으로 카메라로부터 이미지를 캡처하여 스트리밍하게 됩니다.

다음은 camera_agent.py 입니다. monitor_router.py와 같은 위치에 camera_agent.py 파일을 만듭니다. 내용은 다음과 같습니다.

import io
import time

from picamera2 import Picamera2

from summer_toolkit.utility.singleton import Singleton


class CameraAgent(metaclass=Singleton):
    def __init__(self):
        self.camera = Picamera2()
        self.camera_still_config = self.camera.create_still_configuration(
            main={
                'size': (1024, 768)
            }
        )
        self.camera.configure(self.camera_still_config)
        self.camera.start()
        time.sleep(2)

    def capture(self, is_bytearray=True):
        captured = self.camera.capture_image('main')

        if is_bytearray:
            result = io.BytesIO()
            captured.save(result, format='jpeg')

            return result.getvalue()

        return captured

 

__init__ 부분에 보시면 이미지 크기를 1024x768로 지정한 것을 보실 수 있습니다. 최대 크기는 명세상으로는 3280x2464까지 가능합니다.

capture 메소드에서 이미지를 캡처합니다. picamer2의 capture_image 메소드는 카메라에서 캡처한 이미지를 PIL(python image library) 형식으로 반환합니다. if 부분은 PIL 형식을 jpeg의 바이트 스트림으로 변경하여 반환하는 부분입니다.

 

위와 같이 코드를 작성한 후에 picam 디렉토리에서 python local_server.py로 실행 후에 브라우저로 다음의 주소를 접속합니다.

http://192.168.0.171:5000/monitor

 

그러면, 다음과 같이 카메라가 향하는 쪽을 모니터링 할 수 있게 됩니다.

전체 코드는 https://github.com/intotherealworld/picam 에서 확인할 수 있습니다.

참고