Ryuz's tech blog

FPGAなどの技術ブログ

ZYBO-Z7 で Raspberry Pi Camera Module V2 (Sony IMX219) を 1000fpsで使うサンプル (復刻記事)

おしらせ

本記事は以前Qrunchで書いていた記事の復刻です。

はじめに

基本的には下記のブログ記事の続きなのですが、QrunchがMarkdownで書けて書きやすいのでこちらに書かせていただきます。

拙作の件の1000fpsで動くDeep Neural Networkで使っているものです。

本来、Ultra96V2 でいろいろやりたかったのですが、KiCADで描いて発注したボードが新型コロナ影響もありなかなか来ないのでまずはZYBOでのトライです。

サンプルの公開

サンプルプログラムをこちらに公開いたしました(というか公開はずっとしてたのですが、Debianイメージ用とすることで、やっと他でも再現可能な環境になりました)。

今回はこちらの Debianブートイメージ 上で動かすのに必要なものを一式 github に入れる事が出来ましたので、少しは展開性も良くなったのではないかと思います。 動かし方については、github の方に記載しております。 環境についてはこちらの記事なども参考にしてください。

無事に動作するとこのような感じで、X-Window経由でトラックバーでいろいろな操作が可能です。

センサー情報

このブログを読まれている方はの多くはサンプルプログラムを動かしたいわけではなく、それを参考にカメラを使った回路やプログラムが作りたい方々だと思います。 したがってまずは情報源を記載しておきます。

RaspberryPI というすばらしい製品のおかげで様々な情報がそろっており、開発に十分な情報が開示されております。

クロックの設定

内部の設定として特にクロック設定が少しトリッキーですので解説しておきます。 下図で青枠の設定が、私が RaspberryPI でカメラを動かしたときにI2Cをロジアナでログ取った時になされていた設定です。この設定で普通は問題なく使えますが、1000fps をやるときは、センサークロックを定格いっぱいまで高めるために赤枠の設定にしており、センサーを139.2MHz (2.784Gbps読出し)で駆動しています。

139.2MHzなどの設定は本来MIPIを4レーンで利用するときの設定で、2レーン(1.824Gbps)では転送しきれません。 が、Sonyのセンサーはとてもよくできているようで、1ラインの読出し範囲を減らしておくとFIFOで吸収して水平ブランキング期間で追い付いてくれるようで、破綻せずに読み出しができるようです。 センサーが139.2MHzだと、ライン数を132まで減らすと1000fpsに達することができます。

当方のDMAなどの都合上現在は水平ラインは16の倍数の縛りがあるので現在は 640x132 の設定を試しています。

IMX219の設定

IMX219は基本的にはI2C経由で行います。 概ねのところは Imx219Control.hに記載しています。

  • ピクセルクロックの設定(現状はまだ91MHzと139.2MHzの2種)
  • 画像サイズ/露光時間/フレームレートの設定
  • フリップの設定(上下/左右)の設定
  • アナログゲイン/デジタルゲインの設定

など、おおむね対応する名称の関数を追ってもらえばわかるように書いたつもりです。 一番複雑なのが「画像サイズ/露光時間/フレームレートの設定」かと思います。ローリングシャッターカメラですので読み出す速度=走査速度でして、これに間に合う形で走査しながらシャッターを開けていく(内部的には電荷リセット)ことで露光時間調整ができますが、ピクセルクロック速度と読み出すライン数に応じて設定可能範囲が変動します。

1ライン時間(LINE_LENGTH_A:0x0162番地)は読み出すピクセル数にかかわらず、ピクセルクロックで 3448サイクル固定です。 したがって2種ほど設定を書いておきますが下記のような設定が可能です。

設定 pixel clock[MHz] FRM_LENGTH line time[us] frame time[ms] frame rate[fps]
640x132@1000fps(binningあり) 139.2 80 12.39 0.99 1009.28
3280x2464@20fps 91.0 2728 18.95 51.68 19.35

主に FRM_LENGTH_A(0x0160番地) がフレームレートを決定しますが、この値はブランキング時間を含める必要があります(最低量はよく調べておらず実験して決めてます)。またIMX219の場合特殊モードがありアナログビニングを掛けると1ライン時間で倍のラインを読み出せます。

露光時間を決める COARSE_INTEGRATION_TIME_A(0x015A番地)は、FRM_LENGTH より4以上小さくなくてはいけないようですが、フレーム時間の範囲で調整できます。

読出し範囲は X_ADD_STA_A, X_ADD_END_A, Y_ADD_STA_A, Y_ADD_END_A, X_OUTPUT_SIZE, Y_OUTPUT_SIZE などのレジスタで決まりますが、こちらはビニング時の2倍換算での扱いだけ気を付けておけばそれほど難しくはないと思います。

PL側の構成

PL側はまず受信部分ですが、D-PHY に関しては Xilinx の MIPI D-PHY コアが無償で利用できます。非常に助かりました。 下記のような設定で受信できます。

次に、D-PHYで受けた後のデータです。RAW10 形式で受信するようにしていますが、2レーンからくるデータを並び替えて画像にする必要があります。 IMX219にはテストパターンを出す機能もあるので、ぶっちゃけロジアナ埋めて波形眺めていれば概ねのルールは把握できます(笑)。

あとネットを探すとMIPI-CSIの仕様もドラフト版であれば存在するようです(こことか)。

MIPI自体はバイト単位のパケットできますので、10bitのRAWの並べ替えが必要ですが、まあこのあたりはFPGA処理の醍醐味と言ったところで決め打ちで書いてしまえば意外とサクッと処理できます。 一応コードはこのへんです。 受信時点ではバイトパケットとしては 228MByte/s ですが、10bit RAW のピクセルクロック換算では 182.4Mpixel/s ですので、少し頑張ってRTL書けば 1pixel/cycle で処理できます。

ちなみにちゃんと現像処理しようとすると、デモザイクとかカラーマトリックスとかガンマ補正とか、さらには傷補正とか、固定パタンノイズ除去とか、シェーディング補正とか、色収差とか言い出すときりがないのですが、最低限デモザイクすればRGB画像としては視認できます。 もともとAIに流し込むことしか考えてなかったので、この辺りの処理はあまり考えていなかったのと、読み出してしまえば一旦は OpenCV で現像という手もあるのですが、折角FPGAの得意な部分でもあるので、今のところデモザイクとカラーマトリックスの実装だけしております。

ちなみにデモザイクには米国特許が期限切れとなってるはずのACPI法というのを入れております。これも画像でガウシアンフィルタとかラプラシアンフィルタとかでよく使う3x3とか5x5のフィルタの延長で書けて、とてもFPGAの良さが出せるアルゴリズムかと思います。 (もっとも「AIで使う場合にこういう非線形な処理を手前に入れるのはどうなんだ?」というのはありますが。むしろ正規化とか入れるべきかもしれません)。

おわりに

数年越しの記事の進展となりましたが、FPGAでMIPIカメラ弄りたいという人の参考になれば幸いです。