この記事について
今回は以下をやります。
- OpenCVを使えるようなLinuxイメージを作る
- rootfsをSDカードに変更する
- JPEG画像をカラーからグレースケールに変換するアプリケーションを作る
本当は、OpenCV関数をハードウェア化してくれるxfOpenCVとかいうのをやりたかったのですが、上手くいかなかったです。今回はその途中経過ということで、まずはCPUコードでOpenCVを使う方法を本記事に記します。xfOpenCVを使うにはreVISIONとかにも手を出さないとダメっぽくて、まだまだハードルは高かったです。。。
本記事の内容は、SDSoCだけではなく、通常通りPetaLinuxでlinuxイメージを作って、Xilinx SDK(XSDK)でLinuxアプリケーションを開発するときにも使えます。
rootfsの場所について
以前別の記事でも記しましたが、PetaLinuxでLinuxイメージやブートローダを作成すると、デフォルトではrootfsはRAMに展開されます(initramfs)。これはこれで、電源をいつでも切れるとか、毎回cleanな環境で動かせるといったメリットはあるのですが、rootfsのサイズが増加してくるとその分RAMサイズを圧迫したり、起動のたびに必要なファイルをコピーする必要があるといったデメリットがあります。
今回、OpenCVライブラリを取り込むので、rootfsサイズはかなり増加します。素の状態だと、rootfsは16MByteくらいだったのが100MByteくらいになります(ext4イメージファイルサイズで500MByteくらい)。そのため、rootfsをSDカードに展開することにしました。
環境
- 開発用PC: Windows 10 64-bit
- SDx IDE 2018.2
- 開発用PC (Linux): Ubuntu 16.04 本家 (日本語版じゃない) (on VirtualBox 5.2.4)
- PetaLinux 2018.2
- ターゲットボード: ZYBO (Z7-20)
ハードウェアは第1回目 に作成したhdfとdsaを使います。
- design_1.hdf
- design_1.dsa
Linuxイメージを作る
design_1.hdfを、PetaLinxuインストール済みのUbuntuにコピーしておきます。
プロジェクト作成からコンフィギュレーション、ビルドまでの全コマンドを以下に記します。(各コンフィギュレーションの設定は後述します)
ついでに、作成したイメージをまとめるコマンドも、まずは全部記してしまいます。
僕の場合、~/Desktop/win_share/
がwindowsとubuntuの共有ディレクトリなので、そこを介して成果物をやり取りします。
# プロジェクトを作る
petalinux-create --type project --template zynq --name design_1_cv
cd design_1_cv/
# コンフィギュレーション
petalinux-config --get-hw-description=~/Desktop/win_share/
petalinux-config -c kernel
petalinux-config -c rootfs
# デバイスツリーの設定
code project-spec/meta-user/recipes-bsp/device-tree/files/system-user.dtsi &
# ビルド
petalinux-build
# SDSoC用に生成物をまとめる(非SDSoCの場合不要)
mkdir -p output/boot
mkdir -p output/image
code output/boot.bif
cp images/linux/u-boot.elf output/boot/.
cp images/linux/*fsbl.elf output/boot/fsbl.elf
cp images/linux/image.ub output/image/.
cp -rf output ~/Desktop/win_share/
# sysrootを生成して固める(Linuxアプリケーション開発用)
petalinux-build --sdk
petalinux-package --sysroot
zip -r sysroot.zip images/linux/sdk/sysroots/cortexa9hf-neon-xilinx-linux-gnueabi/
mv sysroot.zip ~/Desktop/win_share/
# Linux image領域を書き込む(パーティション生成後に実行)
sudo umount /dev/sdb2
sudo dd if=images/linux/rootfs.ext4 of=/dev/sdb2 bs=4096
sync
sudo resize2fs /dev/sdb2
sync
boot loaderの設定
petalinux-config --get-hw-description=~/Desktop/win_share/
実行時に現れるコンフィグ画面で、ROOTFSをSDカードにするよう設定します。
Image Packaging Configuration → Root filesystem type
で、SD cardを指定します。その下のDevice node of SD device
には自動的に/dev/mmcblk0p2
が設定されているはずです。
project-spec/configs/config
ファイルの中身が以下のようになっていればOKです。
CONFIG_SUBSYSTEM_ROOTFS_SD=y
CONFIG_SUBSYSTEM_BOOTARGS_GENERATED="console=ttyPS0,115200 earlyprintk root=/dev/mmcblk0p2 rw rootwait"
なお、以前、SDSoC用にはKernel Bootargs(DTG Settings → Kernel Bootargs
)の編集も必要と書きました。xilinxのドキュメントにもそう書いてあったのですが、そちらは編集不要でgenerate boot args automatically
を有効にしたままで大丈夫でした。(console=ttyPS0,115200 earlyprintk root=/dev/mmcblk0p2 rw rootwait
)
もしかしたら、rootfstype=ext4
といった一文を追加しておいた方がいいかもしれません。
kernelの設定
petalinux-config -c kernel
実行時に現れるコンフィグ画面で、SDSoCに必要なXilinx APFと、OpenCVに必要そうなv4l2やUSB_VIDEO_CLASSを有効にします。
設定画面が出たら、スラッシュ(/
)キーで検索モードに入って、下記の、例えばCONFIG_CMA_SIZE_MBYTES等を入力してEnterキーを押すと、簡単に設定項目に行けます。
注意点として、有効にするときには、<M>
ではなく、<*>
になるようにしてください。
project-spec/meta-user/recipes-kernel/linux/linux-xlnx
以下に作られるuser_20xx-xx-xx-xx-xx.cfg
ファイルの中身が以下のようになっていればOKです。カーネルコンフィグに関しては、ここに出力されるcfgファイルを日付順にマージして使用されるようです。
CONFIG_CMA_SIZE_MBYTES=256
CONFIG_STAGING=y
CONFIG_XILINX_APF=y
CONFIG_XILINX_DMA_APF=y
CONFIG_MEDIA_USB_SUPPORT=y
CONFIG_USB_VIDEO_CLASS=y
CONFIG_USB_VIDEO_CLASS_INPUT_EVDEV=y
CONFIG_USB_GSPCA=y
CONFIG_VIDEOBUF2_VMALLOC=y
★重要★ SDSoC用ではない場合
SDSoC用にLinuxカーネルを作るときは、上述のXilinx APF(Xilinx APF, CONFIG_XILINX_APF, CONFIG_XILINX_DMA_APF)が必要になります。
しかし、これを付けてしまうと、BOOT.binはSDSoCで作らないと起動が出来なくなってしまいます。
SDSoCを使わずにPetaLinuxでBOOT.binを作る場合には、Xilinx APFは無効のままにしてください。なお、この場合は、hdfにはビットストリームファイルが含まれている必要があります。
rootfsの設定
petalinux-config -c rootfs
実行時に現れるコンフィグ画面で、必要なパッケージをインストールします。今回はOpenCVとそれに必要なライブラリ(jpeg等)、ついでにPython3をインストールします。また、SDSoCに必要なlibstdc++もインストールします。自分で地道に1つづつ設定してもいいのですが、kernelと同様に、スラッシュ(/
)キーで設定項目を検索するのが楽だと思います。
設定結果はproject-spec/configs/rootfs_config
に書かれます。面倒なら、rootfs_configファイルに下記設定をコピペしてしまってもOKです。
なお、下記設定はいろいろと冗長なところがあると思います。適当に設定項目を眺めて必要そうなのを有効にしただけなので。また、僕が未だに-dev
有無の違いとどういうときに必要になるか分かっていないので、とりあえず全部取り込んでみました。
CONFIG_dbus-glib=y
CONFIG_dbus-glib-dev=y
CONFIG_mtd-utils=y
CONFIG_xz=y
CONFIG_liblzma=y
CONFIG_canutils=y
CONFIG_openssh-sftp-server=y
CONFIG_pciutils=y
CONFIG_run-postinsts=y
CONFIG_libjpeg-turbo=y
CONFIG_libturbojpeg=y
CONFIG_jpeg-tools=y
CONFIG_libjpeg-turbo-dev=y
CONFIG_libpng=y
CONFIG_libpng-dev=y
CONFIG_libtool=y
CONFIG_libltdl=y
CONFIG_libwebp=y
CONFIG_libwebp-bin=y
CONFIG_libwebp-dev=y
CONFIG_opencv=y
CONFIG_opencv-dev=y
CONFIG_opencv-apps=y
CONFIG_udev-extraconf=y
CONFIG_libstdcPLUSPLUS=y
CONFIG_glib-2.0=y
CONFIG_glib-2.0-dev=y
CONFIG_packagegroup-core-boot=y
CONFIG_packagegroup-core-ssh-dropbear=y
CONFIG_python3=y
CONFIG_python3-smtpd=y
CONFIG_python3-syslog=y
CONFIG_python3-subprocess=y
CONFIG_python3-pickle=y
CONFIG_python3-db=y
CONFIG_python3-fcntl=y
CONFIG_python3-html=y
CONFIG_python3-re=y
CONFIG_python3-core=y
CONFIG_python3-distutils=y
CONFIG_python3-man=y
CONFIG_python3-sqlite3-tests=y
CONFIG_python3-terminal=y
CONFIG_python3-pprint=y
CONFIG_python3-tkinter=y
CONFIG_python3-unixadmin=y
CONFIG_python3-mime=y
CONFIG_python3-logging=y
CONFIG_python3-signal=y
CONFIG_python3-resource=y
CONFIG_python3-email=y
CONFIG_python3-math=y
CONFIG_python3-json=y
CONFIG_python3-image=y
CONFIG_python3-stringold=y
CONFIG_python3-pydoc=y
CONFIG_python3-codecs=y
CONFIG_python3-debugger=y
CONFIG_python3-selectors=y
CONFIG_python3-xmlrpc=y
CONFIG_python3-io=y
CONFIG_python3-pkgutil=y
CONFIG_python3-idle=y
CONFIG_python3-lang=y
CONFIG_python3-readline=y
CONFIG_python3-reprlib=y
CONFIG_python3-difflib=y
CONFIG_python3-unittest=y
CONFIG_python3-netserver=y
CONFIG_python3-netclient=y
CONFIG_python3-gdbm=y
CONFIG_python3-profile=y
CONFIG_python3-sqlite3=y
CONFIG_python3-textutils=y
CONFIG_libpython3=y
CONFIG_python3-xml=y
CONFIG_python3-threading=y
CONFIG_python3-modules=y
CONFIG_python3-importlib=y
CONFIG_python3-dev=y
CONFIG_python3-curses=y
CONFIG_python3-multiprocessing=y
CONFIG_python3-crypt=y
CONFIG_python3-enum=y
CONFIG_python3-compression=y
CONFIG_python3-shell=y
CONFIG_python3-tests=y
CONFIG_python3-numbers=y
CONFIG_python3-audio=y
CONFIG_python3-pyvenv=y
CONFIG_python3-asyncio=y
CONFIG_python3-misc=y
CONFIG_python3-datetime=y
CONFIG_python3-compile=y
CONFIG_python3-mmap=y
CONFIG_python3-mailbox=y
CONFIG_python3-argparse=y
CONFIG_python3-ctypes=y
CONFIG_tcf-agent=y
CONFIG_tiff=y
CONFIG_tiff-dev=y
CONFIG_v4l-utils=y
CONFIG_libv4l=y
CONFIG_v4l-utils-dev=y
CONFIG_media-ctl=y
CONFIG_libv4l-dev=y
CONFIG_videoproto-dev=y
CONFIG_xf86-video-fbdev=y
CONFIG_bridge-utils=y
CONFIG_libdrm=y
CONFIG_libdrm-dev=y
CONFIG_libdrm-drivers=y
CONFIG_packagegroup-petalinux-opencv=y
CONFIG_packagegroup-petalinux-opencv-dev=y
CONFIG_ROOTFS_ROOT_PASSWD="root"
デバイスツリーの設定
ビルドの前に、デバイスツリー(project-spec/meta-user/recipes-bsp/device-tree/files/system-user.dtsi
)を以下のように設定します。
/include/ "system-conf.dtsi"
/{
xlnk {
compatible = "xlnx,xlnk-1.0";
};
};
生成物をまとめる
SDSoC用の場合
上述のコマンドの通りです。bifファイルは以下のようになります。
/* linux */
the_ROM_image:
{
[bootloader]<fsbl.elf>
<bitstream>
<u-boot.elf>
}
非SDSoC用の場合
SDSoCを使わない場合は、ここでBOOT.binまで作ってしまいます。
petalinux-package --boot --force --fsbl images/linux/zynq_fsbl.elf --fpga images/linux/system.bit --u-boot
sysrootを生成する(Linuxアプリケーション開発用)
OpenCVライブラリなどを使うアプリケーションをビルドするためには、ヘッダファイルやライブラリが必要になります。それを用意します。PetaLinux2017.x以前だと、自動的に生成されたのですが、2018.x以降だと自分で作らないといけないようです。(https://forums.xilinx.com/t5/Embedded-Linux/Where-is-the-quot-linux-sysroot-path-quot/td-p/874525 )
コマンドは前述の(petalinux-build --sdk && petalinux-package --sysroot
)になります。その結果、Linux System Root(sysroot)がimages/linux/sdk/sysroots/cortexa9hf-neon-xilinx-linux-gnueabi/
に生成されます。(2017.x以前だとbuild/tmp/sysroots/plnx_arm
)
この生成されたsysrootをSDSoCやXSDKで参照します。SDSoCやXSDKもUbuntuに入れている人はこのまま使えばいいのですが、僕のようにWindowsで開発している人は、windows側にコピーする必要があります。シンボリックリンクを実体化してzip化します(-r
オプション)。その後、windows側にコピーして解凍します。
windowsで解凍するときは、msys等で、unzip sysroot.zip
で解凍します。途中、上書きしますか? と聞かれますが、yes(All)で大丈夫です。
SDカードを準備する
SDカードを、以下の2つの領域に分けます。パーティションを分けるには、UbuntuのDisksを使いました。
- boot領域: FATフォーマット。100MBくらいでOK
- Linux image領域: ext4。残り全て。
前述のsudo dd if=images/linux/rootfs.ext4 of=/dev/sdb2 bs=4096
コマンドをUbuntuで実行して、Linuxイメージを書き込みます。起動時に問題があったら、rootfs.ext3でも試してみてください。または、bootargsにrootfstype=ext4
を設定することで解決するかも。
SDSoCの場合には、boot領域に書き込むファイルはSDxで生成するので今は何もしないでOKです。
非SDSoCの場合には先ほど作成したBOOT.binとimage.ubをFATフォーマットのboot領域にコピーします。
SDSoCカスタムプラットフォームを作る
(2)Linuxプラットフォームと同じ手順です。
既存の「design_1」に追加する場合には、linux_opencvなどと名前を変えておきます。
Linuxアプリケーションを作る
sysrootの設定
SDx上で先ほど作成したカスタムプラットフォームをターゲットに新規アプリケーションを作成します。アプリケーション名は、test_opencvとします。
作成ウィザードの途中の、System configurationのAdditional SettingsのLinux RootFileSystemに、先ほどwindows側にコピーして解凍したsysrootのフォルダを指定します。
ビルド設定
ビルド変数の設定
生成されたアプリケーションを、Project Explorer上で右クリック → Properties → C/C++ Build → Environment -> Environment variables to setの所に、SYSROOT
を追加して、値に先ほどと同じsysrootのフォルダを設定します。(アプリケーション生成時に既に設定しているのに、再度必要な理由は不明。とにかく、ここに追加しないとビルドが通らなかった。cannot find crt1.o:
といったエラーが発生)
C/C++ Build → Settings → SDS++ Compiler(SDSCC Compilerも同様) → Directoriesに、先ほど設定したsysrootのパスが設定されていることを確認します。なかったら、 "${SYSROOT}/usr/include"
を追加します。(デフォルトだと環境変数を使わないフルパスになっているかもしれません。)
C/C++ Build → Settings → SDS++ Linker → Libraries → Library search path(-L)、先ほど設定したsysrootのパスが設定されていることを確認します。なかったら、 "${SYSROOT}"
を追加します。(デフォルトだと環境変数を使わないフルパスになっているかもしれません。)
ちなみに、cannot find /lib/libpthread.so.0
といったエラーが出た場合は、デフォルトではC:/path-to-sysroots/sysroots/cortexa9hf-neon-xilinx-linux-gnueabi/usr/lib
となっているのを、C:/path-to-sysroots/sysroots/cortexa9hf-neon-xilinx-linux-gnueabi
としたら治りました。(https://forums.xilinx.com/t5/Embedded-Development-Tools/Linker-can-t-find-pthread-library/td-p/849896)
OpenCVライブラリを追加する
自分でOpenCVライブラリをリンクするように設定していってもいいのですが、楽にする方法がありました。ただ、本当はこれはxfOpenCV用だと思うので、正しいかどうかは不明。
File -> New -> SDx Projectで、新しいプロジェクトを作ろうとします。Applicationを選び、適当なプロジェクト名を入れて進みます(実際には、このプロジェクトは作らないので名前は何でもいいです)。最後に、Templatesを選ぶ画面で、SDx Libraries
をクリックします。
Xilinx xfOpenCV Libraryを選択します。無かったら、UpdateやRefreshをクリックして取得します。その後、右下のAdd to projectから、既に作成済みのプロジェクトを選びます。
これで、OpenCVライブラリに必要な設定がされるので、今作成中プロジェクトのウィザードは閉じてしまって大丈夫です。
再度、C/C++ Build → Settings → SDS++ Linker → Libraries 、を見ると下記のように必要そうなライブラリを使うように設定されています。ただし、video(-lvideo
)だけはリンクエラーを消せなかったので、このリストから消しておきます。Linuxイメージ作成時のrootfsで必要なライブラリを取り込めてないのが原因だと思うのですが、解決できませんでした。
アプリケーションを作る
いよいよ、OpenCVを使ったプログラムの実装を行います。
JPEGファイルを読み込んで、グレースケールに変換して、再びJPEGファイルに出力する、というプログラムを書きます。
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <opencv2/opencv.hpp>
int main()
{
cv::Mat imgSrc;
cv::Mat imgDst;
imgSrc = cv::imread("org.jpg");
printf("rows = %d\n",imgSrc.rows);
printf("cols = %d\n",imgSrc.cols);
cvtColor(imgSrc, imgDst, CV_RGB2GRAY);
cv::imwrite("cv_gray.jpg", imgDst);
imgSrc.release();
imgDst.release();
}
実行準備
ビルドが完了したら、SD Card Image (BOOT.binとimage.ub、test_opencv.elf) をSDカードのboot領域(FATフォーマット)にコピーします。rootfsが書き込まれているext4側領域はそのままでOKです。
実行
SDカードをさして、ZYBOを起動します。
起動後、以下のようなコマンドでJPEGファイルを転送します。(あらかじめSDカードにコピーしておいてもOK)
scp /c/Users/tak/Desktop/*.jpg root@192.168.1.87:/home/root
その後、ZYBO側ターミナルで以下のように実行すると、変換されたJPEGが出力されます。
root@design_1_cv:~# ls
org.jpg
root@design_1_cv:~# /run/media/mmcblk0p1/test_opencv.elf
rows = 1280
cols = 1920
root@design_1_cv:~# ls
cv_gray.jpg org.jpg sds_trace_data.dat
その後、再度以下のようなコマンドでJPEGファイルをPC側にコピーすることで、白黒に変換された画像を確認することが出来ました。
scp root@192.168.1.87:/home/root/*.jpg /c/Users/tak/Desktop/.