やったこと
SWITCH SCIENCE が安売りしているOV2640搭載 Unit Cam Wi-Fi Cameraを購入した。
モジュールの発売日は 2021-06-24 で少し古い。
この Unit Cam を ラズパイ4 のシリアル接続のカメラにできたのでメモする。
まず、最初に入っているファームウェアの動作を確認するため先人の知恵を調べた。
Lang-shipさんのM5Stack Unit CAM(U109) に怖いことが書かれている。(私もファームウェアを見つけられなかった。)
最初に入っているファームウエアは垂れ流し系みたいですね。
さて、書き換える前にどんなファームウエアかなと思ったら、、、ファームウエア未公開です。
バイナリでも公開していないので、書き換えるともとの状態に戻せません!!!
Qiitaの@taiyyytaiさんのM5stackCore2+Unit Camで画像を取得する を読むと M5Stack Core2 で表示可能らしい。
私は M5Stack Core* を持たないので python で Linux(ラズパイ4) と Unit Cam をシリアル接続した。
その結果、最初に入っているファームウェアで640x480の動画が乱れることなく撮影できた。
使ったもの
- Unit Cam
- Raspberry Pi 4 4GB
- GROVEケーブル
- M5Stack ダウンローダー
Unit Cam と ラズパイ4の接続
ラズパイ4のUSBケーブルを M5Stack ダウンローダに接続、さらに GROVEケーブルで Unit Cam に接続した。
M5Stack ダウンローダーと GROVE ケーブルの接続は下記である。
ダウンローダー | ケーブル色 |
---|---|
GND | 黒 |
G0 | -- |
EN | -- |
TX0 | 白 |
RX0 | 黄 |
3.3V | 赤 |
Raspberry Pi 4 の設定
Raspberry Pi 4 は Raspberry Pi OS 64 である。
OpenCV を使うので X Window を起動する。
pip で opencv-python と opencv-contrib-python をインストールする。
スクリプト Cam2RPi.py の作成
SWITCH SCIENCE の販売ページの Arduinoサンプルコードを python スクリプトに変換した。
変更点は2点
- JPEGフレームの処理を加え
- 割り込みでなくループ内で表示した
import serial
import threading
import queue
from PIL import Image
import io
import cv2 # OpenCV
import numpy as np
import time
# UART の設定(適切なポートに変更してください)
ser = serial.Serial('/dev/ttyACM0', baudrate=1500000, timeout=1)
# フレーム用のキュー(最大 2 フレーム)
frame_queue = queue.Queue(maxsize=2)
def uart_read_thread():
"""
シリアルからデータを読み込み、JPEGフレームの開始・終了を検出してキューに格納するスレッド関数
JPEGは 0xFFD8 で開始し、0xFFD9 で終了することを利用する。
"""
buffer = bytearray()
while True:
data = ser.read(1024)
if data:
buffer.extend(data)
while True:
# JPEGの開始マーカー(0xFFD8)を検出
start_index = buffer.find(b'\xff\xd8')
if start_index == -1:
# 開始マーカーがない場合は、後続データのためにバッファを保持
break
# JPEGの終了マーカー(0xFFD9)を開始マーカー以降から検出
end_index = buffer.find(b'\xff\xd9', start_index + 2)
if end_index == -1:
# 終了マーカーがまだ見つからない場合、フレームは未完成
break
# 終了マーカーを含めるためにインデックスを調整
end_index += 2
# 完全なJPEGフレームを切り出し
frame_data = buffer[start_index:end_index]
# バッファから処理済み部分を削除
buffer = buffer[end_index:]
# キューにフレームを送信(キューが満杯なら破棄)
try:
frame_queue.put(frame_data, block=False)
except queue.Full:
pass
def display_frame_thread():
"""
キューからJPEGフレームを取得し、デコードして表示するスレッド関数
"""
while True:
frame_data = frame_queue.get() # ブロッキングで取得
try:
# JPEGデータをPillowで読み込み
image = Image.open(io.BytesIO(frame_data))
image.load() # 画像の完全な読み込みを強制
except Exception as e:
print("画像デコードエラー:", e)
continue
# Pillow画像をOpenCV形式(BGR)に変換して表示
image_cv = cv2.cvtColor(np.array(image), cv2.COLOR_RGB2BGR)
cv2.imshow("UART JPEG Frame", image_cv)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
# スレッドの開始
uart_thread = threading.Thread(target=uart_read_thread, daemon=True)
# display_thread = threading.Thread(target=display_frame_thread, daemon=True)
uart_thread.start()
# display_thread.start()
# メインループを維持
try:
while True:
display_frame_thread()
time.sleep(0.03)
pass
except KeyboardInterrupt:
print("終了します")
cv2.destroyAllWindows()
実行
ラズパイ4の X Window のターミナルで python Cam2RPi.py
を実行する。
640x480の動画が乱れることなく撮影できた。
最後に
今回はじめてシリアルを python で受信、OpenCV で jpeg を表示した。
Lang-shipさん と Qiitaの@taiyyytaiさん のように誰かのヒントになれば幸いです。