脳汁portal

アメリカ在住(だった)新米エンジニアがその日学んだIT知識を書き綴るブログ

raspberryPi3(jessie)にopenCV3.1.0をインストールする

わりと手こずりましたが、ラズベリーパイにopencv3.1.0をインストールする方法です
以下のサイトを参考にさせて頂きました。

Procedure

ライブラリのインストール

$ sudo apt-get install build-essential cmake cmake-qt-gui
$ sudo apt-get install libgtk2.0-dev libjpeg-dev libtiff5-dev libjasper-dev libopenexr-dev python-dev python-numpy python-tk libeigen3-dev yasm libopencore-amrnb-dev libopencore-amrwb-dev libtheora-dev libvorbis-dev libxvidcore-dev libx264-dev libqt4-dev libqt4-opengl-dev sphinx-common libv4l-dev libdc1394-22-dev libavcodec-dev libavformat-dev libswscale-dev default-jdk ant libvtk5-qt4-dev

packageのダウンロード

$ cd ~
$ mkdir src
$ cd src

# OpenCVのダウンロード
$ wget https://github.com/Itseez/opencv/archive/3.1.0.zip
$ unzip 3.1.0.zip

# OpenCV contribもダウンロード
$ wget -O opencv_contrib.zip https://github.com/Itseez/opencv_contrib/archive/3.1.0.zip
$ unzip opencv_contrib.zip

build

$ cd opencv-3.1.0
$ mkdir build
$ cd build

$ cmake -D CMAKE_BUILD_TYPE=RELEASE \
-D CMAKE_INSTALL_PREFIX=/usr/local \
-D INSTALL_PYTHON_EXAMPLES=ON \
-D OPENCV_EXTRA_MODULES_PATH=/home/pi/src/opencv_contrib-3.1.0/modules \
-D BUILD_EXAMPLES=ON ..

$ make -j4

install

$ sudo make install
$ sudo ldconfig

確認

