読者です 読者をやめる 読者になる 読者になる

Take’s diary

Macとマイコンに関すること--ワクワクの製作日記

Intel Edison + Henry 基板でSimpleCVを使って動画配信する(その2)

前回SimpleCVのインストール方法を書きました。うまくインストールできましたか?

takesan.hatenablog.com

 今回は、このライブラリを利用して、Henry基板で実際に動画配信するソフトを紹介してみようと思います。(といってもExampleをちょっと修正しただけですが)

 mjpeg-steamerとの大きな相違は、WebCameraで取り込んだ画像を加工してリアルタイムに配信できる点です。おまけにPythonでわずかな行数で実現できるのです。

 ただし欠点は処理が遅い点。この点では、処理対象画像を小さくすることしか対処できません。次からの例に動画を貼り付けておきますが、Edisonの用途を考慮した場合、必要十分であると思います。

 今回使用したExampleは、この記事から抽出しました。

http://www.reedbushey.com/65Practical%20Computer%20Vision%20with%20Simplecv.pdf

市販図書をPDF化したものですが、OpenCV初心者にとっては、かなり充実した内容でした。

  jpeg配信に関してEdisonでは、少しですがコツが必要です。私もこれが分かるまでは、無事インストールに成功したところで、やはりEdisonでは無理かと思いました。でも、実現できてしまえば、とても些細なことです。その辺りも含めてプログラムを紹介したいと思います。

SimpleCVでmjpeg-steamerのマネをさせる。

                            

画像は480X270ドットです。iPhoneでも同時に表示してます。以下SimpleCVを使ったPythonプログラムの注意点

  1. Pi2の場合と違いEdisonではJpegStreamer()の中身。ipアドレスとポート番号を記述する必要あり(Ipアドレスでもホスト名でもOK)
  2. sleep()は要調整。ループ中重い処理をしている場合は必要なし。
  3. サファリでの表示にはJpegStreamer()で記入したipアドレスとポート番号を入力するだけ
  4. 当然最終表示はdisplayではなく、JpegStreamerに変更

from SimpleCV import *

import time

c=Camera(0, { "width":480, "height":270 })

js = JpegStreamer("TakeED.local:8090")

while(1):

  img=c.getImage()

  img.save(js)

  time.sleep(0.08)

簡単でしょ。

SimpleCVで動体認識とHenry基板のセンサー値を表示させてみる。

画像で動いた部分の中心に同心円を書くプログラムです。

                     

最初は教科書通り(少し手直し)。カメラで読み込んだデータにリアルタイム図形描画をしてjpeg配信してます。

from SimpleCV import Camera, Color, Display, RunningSegmentation,JpegStreamer
import sys
from pprint import pprint
import time

print "Now running!!"
#cam = Camera(0, { "width": 480, "height": 270 })
cam = Camera(0, { "width": 320, "height": 240 })
js = JpegStreamer("TakeED.local:8090")
rs = RunningSegmentation(.6)
size = (cam.getImage().size())
center = (size[0] / 2, size[1] / 2)
while True:
input = cam.getImage()
# Assume using monitor mounted camera, so flip to create mirror image
input = input.flipHorizontal()
rs.addImage(input)
if(rs.isReady()):
# Get the object that moved
img = rs.getSegmentedImage(False)
blobs = img.dilate(3).findBlobs()
#If a object in motion was found
if( blobs is not None ):
blobs = blobs.sortArea()
# Update the crosshairs onto the object in motion
center = (int(blobs[-1].minRectX()),int(blobs[-1].minRectY()))
# Inside circle
input.dl().circle(center, 25, Color.BLACK, width = 3)
# Outside circle
input.dl().circle(center, 100, Color.BLACK, width = 6)
# Radiating lines
input.dl().line((center[0], center[1] - 50),(center[0], 0), Color.BLACK, width = 2)
input.dl().line((center[0], center[1] + 50),(center[0], size[1]), Color.BLACK, width = 2)
input.dl().line((center[0] - 50, center[1]),(0, center[1]), Color.BLACK, width = 2)
input.dl().line((center[0] + 50, center[1]),(size[0], center[1]), Color.BLACK, width = 2)
input.save(js)

次にHenry基板内蔵センサ値を同時に表示。左側のiPhoneは無視して下さい。センサー値を読み取り、フォント描画をしてもそんなにスピードが落ちてないことが分かると思います。センサーのライブラリと使い方はMotionCTL/Intel-Edison-henry · GitHubを参照のこと。

                              

 

from SimpleCV import Camera, Color, Display, RunningSegmentation,JpegStreamer
import sys
sys.path.append('/eaglet/lib')
sys.path.append('/eaglet/module')
from MPU9250 import MPU9250
from pprint import pprint
import time
print "Now running!!"
mpu9250 = MPU9250()

