忍者ブログ

Fグループ電子工作講座

秋月電子SH7125ボードで始めるマイコン開発

ラズパイ5のカメラをlibcamerとOpenCV(C++)で動かす

まえおき

ラズパイ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 update
sudo apt upgrade

再起動
reboot

必要な環境を入れる
sudo apt install -y python3-pip git python3-jinja2
pipは仮想環境でなければ使えないので要らないかも

ライブラリを入れる
sudo apt install -y libboost-dev
sudo apt install -y libgnutls28-dev openssl libtiff-dev pybind11-dev
sudo apt install -y qtbase5-dev libqt5core5a libqt5widgets5
sudo apt install -y meson cmake
sudo apt install -y python3-yaml python3-ply
sudo apt install -y libglib2.0-dev libgstreamer-plugins-base1.0-dev
※libqt5widgetsからlibqt5widgets5へ名称が変わった?そもそも要るのか不明。

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 build
cd build

cmakeで設定してビルド
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キーで手動フォーカスの設定ができるようです。
手持ちのカメラが固定焦点なので試せていません。

メインプログラムをちょっと修正

このプログラムではflagtrueになるまでcam.readFramecontinueが繰り返されます。
で、これはひたすら空ループを繰り返している状態なのでCPU1コア分の使用率が100%となって
しまいます。(4コアなので25%表示)
無駄に空ループさせると処理が重いのでflagtrueではない時に即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で動かすこともできそうです。

ちなみにドルフラングレンは細かい検出条件にしてもあまり検出されません。

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

コメント

プロフィール

HN:
ぼんどF博士
性別:
男性
自己紹介:

最新コメント