徒然なる日々を送るソフトウェアデベロッパーの記録(2)

技術上思ったことや感じたことを気ままに記録していくブログです。さくらから移設しました。

8ヶ月ぶりの更新; Raspberry Pi2 で OpenCV をクロス環境で動かしてみる

遅まきながら Raspberry Pi2 を購入して嬉しくなったので、Ubuntu 14.04LTS 上にクロス環境を構築して OpenCV のカメラキャプチャプログラムを動かしてみた。

意外にどはまったので、忘れないようにメモしておきます。
ロスコンパイラは

git clone https://github.com/raspberrypi/tools.git

から gcc 4.8.3(だっけ?)を取得しておきます。

今回は最小限の労力でライブラリを動かしたいため、OpenCV は最新版ではなく、apt-get install libopencv-dev で取得できるバージョンを使用。
(たぶん 2.4.1。古いっ)
これを Ubuntu 側に rsync で吸いだしておきます。

rsync -auv /lib xx.xx.xx.xx:/opt/tools/root/lib
rsync -auv /usr/lib xx.xx.xx.xx:/opt/tools/usr/lib

symbolic link を調べると
libpng.so.12 -> /lib/arm-linux-gnueabihf/libpng.12.so.0
となっていて、指定が絶対パスになっています。

これを1つ1つ相対パスに修正していくのが本来のインストールなんだろうけど、日が暮れそうなので
(ホスト側に影響が出てしまうが、)

ln -s /opencv/tools/arm-linux-gnuabhuf/ /usr/arm-linux-gnuabihuf

でお茶濁した。
その他にも技はありますが、大したことでは無いので割愛します。

今度は /usr/lib 直下にあるライブラリがないと宣われるので、-L と -Xlinker -rpath-link でパスを追加しています。エラーが出なくなるまでこの作業を繰り返す。
Videoカメラは EyeToy を使用。motion jpeg で記録されるのでこの手のカメラでは良い方。
秋月に売っている 300 円の VGA はどうなんでしょうか。(YUYVとして使えると予想しますが)

Rasberry Pi2 になって6倍の CPU 速度になっても Core i7 860 の性能には及ばないようなので、今後本格的にクロス環境を作っていくつもりです。

結果的に以下のようになりました。

#gcc, g++ が使用する環境およびディレクトリ
$>cat source.sh
export ARCH=arm
export CROSS_COMPILE=arm-linux-gnueabihf-
export ROOTDIR=/opt/tools/arm-bcm2708//gcc-linaro-arm-linux-gnueabihf-raspbian-x64/root
#export ROOTDIR=/opt/tools/gcc-4.7-linaro-rpi-gnueabihf/root
export PKG_CONFIG_PATH=${ROOTDIR}/usr/lib/pkgconfig
pushd ${ROOTDIR}/etc
. ld.so.cache
popd

$>cat ld.so.cache
#!/bin/sh
dirs=`ls ld.so.conf.d`
objpath=""
ldpath="${ROOTDIR}/lib"
for d in $dirs; do
echo "for $d"
	files=`cat ld.so.conf.d/$d`
	for f in $files; do
		echo "for $f"
		objpath="-L${ROOTDIR}$f $objpath"
		ldpath="${ROOTDIR}$f:$ldpath"
	done
done
export CROSS_LIBDIRS="$objpath"
export LD_LIBRARY_PATH="$ldpath"
export LIBRARY_PATH="$ldpath"

ロスコンパイラは環境変数を見ないようなので定義しても意味はないんですが。

TARGET=camera
SRC=camera.cc
OBJ=$(SRC:.cc=.o)
CC=$(CROSS_COMPILE)gcc
CXX=$(CROSS_COMPILE)g++
PKGCONFIG=$(CROSS_COMPILE)pkg-config
# for Pi2 Only binary
COMMON_FLAGS=-mcpu=cortex-a7 -mfpu=neon-vfpv4
# for PiB+/2B binary
#COMMON_FLAGS=-march=armv6 -mfpu=vfp
COMMON_FLAGS+=-mfloat-abi=hard
INCLUDES=`$(PKGCONFIG) --cflags opencv`
LINKOPT=-Xlinker -rpath-link -Xlinker /opt/tools/arm-bcm2708/gcc-linaro-arm-linux-gnueabihf-raspbian-x64/root/lib
LINKOPT+=-Xlinker -rpath-link -Xlinker /opt/tools/arm-bcm2708/gcc-linaro-arm-linux-gnueabihf-raspbian-x64/root/usr/lib
LINKOPT+=-Xlinker -rpath-link -Xlinker /lib/arm-linux-gnueabihf
LINKOPT+=-Xlinker -rpath-link -Xlinker /usr/lib/arm-linux-gnueabihf
LDFLAGS=$(COMMON_FLAGS) ${CROSS_LIBDIRS} `$(PKGCONFIG) --libs opencv` $(LINKOPT)
CFLAGS=$(COMMON_FLAGS) $(INCLUDES)
$(TARGET): $(OBJ)
	$(CXX) -o $(TARGET) $(OBJ) $(LDFLAGS)

.cc.o:
	$(CXX) $(CFLAGS) -c $<

clean:
	-rm *.o $(TARGET)

Pi2 用のcpuオプションを通してみたら通ってしまった。本当に正しく動いているかは検証が必要。
最後にいつものビデオキャプチャプログラム

#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <iostream>

int main() {
  cv::VideoCapture cap(0);

  // QVGA
  cap.set(CV_CAP_PROP_FRAME_WIDTH, 320);
  cap.set(CV_CAP_PROP_FRAME_HEIGHT, 240);

  // open camera
  if (!cap.isOpened()) {
    std::cout << "failed to open camera" << std::endl;
    return 1;
  }

  cv::namedWindow("Capture", CV_WINDOW_AUTOSIZE | CV_WINDOW_FREERATIO);

  while(1) {
    cv::Mat frame;
    cap >> frame;

    cv::imshow("Capture", frame);
    if (cv::waitKey(30) >= 0) {
      cv::imwrite("/tmp/cap.png", frame);
      break;
    }
  }
  return 0;
}

デバイスを操作するので root ユーザが特別に権限を与えないとオープンできないことに
注意する。