파이카메라(PiCamera) 파이썬으로 제어하기
본격적으로 파이카메라를 제어하는 방법입니다. 다른 여러 프로그래밍 언어로 제어가 가능하겠지만 여기서는 파이썬으로 제어해 보겠습니다. 진행을 위해서는 아래 설정이 미리 갖추어진 상태여야 합니다.
- 파이카메라 설치, 설정
https://www.hardcopyworld.com/?p=1803 - 라즈베리 파이 GPIO 제어
https://www.hardcopyworld.com/?s=%ED%8C%8C%EC%9D%B4%EC%B9%B4%EB%A9%94%EB%9D%BC&post_type%5B%5D=any&search_limit_to_post_titles=0&fs=1
테스트
가급적 X-Window 그래픽 환경에서 해보는게 좋겠습니다.
- $ startx
- LXTerminal 실행
- sudo idle & (파이썬 개발환경 실행)
- File > New Window
아래 코드를 입력 합니다. 대소문자 주의!
import time import picamera with picamera.PiCamera() as camera: camera.start_preview() time.sleep(5) camera.capture('/home/pi/Desktop/image.jpg') camera.stop_preview()
- File > Save 로 저장
- Run > Run Module 로 스크립트 실행
이상없이 동작되는걸 확인하면 다음과정을 위한 준비가 다 된 것입니다.
버튼으로 사진 촬영
아래와 같이 버튼을 연결합니다.
버튼 입력을 확인하기 위해 RPi.GPIO 모듈을 사용합니다. 그리고 이전 소스에서 sleep(5) 함수를 사용한 부분을 GPIO.wait_for_edge() 로 바꿔줍니다. 버튼이 특정 상태가 되길 기다리는 것입니다.
import time import picamera import RPi.GPIO as GPIO # new GPIO.setmode(GPIO.BCM) # new GPIO.setup(17, GPIO.IN, GPIO.PUD_UP) # new with picamera.PiCamera() as camera: camera.start_preview() GPIO.wait_for_edge(17, GPIO.FALLING) # new camera.capture('/home/pi/Desktop/image.jpg') camera.stop_preview()
/home/pi/Desktop/ 폴더에 image.jpg 파일을 삭제하고 스크립트를 실행합니다. 프리뷰가 시작되면 버튼을 누릅니다. 이미지가 캡쳐되었는지 확인하세요.
셀카용 지연 촬영
버튼을 누르면 5초 뒤 촬영되도록 만들어봅니다. 버튼입력이 감지된 뒤 sleep(5) 만 추가해 주면 되겠죠.
import time import picamera import RPi.GPIO as GPIO GPIO.setmode(GPIO.BCM) GPIO.setup(17, GPIO.IN, GPIO.PUD_UP) with picamera.PiCamera() as camera: camera.start_preview() GPIO.wait_for_edge(17, GPIO.FALLING) time.sleep(5) # new camera.capture('/home/pi/Desktop/image.jpg') camera.stop_preview()
앞서와 마찬가지로 이미지 삭제하고 실행해서 테스트 해보세요.
캠코더 촬영
처음 버튼을 누르면 캠코더 촬영을 시작하고, 다시 버튼을 누르면 촬영을 종료합니다.
camera_start_recording() 함수와 camera_stop_recording() 함수로 촬영 시작과 종료를 할 수 있습니다. 물론 2번의 버튼 입력 대기 코드가 필요 하겠지요.
import time import picamera import RPi.GPIO as GPIO GPIO.setmode(GPIO.BCM) GPIO.setup(17, GPIO.IN, GPIO.PUD_UP) with picamera.PiCamera() as camera: camera.start_preview() GPIO.wait_for_edge(17, GPIO.FALLING) camera.start_recording('/home/pi/Desktop/video.h264') time.sleep(1) GPIO.wait_for_edge(17, GPIO.FALLING) camera.stop_recording() camera.stop_preview()
마지막 n 초의 영상만 저장
슬슬 복잡해 집니다. Circular buffer 를 이용해서 마지막 n 초 간의 영상만 저장하도록 하는 예제입니다.
picamera.PiCameraCircularIO() 함수를 이용해서 버퍼에 영상을 계속 써둡니다. 촬영 종료를 알리는 버튼이 감지되면 버퍼에 있는 스트림을 파일로 저장하는 방식입니다.
import io import time import picamera import RPi.GPIO as GPIO GPIO.setmode(GPIO.BCM) GPIO.setup(17, GPIO.IN, GPIO.PUD_UP) with picamera.PiCamera() as camera: stream = picamera.PiCameraCircularIO(camera, seconds=20) camera.start_preview() camera.start_recording(stream, format='h264') GPIO.wait_for_edge(17, GPIO.FALLING) camera.stop_recording() camera.stop_preview() for frame in stream.frames: if frame.header: stream.seek(frame.position) break with io.open('/home/pi/Desktop/video.h264', 'wb') as output: while True: data = stream.read1() if not data: break output.write(data)
기존에 생성된 비디오 파일을 삭제하고 스크립트를 실행해서 확인해보세요.
일정간격(Time-Lapse) 촬영
촬영을 하고 일정 시간만큼 sleep() 을 한 뒤 다시 촬영하는 패턴입니다. 이걸 몇 일 동안 반복하도록 놔두면 Time-Lapse 영상이 가능합니다.
import time import picamera VIDEO_DAYS = 5 FRAMES_PER_HOUR = 1 FRAMES = FRAMES_PER_HOUR * 24 * VIDEO_DAYS def capture_frame(frame): with picamera.PiCamera() as cam: time.sleep(2) cam.capture('/home/pi/Desktop/frame%03d.jpg' % frame) # Capture the images for frame in range(FRAMES): # Note the time before the capture start = time.time() capture_frame(frame) # Wait for the next capture. Note that we take into # account the length of time it took to capture the # image when calculating the delay time.sleep( int(60 * 60 / FRAMES_PER_HOUR) - (time.time() - start) )
이미지로부터 비딩오 파일을 생성하기 위해서는 FFMPEG 패키지가 필요합니다.
- sudo apt-get install ffmpeg
설치가 끝나면 아래 경로와 파일 설정이 자신에게 맞는지 확인하고 실행합니다.
- ffmpeg -y -f image2 -i /home/pi/Desktop/frame%03d.jpg -r 24 -vcodec libx264 -profile high -preset slow /home/pi/Desktop/timelapse.mp4
스톱 모션 촬영
여기까지 보셨다면 이건 쉽겠죠. 버튼 입력을 대기하고 있다가 버튼 눌러지면 촬영하는 패턴을 무한반복 시키면 됩니다.
import picamera import RPi.GPIO as GPIO GPIO.setmode(GPIO.BCM) GPIO.setup(17, GPIO.IN, GPIO.PUD_UP) with picamera.PiCamera() as camera: camera.start_preview() frame = 1 while True: GPIO.wait_for_edge(17, GPIO.FALLING) camera.capture('/home/pi/Desktop/frame%03d.jpg' % frame) frame += 1 camera.stop_preview()
마찬가지로 영상으로 만들 때는
- ffmpeg -y -f image2 -i /home/pi/Desktop/frame%03d.jpg -r 24 -vcodec libx264 -profile high -preset slow /home/pi/Desktop/stop_motion.mp4
보안 촬영
가장 복잡한 예제. 영상에 움직임이 발견되면 마지막 10초 영상을 기록하는 예제입니다.
마지막 n 초의 영상을 기록하는 예제에 모션을 감지하는 루틴이 추가되면 되겠죠. 친절하게도 소스코드 저자가 모션 감지하는 부분은 직접 해보라고 비워뒀네요………
import io import random import picamera from PIL import Image prior_image = None def detect_motion(camera): global prior_image stream = io.BytesIO() camera.capture(stream, format='jpeg', use_video_port=True) stream.seek(0) if prior_image is None: prior_image = Image.open(stream) return False else: current_image = Image.open(stream) # Compare current_image to prior_image to detect motion. This is # left as an exercise for the reader! result = random.randint(0, 10) == 0 # Once motion detection is done, make the prior image the current prior_image = current_image return result def write_video(stream): # Write the entire content of the circular buffer to disk. No need to # lock the stream here as we're definitely not writing to it # simultaneously with io.open('before.h264', 'wb') as output: for frame in stream.frames: if frame.header: stream.seek(frame.position) break while True: buf = stream.read1() if not buf: break output.write(buf) # Wipe the circular stream once we're done stream.seek(0) stream.truncate() with picamera.PiCamera() as camera: camera.resolution = (1280, 720) stream = picamera.PiCameraCircularIO(camera, seconds=10) camera.start_recording(stream, format='h264') try: while True: camera.wait_recording(1) if detect_motion(camera): print('Motion detected!') # As soon as we detect motion, split the recording to # record the frames "after" motion camera.split_recording('after.h264') # Write the 10 seconds "before" motion to disk as well write_video(stream) # Wait until motion is no longer detected, then split # recording back to the in-memory circular buffer while detect_motion(camera): camera.wait_recording(1) print('Motion stopped!') camera.split_recording(stream) finally: camera.stop_recording()
모션을 감지하는 부분 – detect_motion() 은 직접 채워넣어야 제대로 동작합니다.
활용 방법
이정도 코드만 가지고도 다양하게 응용할 수 있습니다.
가장 먼저 생각나는게 라즈베리 파이 카메라 겠죠. 이미 Adafruit 에서 이런 코드를 응용하고 배터리 팩을 달아서 휴대용 카메라를 만든 프로젝트가 공개되어 있습니다.
https://learn.adafruit.com/diy-wifi-raspberry-pi-touch-cam/overview
혹은 CCTV, 보안 서버 등의 용도로 제작이 가능할겁니다. 상상력을 발휘해 보세요.
참고자료 :
- Picamera : http://picamera.readthedocs.org/en/release-1.2/index.html
- RPi Learning Resource : https://www.raspberrypi.org/learning/python-picamera-setup/worksheet.md