今回はRaspberry Piを使ってデジカメもどきを作ってみます。もどきと呼んだのは、デジカメサイズにパッケージングしていないからです。ディスプレイはPCモニタを使用します。シャッターボタンはタクトスイッチを使用し、押した瞬間のカメラ画像が保存される仕組みです。
▼完成し、プログラムを実行したときの動作がこちらになります。
タクトスイッチを押すとカメラ画像がフォルダに保存されます。このデジカメもどきの製作方法を解説していきます。
準備したもの
- Raspberry Pi
- Raspberry Pi用カメラモジュール
- PCモニタ
- ブレッドボード
- ジャンパワイヤ オス-メス
- タクトスイッチ
回路図
Raspberry Piの3.3V端子、GPIO 17とタクトスイッチを繋ぎます。今回、Raspberry Piの内部プルダウン抵抗を使うため、タクトスイッチ側にプルダウン抵抗は設けていません。
この他にHDMI端子にPCモニタを接続、カメラ端子にカメラモジュールを接続しています。
▼ブレッドボード図
▼回路図
▼実機の接続写真(PCモニタはまだ接続していません)
プログラム
サンプルプログラム
▼デジカメプログラムのサンプルはこちら
import RPi.GPIO as GPIO
import cv2 as cv
import datetime
import threading
global frame
def saveImg():
d = datetime.datetime.today()
filename = "IMG{0}{1:02d}{2:02d}_{3:02d}{4:02d}{5:02d}.jpg".format(d.year, d.month, d.day, d.hour, d.minute, d.second)
print("Save image")
cv.imshow(filename, frame) #撮影した画像をディスプレイに表示
cv.imwrite(filename, frame) #撮影した画像の保存
def shutter_callback(channel):
if channel==17:
t = threading.Thread(target=saveImg)
t.start()
print("SW ON")
cap = cv.VideoCapture(0)
GPIO.setmode(GPIO.BCM)
GPIO.setup(17, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)
GPIO.add_event_detect(17, GPIO.RISING, callback=shutter_callback, bouncetime=200)
try:
#カメラ画像をリアルタイムでディスプレイに表示
while True:
ret, frame = cap.read()
if not ret:
print("Can't receive frame")
break
cv.imshow("frame", frame)
k = cv.waitKey(1) & 0xFF
#「q」キーが押されたらプログラム終了
if k==ord('q'):
print("Exit")
break
except KeyboardInterrupt:
pass
cap.release()
GPIO.cleanup()
cv.destroyAllWindows()
プログラムの要点は以下のとおりです。
- タクトスイッチON/OFFを検知するためのGPIO設定
- カメラに映った画像をリアルタイムでディスプレイに表示
- タクトスイッチONのエッジを検出してカメラ画像を保存
タクトスイッチON/OFFを検知するためのGPIO設定
GPIO.setmode(GPIO.BCM)
GPIO.setmode()でGPIOの各端子指定方法をGPIO番号に指定します。引数を”GPIO.BOARD”にすれば物理端子番号指定となります。
GPIO.setup(17, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)
タクトスイッチON/OFFを検出出来るよう、GPIO.setup()メソッドでGPIO 17をInputに設定しています。第3引数でプルアップ・プルダウン抵抗を有効に設定できます。今回、プルダウン抵抗を配置していないので、pull_up_down引数にGPIO.PUD_DOWNを代入し、プルダウン抵抗を有効化しました。これによってスイッチON時は入力がHigh、OFF時はLowと検出されます。
GPIO.add_event_detect(17, GPIO.RISING, callback=shutter_callback, bouncetime=200)
GPIO.add_event_detect()メソッドは、GPIO入力のエッジを検出したときに、呼び出す関数を指定できます。第1引数でGPIO番号を指定。第2引数でアップエッジ、ダウンエッジどちらを検出するか指定します。第3引数は、呼び出す関数を指定します。第4引数は次のエッジ検出まで検出を無視する時間”bouncetime”を指定します。今回、200msを設定しました。
どうしてbouncetimeが必要なの?
カメラに映った画像をリアルタイムでディスプレイに表示
cap = cv.VideoCapture(0)
while True:
ret, frame = cap.read()
if not ret:
print("Can't receive frame")
break
cv.imshow("frame", frame)
k = cv.waitKey(1) & 0xFF
OpenCVを用いてカメラ画像をディスプレイに表示します。whileで無限ループさせることで動画としています。
タクトスイッチONのエッジを検出してカメラ画像を保存
def shutter_callback(channel):
if channel==17:
t = threading.Thread(target=saveImg)
t.start()
print("SW ON")
GPIO.add_event_detect()メソッドでshutter_callback関数を登録したので、タクトスイッチONでエッジ検出されたらshutter_callback関数が呼び出されます。カメラ画像のリアルタイムPCモニタ表示は継続したいので、新しくスレッドを立ち上げます。
threading.Thread()メソッドの引数に、実行する関数”saveImg”を指定してインスタンスを生成します。
t.start()メソッドでsaveImgを実行します。
def saveImg():
d = datetime.datetime.today()
filename = "IMG{0}{1:02d}{2:02d}_{3:02d}{4:02d}{5:02d}.jpg".format(d.year, d.month, d.day, d.hour, d.minute, d.second)
print("Save image")
cv.imshow(filename, frame) #撮影した画像をディスプレイに表示
cv.imwrite(filename, frame) #撮影した画像の保存
カメラ画像を保存するための関数を作成しました。この関数は、タクトスイッチが押されたときの時間をファイル名とし、カメラ画像をカレントフォルダに保存します。
プログラム実行結果
▼タクトスイッチを押す前です。フォルダに画像は保存されていません。
▼タクトスイッチを押した後です。押した瞬間のカメラ画像がフォルダに保存されました。
まとめ
以上、Raspberry Piを使ったデジカメもどきの製作解説でした。デジカメ製作の基本プログラムは出来ましたので、デジカメサイズに収まるハードウェアを設計すれば(これが一番難しい・・・)デジカメが出来上がります。機会があれば小型ディスプレイ購入、筐体を製作し、パッケージングしてみたいと思います。