cam = Camera(0, { "width": 320, "height": 240 })
js = JpegStreamer("TakeED.local:8090")
rs = RunningSegmentation(.6)
size = (cam.getImage().size())
# Start the crosshairs in the center of the screen
center = (size[0] / 2, size[1] / 2)
while True:
accel = mpu9250.getAcceleromter()
input = cam.getImage()
# Assume using monitor mounted camera, so flip to create mirror image
input = input.flipHorizontal()
rs.addImage(input)
if(rs.isReady()):
# Get the object that moved
img = rs.getSegmentedImage(False)
blobs = img.dilate(3).findBlobs()
#If a object in motion was found
if( blobs is not None ):
blobs = blobs.sortArea()
# Update the crosshairs onto the object in motion
center = (int(blobs[-1].minRectX()),int(blobs[-1].minRectY()))
# Inside circle
input.dl().circle(center, 25, Color.BLACK, width = 3)
# Outside circle
input.dl().circle(center, 100, Color.BLACK, width = 6)
# Radiating lines
input.dl().line((center[0], center[1] - 50),(center[0], 0), Color.BLACK, width = 2)
input.dl().line((center[0], center[1] + 50),(center[0], size[1]), Color.BLACK, width = 2)
input.dl().line((center[0] - 50, center[1]),(0, center[1]), Color.BLACK, width = 2)
input.dl().line((center[0] + 50, center[1]),(size[0], center[1]), Color.BLACK, width = 2)
aaa= " Acccel:%5d, %5d, %5d" % (accel['x'], accel['y'], accel['z'])
input.drawText( aaa, 10, 8,fontsize=30,color=Color.ORANGE)
input.save(js)

SimpleCVで顔認識をさせてみる。

 この例では以下の画像ファイルの上に顔認識画像を貼り付けてjpeg配信しています。スピードを上げるため画像スケールを内部で1/2に落とし、顔認識した時点で元の大きさに変換しています。また、顔の大きさに合わせて?マークを表示させています。

                            f:id:TAKEsan:20150808123657p:plain

                          

 1フレームあたり1秒くらいでしょうか。静止画に比べたら断然使えると思いませんか? 画像ファイルsimple.pngはこのPythonプログラムと同じフォルダに入れておきます。

 顔認識はかなり大きなHaarCascadeというサンプルデータ群を読み込んで解析しています。Openframeworkesでの経験上、OpenCVの中ではかなり重い処理です。

 この参考プログラムでは、1フレームごとに非常に大きなサンプルデータを読み込んでいるようですが、これも遅くなる原因と思われます。SimpleCVの仕様のようです。今後の改善に期待するしかなさそうですね、

 また、標準インストールでは、このサンプルデータ群がインストールされていない可能性がありますが(どうだったか忘れてしまった)、その場合は

~/.local/lib/python2.7/sitepackages/SimpleCV/Features/HaarCascade

か、~/miniconda/python2.7/sitepackages/SimpleCV/Features/HaarCascade

のフォルダを作って、ファイルを入れてください(前回のインストールを行った場合のフォルダ構成。インストールしたSimpleCVがどこにあるか確認してください)。ファイルは標準OpenCVのソースをダウンロードすると入っています。拡張子は .xmlです。

 

※SimpleCVの標準HaarCascadeは、ここに入ってました。

SimpleCV/SimpleCV/Features/HaarCascades at master · sightmachine/SimpleCV · GitHub

haarcascade_frontalface_⚪︎⚪︎⚪︎.xml が該当ファイルで、その場合は下記プログラムのfindHaarFeatures()の中のファイル名を変更してください。

from SimpleCV import *

import time,pygame

c=Camera(0, { "width": 320, "height": 180 })

disp=Image("simple.png")

js = JpegStreamer("TakeED.local:8090")

print "Now running!!"

while True:

    img = c.getImage()

    img1 = img.scale(0.5)

    faces = img1.findHaarFeatures('face3')

    disp.dl().blit(img,(20,70))

    if faces is not None:

        faces = faces.sortArea() 

        bigFace = faces[-1]

        aa=bigFace.width()*2

        disp.drawText("?", 20+bigFace.x*2-aa/2,70+bigFace.y*2-aa/2,fontsize=bigFace.width()*4,color=Color().getRandom())

    disp.save(js)

このプログラムも、処理内容に比べたらかなり簡単に記述できます。

 この他にも魅力的な参考プログラムがたくさん紹介されています。(Kinectは使えません。Edisonの中でUSB認識しているようなので、ドライバが必要なようです。Edisonでは標準ディスプレイを設定できないのでマウスも使えません)

次回からの予定

 説明書のほとんど無いスイッチサイエンス版egletをとことん使って、最終的に魅力的なセンサーや表示機を使ったiPhoneとのOSC通信までの過程をメモしたいと思います。

 

この記事を書いた時点から7ヶ月後、SimpleCVとPythonのflaskライブラリを使って、Edisonとの双方向通信が可能になりました。SimpleCVで加工した画像を表示しながらEdisonを遠隔操作できるなんて夢だと思ってたのですから、できた時は「バンザーイ」でした。

takesan.hatenablog.com