まえおき
ラズパイ4などBullseyeまではraspi-config等でカメラを有効にすることで
v4l2経由で専用カメラ(MIPI CSI)を使うことができていました。
ところがBookwormからカメラの設定項目が消えました。BusterまではOS側でカメラの
ドライバを管理してたのが順次libcamer(およびrpicam)へ移管する様です。
参考:えいあーるれいの技術日記様
Raspi-Bullseyeでpicameraを検出するプログラムを作成するさて、ラズパイ5でもOpenCV(C++)で画像処理を行いたいのですがラズパイ5は
Bookworm以降を使うので旧手段ではカメラを読み出せません。
PythonであればPicamera2で読み出せますがC++には対応していない様です。
そこで、libcamerを使ってOpenCV(C++)から専用カメラを読み出す環境を設定します。
libcameraのv4l2互換機能を使うとメインプログラムをそのまま使えるっぽいのですが、
未だ使い方を理解していないのでまずはlibcamerを直接使用する方法を試してみます。
※C++向けにOpenCVの環境が設定でき、
C++でビルドできたものとします。
libcamera-hello
とりあえず素の状態でカメラを読めるか確認しておきます。
カメラが
OV5647(Zero向けのスパイカメラ)の場合は特に何もしなくても
libcamera-helloを実行するだけで表示できると思います(たぶん)。また、
libcamera-hello --list-camerasにて、カメラの情報が取得できると思います。
OV5647を検出できない場合はconfig.txtの
camera_auto_detect=0で自動検出を
停止しているとか
dtoverlayで違う機種をしているとかの可能性があります。
自動検出を停止している場合でも
dtoverlay=ov5647であれば読み出せると思います。
その他の
IMX219などを接続した場合は
ERROR: *** no cameras available ***とか
No camera available!とか出てきてカメラを認識していません。
IMX219を使う場合は
boot/firmware/config.txtへ
dtoverlay=imx219を追記します。config.txtは管理者権限でないと編集できないので
sudo pcmanfmで管理者権限でファイルマネージャーを起動するなど対応してください。
再起動後に再度
libcamera-hello
で映像が表示できればOKです。
なお、bullseyeで使えた
vcgencmd get_cameraはbookwormでは機能しません。
vcgencmdを実行するとvcgencmd commandsでコマンド一覧を取得できるとの説明が出るので
vcgencmd commandsで確認するとget_camera引数がリストに出てこないので対応していない様です。
Bookwormのboot/firmware/config.txt の挙動について
camera_auto_detect:標準設定のカメラ(OV5647)を自動検出する
標準カメラov5647はcamera_auto_detect=1で
検出される標準外カメラimx219はcamera_auto_detect=1で
検出されないBusterでは何方でも自動検出できたのでOS等の仕様違いに注意。
dtoverlay:カメラの設定を上書きする
ov5647/imx219共にdtoverlayで指定できる。
dtoverlayを複数回記述した場合は一番最後に記述したものが適用される。
ov5647が接続された状態で
camera_auto_detect=1
dtoverlay=
imx219とすると自動検出で
ov5647を検出できるはずのところ、
imx219で上書きされるので
正しく検出できません。
複数のカメラを個別に指定する場合は ポート0にimx219、ポート1にov5647を接続した例では
camera_auto_detect=1
dtoverlay=imx219,cam0
または
dtoverlay=imx219,cam0
dtoverlay=ov5647,cam1
指定すると検出できます。
dtoverlay=imx219,cam1
dtoverlay=ov5647,cam0
と逆に指定しまうと検出できません。
start_x:Bookwormでカメラに何の影響が出るか確認できず。
libcameraのビルド
素の状態だとlibcameraは使えるものの、ライブラリ
libcamea.soが使えないので
自分でビルドします。
公式と参考サイト様の手順に従ってビルドします。
公式
https://github.com/raspberrypi/libcamera参考:Zenn@hattori-sat様
libcameraの設定とりあえずラズパイを更新しておく
sudo apt updatesudo apt upgrade再起動
reboot必要な環境を入れる
sudo apt install -y python3-pip git python3-jinja2pipは仮想環境でなければ使えないので要らないかも
ライブラリを入れる
sudo apt install -y libboost-devsudo apt install -y libgnutls28-dev openssl libtiff-dev pybind11-devsudo apt install -y qtbase5-dev libqt5core5a libqt5widgets5sudo apt install -y meson cmakesudo apt install -y python3-yaml python3-plysudo apt install -y libglib2.0-dev libgstreamer-plugins-base1.0-dev※libqt5widgetsからlibqt5widgets
5へ名称が変わった?そもそも要るのか不明。
libcamera本体をダウンロードする
git clone https://github.com/raspberrypi/libcamera.git※zipでダウンロードしてunzipで展開してもOK
ダウンロードしたディレクトリへ移動
cd libcameraビルド環境を設定(cmakeみたいなやつ)
meson setup build --buildtype=release -Dpipelines=rpi/vc4,rpi/pisp -Dipas=rpi/vc4,rpi/pisp -Dv4l2=true -Dgstreamer=enabled -Dtest=false -Dlc-compliance=disabled -Dcam=disabled -Dqcam=disabled -Ddocumentation=disabled -Dpycamera=enabledビルド(ninjaはmakeの高速っぽいやつ)+インストール
ninja -C build installインストール時にパスワードを求められる
ラズパイを実行中のユーザーのパスワードを入力
とりあえずインストールできました。
pkg-configコマンドでライブラリのインストール先を確認できます。
ラズパイ5(bookworm)だとインクルードフォルダとライブラリが
pkg-config --cflags libcamera-I/usr/local/include/libcamera pkg-config --libs libcamera-L/usr/local/lib/aarch64-linux-gnu -lcamera -lcamera-baseとなっていました。
本記事を作成した時点では
libcamera.so.0.4.0が生成されています。
ここでもうひと手間この状態だとライブラリがフォルダに格納されパスが通っていますが、
追加したライブラリを認識できていない様です。
ビルドでエラーが出たり、ビルドできたとしても実行時にエラーになります。
参考:Qiita@hachicomb様
共通ライブラリがみつからない(cannot open shared object file)エラー対処方法自作プログラムをビルド時
cannot open shared object file
実行時
symbol lookup error: /usr/local/lib/aarch64-linux-gnu/libcamera.so.0.4:
undefined symbol: _ZN7libpisp22compute_optimal_strideER24pisp_image_format_config
この症状が出たら、まずパスが通っているか確認しておきます。
cat /etc/ld.so.conf.d/*/usr/local/lib/aarch64-linux-gnu先ほどのディレクトリが出てくるのでパスは通っている様です。
パスが通っているのにライブラリが読めないときはOpenCVをインストールした時と同じく
sudo ldconfigを実行すればライブラリが使えるようになります。
念のため再起動
rebootこれで準備が整いました。
libcameraを使う
libcamera公式サンプルだと"event_loop.h"なるものをインクルードしておりなんだかよくわかりません。
調べていると分かりやすいサンプルを公開している方が居ました。
https://github.com/edward-ardu/libcamera-cpp-demo/この方のプログラムではlibcameraの設定項目をまとめた
LibCamera.cpp / LibCamera.hとメインプログラムの
main.cppに分かれています。
main.cppの中身を見るとOpenCVで処理するひな型になっているのでこれを利用させて頂きます。
とりあえず手順通りにビルドしてみます。
git cloneなり、zipファイルでダウンロードするなりして
git clone https://github.com/edward-ardu/libcamera-cpp-demo.gitフォルダの中に
・CMakeLists.txt
・LibCamera.cpp
・LibCamera.h
・main.cpp
がある状態にします。
フォルダ移動して
cd libcamera-cpp-demoビルドフォルダを作って移動
mkdir buildcd buildcmakeで設定してビルド
cmake ..make多分このままだとエラーでビルドが通りません。
error: 'class Libcamera::Camera Configuertion' has no member named 'transform'
このプログラムが公開されてからlibcameraの仕様が変わってしまった様です。
エラー箇所を見ると
config_->transform = transform;となっており、取得時に画像を回転させる処理っぽいですがOpenCV側でも対応できるので
とりあえず、この行をコメントアウトします。
//config_->transform = transform;これでビルドが通る様になりました。
ビルドが通るとビルドフォルダに
libcamera-demoが生成されているはずです。
./libcamera-demoで実行できれば成功です。
qキーでプログラムを終了します。
カメラの表示範囲がおかしい場合はウインドウを閉じれば表示されます。
ちなみに、pkg-configが設定されているのでmain.cppのあるlibcamera-cpp-demoフォルダで
g++ main.cpp -o main.out LibCamera.cpp `pkg-config --cflags --libs opencv4 libcamera`とか
コマンドを直接打ってもビルドできます。
Makefileを作っておけばGeanyとかからもMake(ビルド)できます。
サンプルプログラムの中身
main.cppを見てみます。
<カメラ初期設定>必須
カメラを起動※カメラ0固定
cam.initCamera();解像度、フォーマット、バッファ数、回転 を設定
cam.configureStill(width, height, formats::RGB888, 1, 0);この項目は設定しないと動きませんでした。
<カメラパラメータ設定>オプション
FrameDurationLimits:FPS相当、マイクロ秒
Brightness:明るさ
Contrast:コントラスト
ExposureTime:露光
の値を決めて
cam.set(controls_);にて設定しています。
これら項目は設定しなくても動作しました。
設定しない場合はデフォルト値が適用されるようです。
<キャプチャー処理>必須
カメラ開始
cam.startCamera();動画設定?
cam.VideoStream(&width, &height, &stride);生データ取得
flag = cam.readFrame(&frameData);OpenCVで使える配列へ変換
Mat im(height, width, CV_8UC3, frameData.imageData, stride);フレームへのアクセス終了?
cam.returnFrameBuffer(frameData);<カメラ焦点設定>オプション
fキーでオートフォーカス
A/Dキーで手動フォーカスの設定ができるようです。
手持ちのカメラが固定焦点なので試せていません。
メインプログラムをちょっと修正
このプログラムでは
flagが
trueになるまで
cam.readFrameと
continueが繰り返されます。
で、これはひたすら空ループを繰り返している状態なので
CPU1コア分の使用率が100%となって
しまいます。(4コアなので25%表示)
無駄に空ループさせると処理が重いので
flagが
trueではない時に即
continueするのではなく
if (!flag){ usleep(10); continue;}の様に
10マイクロ秒待機するだけでもCPU使用率が大幅に軽減されます。
fpsを追い求めないのであれば
100マイクロ秒ぐらいで良いでしょう。
LibCamera.cpp / .h を編集する
カメラを指定できるように改造
int LibCamera::initCamera()を実行すると
cameraId = cm->cameras()[0]->id();にて
カメラ0にアクセスする仕様になっています。
これを
int LibCamera::initCamera(int index)および
cameraId = cm->cameras()[index]->id();としておけばカメラIDを指定することができます。
※
id指定バージョンのコミットもある?
ただし、同一プログラムから2つのカメラへアクセスしようとするとエラーになります。
プログラムを2つ起動してそれぞれアクセスする場合は同時でも動きます。
要検討。
使っていないインクルードを除外
LibCamera.hにてヘッダファイルを沢山インクルードしていますが、仕様が変わったためか
インクルード不要なものもあります。
インクルードから除外する必要はとくにありませんが、整理してみます。
必要な物@libcamera.so.0.4.0(太字のみ)
//#include <atomic>//#include <iomanip>//#include <iostream>//#include <signal.h>#include <limits.h>//#include <memory>//#include <stdint.h>//#include <string>//#include <vector>//#include <unordered_map>#include <queue>//#include <sstream>#include <sys/mman.h>//#include <unistd.h>//#include <time.h>#include <mutex>//#include <libcamera/controls.h>//#include <libcamera/control_ids.h>//#include <libcamera/property_ids.h>#include <libcamera/libcamera.h>//#include <libcamera/camera.h>//#include <libcamera/camera_manager.h>//#include <libcamera/framebuffer_allocator.h>//#include <libcamera/request.h>//#include <libcamera/stream.h>//#include <libcamera/formats.h>//#include <libcamera/transform.h>unistd.hはusleepに
time.hはメイン関数(FPSの計算のみ)で使用しているので、
main.cppでインクルードするのが良いでしょう。
顔検出
恒例の顔検出をしておきます。
諸注意
using namespace cv;
using namespace libcamera;
でそれぞれ
cv::と
libcamera::を省略できるのですが、SizeとPointはそれぞれ
cv::Size、
cv::Point
libcamera::Size、
libcamera::Point
と両方に定義があり、
cv::を省略すると区別がつかなくなるため
error :reference to 'Size' is ambiguous等エラーが出ます。
OpenCVで使うSizeとPointはそれぞれ
cv::Size、
cv::Pointと記述する必要があります。
実行結果
いつもの検出条件
・640×480の倍率2 → 320x240
・スケール変化率1.3
・重複数4
にて実行してみます。
カメラのfps上限は限界確認のため
frame_time = 1000000 / 240;
としてみます。
ラズパイ5の性能が発揮され検出時間が10msを切りました

この状態でFPSも60fps以上出ています。
なお、IMX219、キャプチャのみ、その他処理なし、表示なし だと205fpsぐらいまで
出ており、IMX219の上限値206.65fpsで動かすこともできそうです。
ちなみにドルフラングレンは細かい検出条件にしてもあまり検出されません。

斜め向いてたり、上半身全体だったり、影がきつかったり で検出し辛い様です。
ヴァンダムも同じ?