$ python
Python 2.7.9 (default, Sep 17 2016, 20:26:04)
[GCC 4.9.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import cv2
>>> print cv2.__version__
3.1.0

無事3.1.0がインストールされていることを確認して完了

openCVでraspberryPiのUSBカメラのキャプチャをとって、サイズを変更する

import cv2

# cameraの設定
c = cv2.VideoCapture(0)

# キャプチャ
boolean, img = c.read()
h, w = img.shape[:2]
print h # 1280
print w # 720

# サイズ変更
half = cv2.resize(img, (320, 240))
hh, hw = half.shape[:2]
print hh  # 320
print hw  # 240

cv2.imwrite('half.jpg', half)
  • VideoCaptureでdeviceを指定して、readでキャプチャをとる
  • resize(source, (heightのsize, widthのsize))でサイズ変更
  • imwriteでimageをファイル出力

うまく行かない場合はlsusbとかでちゃんとUSBカメラが認識されているか確認する

motionを使ってRaspberryPiからUSBカメラの映像をストリーミングする方法

motionのインストール

sudo apt-get install motion

設定の変更

sudo vi /etc/motion/motion.conf

 ### USBカメラの設定をする(解像度やFramerate)
 89 # Image width (pixels). Valid range: Camera dependent, default: 352
 90 width 320
 91
 92 # Image height (pixels). Valid range: Camera dependent, default: 288
 93 height 240
 94
 95 # Maximum number of frames to be captured per second.
 96 # Valid range: 2-100. Default: 100 (almost no limit).
 97 framerate 2

 ### 画像ファイルとして保存する機能は今回はoffにする
 250 output_pictures off

 ### Streamingの設定をする
 473 # Maximum framerate for stream streams (default: 1)
 474 stream_maxrate 2
 475
 476 # Restrict stream connections to localhost only (default: on)
 477 stream_localhost off

motion起動

sudo motion -n

確認

以下のURLにアクセスして映像が確認できれば完了

http://{raspberryPiのIP}:8081

ただしMJPG-stremerに比べて3秒前後の遅延が発生する。

USBカメラの解像度等の情報を確認する方法

deviceの確認

まずはカメラを接続したらlsusbで認識されているか確認します

$ lsusb
Bus 001 Device 005: ID 288c:0002  # <== これが追加された
Bus 001 Device 007: ID 05ac:0222 Apple, Inc. Aluminum Keyboard (JIS)
Bus 001 Device 006: ID 04ca:0061 Lite-On Technology Corp.
Bus 001 Device 004: ID 05ac:1006 Apple, Inc. Hub in Aluminum Keyboard
Bus 001 Device 003: ID 0424:ec00 Standard Microsystems Corp. SMSC9512/9514 Fast Ethernet Adapter
Bus 001 Device 002: ID 0424:9514 Standard Microsystems Corp.
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub

今回はBus 001 Device 005にUSBカメラが設定されています。
このときカメラの製造元や会社の名前が出てくるときと出てこない場合があります

各項目の確認

概要確認
$ v4l2-ctl -d 0 --info
Driver Info (not using libv4l2):
        Driver name   : uvcvideo
        Card type     : MS-M103HU
        Bus info      : usb-3f980000.usb-1.5
        Driver version: 4.4.11
        Capabilities  : 0x84200001
                Video Capture
                Streaming
                Extended Pix Format
                Device Capabilities
        Device Caps   : 0x04200001
                Video Capture
                Streaming
                Extended Pix Format
全項目確認
$ v4l2-ctl -d 0 --all
Driver Info (not using libv4l2):
        Driver name   : uvcvideo
        Card type     : MS-M103HU
        Bus info      : usb-3f980000.usb-1.5
        Driver version: 4.4.11
        Capabilities  : 0x84200001
                Video Capture
                Streaming
                Extended Pix Format
・
・
・
対応解像度&FPS確認
$ v4l2-ctl -d 0 --list-formats-ext
ioctl: VIDIOC_ENUM_FMT
        Index       : 0
        Type        : Video Capture
        Pixel Format: 'YUYV'
        Name        : YUYV 4:2:2
                Size: Discrete 1280x720
                        Interval: Discrete 0.083s (12.000 fps)

        Index       : 1
        Type        : Video Capture
        Pixel Format: 'MJPG' (compressed)
        Name        : Motion-JPEG
                Size: Discrete 1280x720
                        Interval: Discrete 0.033s (30.000 fps)
lsusbでも確認できる
$ lsusb -s 001:005 -v | egrep "Width|Height"
Couldn't open device, some information will be missing
        wWidth                           1280
        wHeight                           720
        wWidth( 0)                       1280
        wHeight( 0)                       720
        wWidth                           1280
        wHeight                           720
        wWidth( 0)                       1280
        wHeight( 0)                       720

low voltage detected, date/time is not reliable.

エラー文

RaspberryPiでRTCから時刻合わせをしようとすると以下のようなエラーが出ることがある。

Jan 01 23:11:11 raspberrypi kernel: [  131.098038] rtc-pcf8563 1-0051: low voltage detected, date/time is not reliable. 

ちなみに使ったRTCはこちら
リアルタイムクロック(RTC)モジュール: 半導体 秋月電子通商 電子部品 ネット通販
f:id:portaltan:20170113194609j:plain
エラーの意味としては『電圧低下が検知されたよ。だから今の時刻データは信頼できないよ。』って感じの警告だ。

調査

アプリケーションノートを読んでみると、RTCへの電源供給の電圧が一定以下になった場合、VLflag(Voltage Low Flag)が立つらしい。
f:id:portaltan:20170113100032p:plain

対応

ちなみにこのVLフラグは秒数を管理しているメモリ区域で管理されている
f:id:portaltan:20170113100209p:plain

なので上記のエラー文を出したくなければ、VLフラグを0クリアしてやればエラーは出なくなる。

RubyのThreadのステータスに関して

class Thread (Ruby 2.4.0)
instance method Thread#status (Ruby 2.4.0)
instance method Thread#alive? (Ruby 2.4.0)

Rubyのスレッドに関して各ケースのステータスまとめ

各項目がとる値

  • thread object(thread.inspect)
    • run
    • sleep
    • aborting
    • dead
  • thread.status
    • run
    • sleep
    • false
    • nil
  • thread.alive?
    • true
    • false

調査

通常時(スレッドが生きているとき)

ソース
t1 = Thread.start{
  loop do
    sleep 1
  end
}

loop do
  p t1 # run, sleep, aborting, dead
  puts t1.status || t1.status.class # run, sleep, false, nil
  puts t1.alive? # true, false
  puts "--------------------------------"
  sleep 1
end
実行結果
#<Thread:0x55e80d30 sleep>
run
true
--------------------------------
#<Thread:0x55e80d30 sleep>
run
true
--------------------------------
#<Thread:0x55e80d30 sleep>
run
true
--------------------------------
各ステータス
  • thread object : (run), sleep
  • thread.status : run, (sleep)
  • thread.alive? : true

正常終了時

ソース
t1 = Thread.start{
  2.times{|n|
    sleep 1
    puts n+1
  }
}

loop do
  p t1
  puts t1.status || t1.status.class # run, sleep, aborting, false, nil
  puts t1.alive? # true, false
  puts "--------------------------------"
  sleep 1
end
実行結果
#<Thread:0x566a0c48 run>
sleep
true
--------------------------------
1
#<Thread:0x566a0c48 sleep>
sleep
true
--------------------------------
2
#<Thread:0x566a0c48 dead>
FalseClass
false
--------------------------------
#<Thread:0x566a0c48 dead>
FalseClass
false
--------------------------------
#<Thread:0x566a0c48 dead>
FalseClass
false
--------------------------------
各ステータス
  • thread object : dead
  • thread.status : false
  • thread.alive? : false

異常終了時

ソース
t1 = Thread.start{
  2.times{|n|
    sleep 1
    puts n+1
  }
  raise
}

loop do
  p t1
  puts t1.status || t1.status.class # run, sleep, aborting, false, nil
  puts t1.alive? # true, false
  puts "--------------------------------"
  sleep 1
end
実行結果
#<Thread:0x558e8bf0 run>
sleep
true
--------------------------------
#<Thread:0x558e8bf0 run>
sleep
1
true
--------------------------------
#<Thread:0x558e8bf0 run>
2
sleep
false
--------------------------------
#<Thread:0x558e8bf0 dead>
NilClass
false
--------------------------------
#<Thread:0x558e8bf0 dead>
NilClass
false
各ステータス
  • thread object : dead
  • thread.status : nil
  • thread.alive? : false

まとめると

  • thread.statusはスレッドが生きている間はrunかsleepで、正常終了するとfalseになり、異常終了するとnilになる
  • thread.alive?はスレッドが生きている間はtrueで、終了すると正常・異常に関わらずfalseになる
  • thread情報を直接見ると、スレッドが生きている場合はrunかsleepで、終了処理中はabortingになり、完全に終了するとdeadになる

raspberryPiでRTCを使おうとしたときにargument Errorになる

前提条件として、raspberryPiにはRTCがついていないため、シャットダウンしてしまうと時間を保持しておくことが出来ない。

なので通常は起動時にntpでシステムの時刻をあわせるのだが、当然インターネット環境がない場合はその時間合わせをすること自体が出来ない。

そういう場合などのために個別にRTCを取り付けることが出来るのだが、以下のようにエラーが出てRTCへアクセスが出来ないことがたまにある。

hwclock: ioctl(RTC_RD_TIME) to /dev/rtcX to read the time failed: Invalid argument

調べていくと割とメジャーなつまづきポイントらしい。
Raspberry Pi • View topic - Wierd RTC(ds1307) problem

調べていくとfake_hwclockというものが悪さをしているらしく、上の方でraspberryPiにはRTC(hwclock)はついていないといったのだが、その代わりにのっているのがfake_hwclockらしい。

これは何かというと、普通ならば時刻データを保持できないのでネット環境がない場合は起動するたびに初期値の1970年1月1日0時0分0秒になってしまうのだが、
このfake_hwclockというシステムが有効化されていると、起動している時間を定期的(shutdown時にも?)にtxtとして保存しておき、次回起動時にこの時間を現在時刻として
使おうという機能である。

今回は本物のhwclockであるRTCとこのfake_hwclockが競合してしまったために上記のエラーが出たものと思われる。
解決方法としてはこいつを無効化してやればよい。

sudo update-rc.d fake-hwclock disable

これで競合することはなくなり、上記のエラーが出ることもなくなった。