現象
「Wavashare Jetbot AIカー」において、以下のようにカメラのインスタンスを生成しようとすると、
from jetbot import Camera
camera = Camera()
エラー : RuntimeError('Could not read image from camera.')
が表示される。
原因
1.他のプログラムでもカメラを使用している
→ Jupyterlab画面右の所から他のプログラムを終了しましょう。
2.前回使用時、カメラを正常に終了していない可能性がある
→ 以下で解説します。
解決法
カメラを終了する際に<Cameraのインスタンス>.stop()
を記述する。
from jetbot import Camera
camera = Camera()
camera.stop() # カメラの終了
実は...
Wavashare Jetbot AIカーの公式ドキュメントでは、NVIDIA公式ではなくWavashare独自のJetbotのgithubを使用してセットアップが行われますが、その中にあるNotebookのバージョンが古いものになっています。(NVIDIAの方では何回かNotebookが修正されているのですが、Wavashareの方は特に何もしていないようです)
NVIDIA公式githubの方では、プログラムの最後にcamera.stop()
が追加されていたりとコードが一部修正されていますので、Notebookに関してはこちらを参照した方が良いかと思います。
おまけ - camera.stop()
についての小話
JetRacerを解説しているサイトにて、カメラ終了時にcamera.cap.release()
を記述しないと次回読み込み時にエラーが起きるという記述を見ました。
camera
はインスタンス名です。
最初は特に気にせず、私も例に倣いプログラムの最後に付け加えていましたが、何故これで上手く行くのか気になったため、少し調べる事にしてみました。
差し当たっては、Camera
モジュールが提供している関数について知りたいと思い、NVIDIA公式が提供しているJetbotのgithubを覗いてみる事にしてみました。
上から2つ目にcamera
というディレクトリがあります。名前からしてここに書いてありそうです。
中身はこんな感じです。
見たところ、各ファイルが実装している機能は以下のような感じでした。
ファイル名 | 機能 |
---|---|
__init__.py |
カメラのセットアップ / importされる際の名前の指定 |
camera_base.py | カメラから受け取った画像の受け渡し |
opencv_gst_camera.py | カメラの開始、終了などを行う関数 |
zmq_camera.py | 同上 |
Jetbotで使用可能なカメラはいくつかありますが、その種類によって使用する関数が変わるようです。 __init__.py
で接続されているカメラの種類を判別し、それに合わせてopencv_gst_camera.py
かzmq_camera.py
を適用する...というとこまでは読み取れましたが、それ以上は分かりませんでした。
USBカメラにも対応しているとの事なので、zmq_camera.py
はそれ用なのでしょうか。
少し先回りした書き方になってしまいましたが、結論から言うと「Camera
モジュールが提供している関数」はopencv_gst_camera.py
に記述されていました。
実際に見てみましょう。
import traitlets
import atexit
import cv2
import threading
import numpy as np
from .camera_base import CameraBase
class OpenCvGstCamera(CameraBase):
value = traitlets.Any()
# config
width = traitlets.Integer(default_value=224).tag(config=True)
height = traitlets.Integer(default_value=224).tag(config=True)
fps = traitlets.Integer(default_value=30).tag(config=True)
capture_width = traitlets.Integer(default_value=816).tag(config=True)
capture_height = traitlets.Integer(default_value=616).tag(config=True)
def __init__(self, *args, **kwargs):
self.value = np.empty((self.height, self.width, 3), dtype=np.uint8)
super().__init__(self, *args, **kwargs)
try:
self.cap = cv2.VideoCapture(self._gst_str(), cv2.CAP_GSTREAMER)
re, image = self.cap.read()
if not re:
raise RuntimeError('Could not read image from camera.')
self.value = image
self.start()
except:
self.stop()
raise RuntimeError(
'Could not initialize camera. Please see error trace.')
atexit.register(self.stop)
def _capture_frames(self):
while True:
re, image = self.cap.read()
if re:
self.value = image
else:
break
def _gst_str(self):
return 'nvarguscamerasrc sensor-mode=3 ! video/x-raw(memory:NVMM), width=%d, height=%d, format=(string)NV12, framerate=(fraction)%d/1 ! nvvidconv ! video/x-raw, width=(int)%d, height=(int)%d, format=(string)BGRx ! videoconvert ! appsink' % (
self.capture_width, self.capture_height, self.fps, self.width, self.height)
def start(self):
if not self.cap.isOpened():
self.cap.open(self._gst_str(), cv2.CAP_GSTREAMER)
if not hasattr(self, 'thread') or not self.thread.isAlive():
self.thread = threading.Thread(target=self._capture_frames)
self.thread.start()
def stop(self):
if hasattr(self, 'cap'):
self.cap.release()
if hasattr(self, 'thread'):
self.thread.join()
def restart(self):
self.stop()
self.start()
@staticmethod
def instance(*args, **kwargs):
return OpenCvGstCamera(*args, **kwargs)
下の方にstop(self)
という関数があります。
更にその関数内には、self.cap.release()
と記述されています。
つまり、冒頭で紹介したJetRacerを解説しているサイトで見たcamera.cap.release()
の下りはこの部分から来ている、というオチでした。
参考