websocketでRaspberryPiからwindowsPCに画像を送る(Python)

Raspberypi3から webscketで画像をあげて、PC側で取得・表示までやります。

概要図↓

f:id:weekendproject9:20171111170611p:plain

実行画面↓

f:id:weekendproject9:20171111180301p:plain

開発環境

  • win10 64bit

 python3.5.2
 openCV3.1.0

  • Raspberrypi3

 Raspbian Debian Stretch - Version:September 2017
 python3.5.2

RaspberryPi側開発

構成

Raspberry Piのカメラモジュールで撮った映像をWebSocketでブラウザに送る!! - ami_GS's diary
ラズパイの方は、ほぼ上記参考HPのまま使っています。
今回は自分の環境でエラーが出たところを修正しました。
また、IP固定はあらかじめしておいてください。

ーーここ引用ーー
camera.py
 RPi側でブラウザからのアクセスを受け付けるwebサーバ、及びカメラから映像を撮り、ブラウザへ送る。

index.html
 WebSocketでブラウザに送られてきた画像を表示する。

ーーーーーーーー

インストール

pip3 install websocket
pip3 install tornado
camera.py
import time
import picamera
import io
import tornado
import tornado.httpserver
import tornado.websocket
import tornado.ioloop
import tornado.web
import socket
from threading import Thread

WIDTH = 480
HEIGHT = 360
FPS = 30
class HttpHandler(tornado.web.RequestHandler):
    def initialize(self):
            pass

    def get(self):
        self.render("./index.html")  #最初のHTTPアクセスを受け付け、WebSocket接続を確立させるスクリプトが入ったindex.htmlを返す

class WSHandler(tornado.websocket.WebSocketHandler):
    def initialize(self, camera):
        self.camera = camera
        self.state = True

    def open(self):
        print(self.request.remote_ip, ": connection opened")
        t = Thread(target=self.loop)    #撮影&送信スレッドの作成
        t.setDaemon(True)
        t.start()

    def loop(self):
        stream = io.BytesIO()

        for foo in self.camera.capture_continuous(stream, "jpeg"):
            stream.seek(0)
            self.write_message(stream.read(), binary=True)
            stream.seek(0)
            stream.truncate()
            if not self.state:
                break

    def on_close(self):
        self.state = False     #映像送信のループを終了させる
        self.close()     #WebSocketセッションを閉じる
        print(self.request.remote_ip, ": connection closed")

def piCamera():
    camera = picamera.PiCamera()
    camera.resolution = (WIDTH, HEIGHT)
    camera.framerate = FPS
    camera.start_preview()
    
    time.sleep(2)        #カメラ初期化
    return camera

def main():
    camera = piCamera()
    print("complete initialization")
    app = tornado.web.Application([
        (r"/", HttpHandler),                     #最初のアクセスを受け付けるHTTPハンドラ
        (r"/camera", WSHandler, dict(camera=camera)),   #WebSocket接続を待ち受けるハンドラ
    ])
    http_server = tornado.httpserver.HTTPServer(app)
    http_server.listen(8080)
    tornado.ioloop.IOLoop.instance().start()

if __name__ == "__main__":
    main()
index.html
<html>
<head>
<title>livecamera</title>
<img id="liveImg" src="" width="480" height="360">
<script type="text/javascript">
var img = document.getElementById("liveImg");  
var arrayBuffer;

//WebSocketでサーバに接続
var ws = new WebSocket("ws://192.168.1.201:8080/camera"); ws.binaryType = 'arraybuffer';   //受診するデータがバイナリであるので設定

ws.onopen = function(){console.log("connection was established");};  //接続が確立した時に呼ばれる
ws.onmessage = function(evt){
	arrayBuffer = evt.data;
        //受信したデータを復号しbase64でエンコード
	img.src = "data:image/jpeg;base64," + encode(new Uint8Array(arrayBuffer));
};

window.onbeforeunload = function(){
    //ウィンドウ(タブ)を閉じたらサーバにセッションの終了を知らせる
    ws.close(1000);
};

function encode (input) {
    var keyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
    var output = "";
    var chr1, chr2, chr3, enc1, enc2, enc3, enc4;
    var i = 0;

    while (i < input.length) {
        chr1 = input[i++];
        chr2 = i < input.length ? input[i++] : Number.NaN; // Not sure if the index
        chr3 = i < input.length ? input[i++] : Number.NaN; // checks are needed here

        enc1 = chr1 >> 2;
        enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
        enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
        enc4 = chr3 & 63;

        if (isNaN(chr2)) {
            enc3 = enc4 = 64;
        } else if (isNaN(chr3)) {
            enc4 = 64;
        }
        output += keyStr.charAt(enc1) + keyStr.charAt(enc2) +
                  keyStr.charAt(enc3) + keyStr.charAt(enc4);
    }
    return output;
}
</script>
</head>
</html>

*WebSocketでサーバに接続する部分のIPアドレスを自分のラズパイのIPに書き換えてください

起動

上の2つのプログラムを同じフォルダに置いて実行します。

 python camera.py

ブラウザでwindowsPCからラズパイIPにアクセスするとリアルタイム画像が表示されます。

Windows側開発

構成

client.py
 websocketのクライアント文とOpenCVでの画像表示になります。

インストール
pip3 install websocket-client
client.py
#-*- coding:utf-8 -*-

from websocket import create_connection
import sys
import base64
from io import BytesIO 
import cv2
import numpy as np

ws = create_connection("ws://192.168.1.201:8080/camera")


# decode
while True:
    arr = np.asarray(bytearray(ws.recv()), dtype=np.uint8)
    img = cv2.imdecode(arr, -1)  # 'load it as it is'
    cv2.imshow('image', img)
    cv2.waitKey(10)
cv2.destroyAllWindows()

ws.close()

*create_connectionのアドレスをラズパイのアドレスに書き換えてください

起動

こっちを起動する前にラズパイの方を起動させておいてください。
ラズパイ起動確認後、以下で実行

python client.py

ウインドウが表示され、ラズパイが取得している画像が表示されます
 
f:id:weekendproject9:20171111180301p:plain

 
初めてwebsocketやりました。
おわり

[参考]
Raspberry Piのカメラモジュールで撮った映像をWebSocketでブラウザに送る!! - ami_GS's diary
【技術】pythonでwebsocketを試してみた - エンジニアリングとお金の話
PicameraによるRaspberry Pi 3カメラモジュールのカメラ設定 | TomoSoft
Pythonの画像読み込み: PIL, OpenCV, scikit-image - Qiita

YoLo/darknet初期学習データで判別できる種類一覧

f:id:weekendproject9:20171110113935p:plain

github等でYOLO、YOLOv2、darknetでよく用意されている学習データをそのまま適用したときに
判別できる種類の一覧は、以下の全80種類になります。

人
自転車
車
バイク
飛行機
バス
列車
トラック
ボート
信号機
消火栓
一時停止標識
パーキングメーター
ベンチ
鳥
ネコ
犬
馬
羊
牛
ゾウ
くま
シマウマ
キリン
バックパック
傘
ハンドバッグ
ネクタイ
スーツケース
フリスビー
スキー
スノーボード
スポーツボール
凧
野球用バット
野球グローブ
スケートボード
サーフボード
テニスラケット
ボトル
ワイングラス
カップ
フォーク
ナイフ
スプーン
ボウル
バナナ
林檎
サンドイッチ
オレンジ
ブロッコリ
にんじん
ホットドッグ
ピザ
ドーナツ
ケーキ
椅子
ソファー
鉢植え
ベッド
ダイニングテーブル
トイレ
テレビモニター
ノートPC
マウス
リモコン
キーボード
携帯電話
電子レンジ
オーブン
トースター
シンク
冷蔵庫
本
時計
花瓶
はさみ
テディベア
ヘアドライヤー
歯ブラシ

元の英語では

person
bicycle
car
motorbike
aeroplane
bus
train
truck
boat
traffic light
fire hydrant
stop sign
parking meter
bench
bird
cat
dog
horse
sheep
cow
elephant
bear
zebra
giraffe
backpack
umbrella
handbag
tie
suitcase
frisbee
skis
snowboard
sports ball
kite
baseball bat
baseball glove
skateboard
surfboard
tennis racket
bottle
wine glass
cup
fork
knife
spoon
bowl
banana
apple
sandwich
orange
broccoli
carrot
hot dog
pizza
donut
cake
chair
sofa
pottedplant
bed
diningtable
toilet
tvmonitor
laptop
mouse
remote
keyboard
cell phone
microwave
oven
toaster
sink
refrigerator
book
clock
vase
scissors
teddy bear
hair drier
toothbrush

BBoX-Label-ToolをPython3で使用する

BBoX-Label-Toolはpyhotn2.7で作成されているので3系では起動しません。
そこで今回はpython3系で起動できるようにします。

[環境]
win7 64bit
python3.5


[ソースダウンロード]
以下から本体をダウンロードします
github.com

[python3系に編集]
DL後、解凍。
main.pyを開きます。

10  from Tkinter import *
11  import tkMessageBox

の部分を削除して

try:
    import tkinter
    import tkinter.messagebox
except:
    import Tkinter as tkinter
    import tkMessageBox
from tkinter import *

に書き換え

あとはpython3用にprint文にカッコを追記して

python main.py

私の環境ではpythonで3系が起動します。
これで起動できたと思います。
f:id:weekendproject9:20171105213126p:plain


ちなみに画像フォルダはimages/002とか作ってそこにいれても画像が読み込めない?事態がおきますので、
その場合は001に入っている初期画像を消して、自分の学習させたい画像を001に移動させます。
そしてBBoxを起動させてフォルダパスのところに"1"と数字のみ入れてloadすると読み込めるはずです。


おわり

Darknetをwindows10にインストールして物体認識・物体判別をする

今までChainerやTensorflowなどで記述された物体認識・物体判別をしてきましたが
今回はYoloV2の制作者がC言語製作したdarknetをwindowsPCにインストールして試してみました。

[環境]
windows7 64bit
python3.5.2
VisualStudio2015
OpenCV3.2

[OpenCVインストール]

OpenCV3.2をダウンロードします
*製作者はOpenCV3.0でやっていますがファイル関係のエラーでできなかったため3.2でやっています
OpenCV 3.2 - OpenCV library

ここの一番下の”Windows self-extracting archive:”からexeをダウンロードします

exeを実行し、C:\直下に定してインストール
環境設定に

”C:\opencv\build\x64\vc14\lib;C:\opencv\build\include”
を追加する

[DarkerNetビルド]

https://github.com/AlexeyAB/darknet
ここにアクセスしてzipをダウンロード
ダウンロードしたファイルを解凍し、

”darknet-master\darknet-master\build\darknet”
に移動
次に”darknet.sln”を実行(VisualStudio2015)

VisualStudioが起動したら、
プラットフォームを”x64”に構成を”Release”に変更
ビルド→darknetのリビルドを実行

以下エラー除去
・include失敗、見つからない
 プロジェクト→プロパティ→VC++ディレクトリ→インクルードディレクトリ右の▼→編集→C:\opencv\build\includeを追加
 f:id:weekendproject9:20171029225053p:plain
opencv_world320.libが見つからない、参照できない
 プロジェクト→プロパティ→VC++ディレクトリ→ライブラリディレクトリ右の▼→編集→C:\opencv\build\x64\vc14\libを追加
 f:id:weekendproject9:20171029225506p:plain

*1.CUDA8.0ではなくて他のバージョンをお持ちの場合は、メモ帳を使用してbuild\darknet\darknet.vcxprojを開き、
  ”CUDA 8.0”で2か所探して自分のCUDAバージョンに変更して、リビルドを実行してください。
*2.GPUがない人はbuild\darknet\darknet_no_gpu.slnで実行してください
*3.OpenCV2.4.13の人はパスを変更してください
 ・(right click on project) -> properties -> C/C++ -> General -> Additional Include Directories:
   C:\opencv_2.4.13\opencv\build\include
 ・(right click on project) -> properties -> Linker -> General -> Additional Library Directories:
   C:\opencv_2.4.13\opencv\build\x64\vc14\lib
*4.OpenCV2.4.**の人はさらにパス変更が必要なようです
  \src\detector.c等を開き、”#pragma comment(lib, "opencv_core2413.lib"”などを変更


無事ビルドが終了したら”darknet-master\darknet-master\build\darknet\x64”にexeができています。

[DarkerNet実行]

コマンドプロンプトを起動して以下に移動

cd ***\***\darknet-master\darknet-master\build\darknet\x64
移動したら以下を実行
”darknet_voc.cmd”

以下エラー除去
opencv_world320.libが見つからない、参照できない
  C:\opencv\build\x64\vc14\bin\opencv_world320.dllをdarknet_voc.cmdを生成したexeと同じフォルダにコピー
opencv_ffmpeg320_64.dllが見つからない、参照できない
  C:\opencv\build\x64\vc14\bin\opencv_ffmpeg320_64.dllをdarknet_voc.cmdを生成したexeと同じフォルダにコピー

以下実行結果

\darknet-master\darknet-master\build\darknet\x64>darknet.exe detector
 test data/voc.data yolo-voc.cfg yolo-voc.weights -i 0 -thresh 0.2
layer     filters    size              input                output
    0 conv     32  3 x 3 / 1   416 x 416 x   3   ->   416 x 416 x  32
    1 max          2 x 2 / 2   416 x 416 x  32   ->   208 x 208 x  32
    2 conv     64  3 x 3 / 1   208 x 208 x  32   ->   208 x 208 x  64
    3 max          2 x 2 / 2   208 x 208 x  64   ->   104 x 104 x  64
    4 conv    128  3 x 3 / 1   104 x 104 x  64   ->   104 x 104 x 128
    5 conv     64  1 x 1 / 1   104 x 104 x 128   ->   104 x 104 x  64
    6 conv    128  3 x 3 / 1   104 x 104 x  64   ->   104 x 104 x 128
    7 max          2 x 2 / 2   104 x 104 x 128   ->    52 x  52 x 128
    8 conv    256  3 x 3 / 1    52 x  52 x 128   ->    52 x  52 x 256
    9 conv    128  1 x 1 / 1    52 x  52 x 256   ->    52 x  52 x 128
   10 conv    256  3 x 3 / 1    52 x  52 x 128   ->    52 x  52 x 256
   11 max          2 x 2 / 2    52 x  52 x 256   ->    26 x  26 x 256
   12 conv    512  3 x 3 / 1    26 x  26 x 256   ->    26 x  26 x 512
   13 conv    256  1 x 1 / 1    26 x  26 x 512   ->    26 x  26 x 256
   14 conv    512  3 x 3 / 1    26 x  26 x 256   ->    26 x  26 x 512
   15 conv    256  1 x 1 / 1    26 x  26 x 512   ->    26 x  26 x 256
   16 conv    512  3 x 3 / 1    26 x  26 x 256   ->    26 x  26 x 512
   17 max          2 x 2 / 2    26 x  26 x 512   ->    13 x  13 x 512
   18 conv   1024  3 x 3 / 1    13 x  13 x 512   ->    13 x  13 x1024
   19 conv    512  1 x 1 / 1    13 x  13 x1024   ->    13 x  13 x 512
   20 conv   1024  3 x 3 / 1    13 x  13 x 512   ->    13 x  13 x1024
   21 conv    512  1 x 1 / 1    13 x  13 x1024   ->    13 x  13 x 512
   22 conv   1024  3 x 3 / 1    13 x  13 x 512   ->    13 x  13 x1024
   23 conv   1024  3 x 3 / 1    13 x  13 x1024   ->    13 x  13 x1024
   24 conv   1024  3 x 3 / 1    13 x  13 x1024   ->    13 x  13 x1024
   25 route  16
   26 conv     64  1 x 1 / 1    26 x  26 x 512   ->    26 x  26 x  64
   27 reorg              / 2    26 x  26 x  64   ->    13 x  13 x 256
   28 route  27 24
   29 conv   1024  3 x 3 / 1    13 x  13 x1280   ->    13 x  13 x1024
   30 conv    125  1 x 1 / 1    13 x  13 x1024   ->    13 x  13 x 125
   31 detection

Loading weights from yolo-voc.weights...Done!
Enter Image Path:

使用する画像を尋ねられるからサンプルに付属している画像のパスを入れる

Enter Image Path: ***\darknet-master\darknet-master\data\dog.jpg
***\darknet-master\darknet-master\data\dog.jpg: Predicted in 0.038000 seconds.

car: 75%
bicycle: 77%
dog: 91%

^Cバッチ ジョブを終了しますか (Y/N)? y

Ctrl+Cを打ってyで終了する

実行結果↓
f:id:weekendproject9:20171030003830p:plain
f:id:weekendproject9:20171030003839p:plain



[参考HP]
「darknet」C言語で機械学習!とりあえずインストールとmake、エラーの対処をしてみた - lisz-works
DarknetをWindowsにインストールする - TadaoYamaokaの日記
YOLOv2を使って自前のデータを学習させて認識させるまで。 - 可変ブログ
OpenCV3.2インストール方法(ビルドなし) - Qiita

ChainerでYOLOv2をやってみる [訓練/学習編]

前回ChainerでYOLOv2をやったという記事にアクセスがまぁまぁあったので、
下書きのままだったYOLOv2の訓練/学習編を書いていきます。(メモ程度ですがご容赦ください)

YOLOv2の訓練手順(製作者様)↓
YOLOv2/YOLOv2_animal_train.md at master · leetenki/YOLOv2 · GitHub


[環境]
Ubuntu16.04
python3.5.2

windows7 64bitでも可能だと思います

[インストール]
以下のものを入れてない人は入れてください
pip3 install mock
pip3 install cupy


[画像収集]
・backup
・sample_images
・items
・backgrounds
以上の4つのフォルダを新規作成してください。

次に画像を収集します

python download_images.py

itemsフォルダ内に画像がダウンロードされたか確認します。


[画像生成]

python image_generate.py 

で画像がランダムに作られていきます。
ここでエラーが起こるので少し変更しました。
random_overlay_image関数の74,75行目の部分でエラーが起こります。

   y = int(np.random.randint(src_h-scale_item_h)) - int(shift_item_h)
   x = int(np.random.randint(src_w-scale_item_w)) -int( shift_item_w)

変更した関数ごとを載せます。

def random_overlay_image(src_image, overlay_image, minimum_crop):
    src_h, src_w = src_image.shape[:2]
    overlay_h, overlay_w = overlay_image.shape[:2]
    shift_item_h, shift_item_w = overlay_h * (1-minimum_crop), overlay_w * (1-minimum_crop)
    scale_item_h, scale_item_w = overlay_h * (minimum_crop*2-1), overlay_w * (minimum_crop*2-1)

    a = src_h-scale_item_h
    b = src_w-scale_item_w
    if a <= 1:
      a = 100
    if b <= 1:
      b = 100
    c = int(np.random.randint(a) - shift_item_h)
    d = int(np.random.randint(b) - shift_item_w)
    if c < 0:
      c = int(np.random.randint(a))
    if d < 0:
      d = int(np.random.randint(a))

    y = (int(c))
    x = (int(d))

    image = overlay(src_image, overlay_image, x, y)
    bbox = ((np.maximum(x, 0), np.maximum(y, 0)), (np.minimum(x+overlay_w, src_w-1), np.minimum(y+overlay_h, src_h-1)))

    return image, bbox

はい、汚いですすみません。とにかくrandintにマイナスを入れないようにすればいいと思います。
今回は恐竜のほかにカービーを混ぜました。それに伴い、パラサウロロフスさんは消去しました。
追加したカービー↓
f:id:weekendproject9:20171023144113p:plain
とてもかわいいですね。

もう一度image_generate.pyを実行して確認します。
f:id:weekendproject9:20171023143901p:plain


[darknet19(画像識別器)の訓練・テスト]
今回カービーを追加したのでdata/label.tetラベルを書き換えます。

Tyrannosaurus
Brachiosaurus
Triceratops
Riopururodon
Rhino
Dog
Erasmosaurus
Ammonite
kirby
Deer

*パラサウロロフスさんは消去しました。
*順番も順守
訓練

python darknet19_train.py

訓練終了後、テストします。

python darknet19_predict.py items/kirby.png

以下、結果です
f:id:weekendproject9:20171023154918p:plain

[darknet19_448の訓練・重み切り出し]
訓練する

python darknet19_448_train.py

backupフォルダにdarknet19_448_final.modelが保存されたか確認
つぎに重み切り出し

python partial_weights.py

backupフォルダにpartial.modelが保存されたか確認

[YOLOv2の訓練・テスト]

python yolov2_train.py

ここで私のPCではメモリ不足になってしまったので
88行目のloss.backward()の下に

loss.unchain_backward()

を追加します。
*これでメモリが解放されますが学習にどう響くかは不明です。。。
ちなみに3日~1週間の間、学習処理をしました。

無事、訓練が終わったらテストです。
しかし、テストするまえに
yolov2_predict.pyの12行目を変更を出力したモデルの名前に書き換えます

12    weight_file = "./backup/yolov2_final_cpu.model"
 ↓
12    weight_file = "./backup/yolov2_final.model"

認識テスト実行

python yolov2_predict.py test.jpg 

テスト画像↓
f:id:weekendproject9:20171023143910p:plain

結果
f:id:weekendproject9:20171023154934p:plain

以上、終わりです。




[参考]
Chainer2.0がリリース【今後の安定版】とりあえず抑えておきたい変更点 - HELLO CYBERNETICS
chainer-object-detection/model/yolov2 at master · dsanno/chainer-object-detection · GitHub

tensorflowで物体認識(YOLOv2)をやってみる 2

前回、前々回でやっているtensorflowでYolov2なのですが、
参考にしているものがもう一つありましたので動作させるところまで書きます。
github
github.com


[環境]
 win7 64bit
 GTX 960
 python3.5
 tensolfolw 1.2.1


[動作手順]
1.先ほどのリンクに行きソースをDL、解凍
2.下のリンクをクリックしてDL。さきほど解凍したフォルダにbinという名前でフォルダを新規作成して入れる
https://pjreddie.com/media/files/yolo.weights

3.コマンドラインで先ほど解凍したフォルダに移動して

python setup.py build_ext --inplace

でインストール

4.githubのREADMEの”Using darkflow from another python application”に書かれているプログラムを参考に実行してみる
↓パスの部分を少し変えてます

[test.py]

from darkflow.net.build import TFNet
import cv2

options = {"model": "cfg/yolo.cfg", "load": "bin/yolo.weights", "threshold": 0.4, "gpu": 0.3}

tfnet = TFNet(options)


input_image = "sample_dog.jpg"
image_folder = "sample_img"
current_path  = os.getcwd()
current_path =  os.path.join(current_path,image_folder)

src = cv2.imread(os.path.join(current_path,input_image))
result = tfnet.return_predict(imgcv)
print(result)

実行

python test.py

メモリエラーが出る場合は"options"で指定しているgpuの値をもっと引き下げてください
結果

[{'label': 'bicycle', 'confidence': 0.8448509, 'topleft': {'y': 114, 'x': 81}, '
bottomright': {'y': 466, 'x': 553}}, {'label': 'truck', 'confidence': 0.79510289
, 'topleft': {'y': 81, 'x': 462}, 'bottomright': {'y': 167, 'x': 693}}, {'label'
: 'dog', 'confidence': 0.76959282, 'topleft': {'y': 214, 'x': 136}, 'bottomright
': {'y': 539, 'x': 322}}]

[画像表示]
結果は得られましたが、これでは見にくいので画像に結果を表示させようと思います。
返答値を分解すればいいんですが面倒になったので
今回は検出部に直接描画用コードを書いてしまいました。

                                                          • -

[2017/9/25]追記
YOLOv2のリアルタイム物体検出をTensorFlowとPythonで実装する方法 | AI coordinator
このブログの公開1日前にここのお方がメインで描画するソースを公開してくださいました。
動画での解析になっているので読み込みなどの部分を変える必要がありますが、、

                                                          • -


以下、メインです。

[test.py]

from darkflow.net.build import TFNet
import cv2
import os
import json

options = {"model": "cfg/yolo.cfg", "load": "bin/yolo.weights", "threshold": 0.4, "gpu": 0.3}
tfnet = TFNet(options)

input_image = "sample_dog.jpg"
image_folder = "sample_img"
current_path  = os.getcwd()
output_file = "out"
current_path =  os.path.join(current_path,image_folder)
output_path =  os.path.join(current_path,output_file)
if not os.path.exists(output_path):
    print('Creating output path {}'.format(output_path))
    os.mkdir(output_path)

src = cv2.imread(os.path.join(current_path,input_image))
dst = src
cv2.imshow("img", src)

result = tfnet.return_predict(src,dst)
print(result)

cv2.imshow("img_out", dst)
cv2.waitKey()
cv2.imwrite(output_path + '\\' + input_image, dst)

次にメイン文で呼んでる tfnet.return_predict()の中身を書き換えます。
場所は"darkflow-master\darkflow-master\darkflow\net\flow.py"です。

[flow.py]
#importの追加
import cv2
import random
import colorsys

#メインで呼んでる関数の中身変更
def return_predict(self, im ,dst , output_image = True  ):#引き数追加
    assert isinstance(im, np.ndarray), \
				'Image is not a np.ndarray'
    h, w, _ = im.shape
    im = self.framework.resize_input(im)
    this_inp = np.expand_dims(im, 0)
    feed_dict = {self.inp : this_inp}

    out = self.sess.run(self.out, feed_dict)[0]
    boxes = self.framework.findboxes(out)
    threshold = self.FLAGS.threshold
    boxesInfo = list()
    
    #描画する色の指定
    hsv_tuples = [(x / 80, 1., 1.)
                  for x in range(80)]
    colors = list(map(lambda x: colorsys.hsv_to_rgb(*x), hsv_tuples))
    colors = list(
        map(lambda x: (int(x[0] * 255), int(x[1] * 255), int(x[2] * 255)),
            colors))
    random.seed(10101)  # Fixed seed for consistent colors across runs.
    random.shuffle(colors)  # Shuffle colors to decorrelate adjacent classes.
    random.seed(None)  # Reset seed to default.
    i=0

    for box in boxes:
        tmpBox = self.framework.process_box(box, h, w, threshold)
        if tmpBox is None:
            continue
        boxesInfo.append({
            "label": tmpBox[4],
            "confidence": tmpBox[6],
            "topleft": {
                "x": tmpBox[0],
                "y": tmpBox[2]},
            "bottomright": {
                "x": tmpBox[1],
                "y": tmpBox[3]}
        })
        #描画
        if output_image:
            cv2.rectangle(dst,(tmpBox[0],  tmpBox[2]), (tmpBox[1], tmpBox[3]), colors[i], 2)
            fontType = cv2.FONT_HERSHEY_SIMPLEX
            cv2.putText(dst,  tmpBox[4], (tmpBox[0],  tmpBox[2] - 6),fontType , 0.6,  colors[i],1,cv2.LINE_AA)
            cv2.putText(dst,  str(tmpBox[6]), (tmpBox[0],  tmpBox[2] + 15),fontType , 0.6,  colors[i],1,cv2.LINE_AA)
            i+=1
    return boxesInfo

実行

python test.py

結果
f:id:weekendproject9:20170924192608j:plain
sample_computerの結果
f:id:weekendproject9:20170924192656j:plain
sample_computerのprint文結果

[{'bottomright': {'x': 345, 'y': 280}, 'confidence': 0.87742555, 'topleft': {'x'
: 157, 'y': 94}, 'label': 'tvmonitor'}, {'bottomright': {'x': 333, 'y': 371}, 'c
onfidence': 0.79934323, 'topleft': {'x': 123, 'y': 263}, 'label': 'keyboard'}, {
'bottomright': {'x': 130, 'y': 351}, 'confidence': 0.48609883, 'topleft': {'x':
0, 'y': 20}, 'label': 'refrigerator'}]

関数の引き数ごと変えちゃったけどまぁいいか。。

[参考]
YOLOv2(TensorFlow)を使ってリアルタイムオブジェクト認識をしてみる - Qiita

おわり

tensorflowで物体認識(YOLOv2)をやってみる[動画解析編]

前回はYolov2の準備をして画像から物体認識をさせました。
今回は動画を読み込み物体認識をさせようと思います。
また、解析した動画は保存するようにしました。

[結果]
まずは結果から
f:id:weekendproject9:20170921224625g:plain:w550

↓切り抜き画像↓
f:id:weekendproject9:20170921234021p:plain:w650
f:id:weekendproject9:20170921234124p:plain:w650
f:id:weekendproject9:20170921234138p:plain:w650
f:id:weekendproject9:20170921234151p:plain:w650




[処理]
元のソースは画像系をPILで処理していました。
今回は動画読み込みや保存をOpencvで行いましたので、
Opencv -> PIL -> Yolov2 -> PIL -> Opencv
といった流れで処理させています。

[test_yolo_video.py]

#! /usr/bin/env python
# python test_yolo.py model_data/yolo.h5
"""Run a YOLO_v2 style detection model on test images."""
import argparse
import colorsys
import imghdr
import os
import random

import numpy as np
from keras import backend as K
from keras.models import load_model
from PIL import Image, ImageDraw, ImageFont

from yad2k.models.keras_yolo import yolo_eval, yolo_head
import cv2

input_width, input_height = (416, 416)

parser = argparse.ArgumentParser(
    description='Run a YOLO_v2 style detection model on test images..')
parser.add_argument(
    'model_path',
    help='path to h5 model file containing body'
    'of a YOLO_v2 model')
parser.add_argument(
    '-a',
    '--anchors_path',
    help='path to anchors file, defaults to yolo_anchors.txt',
    default='model_data/yolo_anchors.txt')
parser.add_argument(
    '-c',
    '--classes_path',
    help='path to classes file, defaults to coco_classes.txt',
    default='model_data/coco_classes.txt')
parser.add_argument(
    '-t',
    '--test_path',
    help='path to directory of test images, defaults to images/',
    default='images')
parser.add_argument(
    '-o',
    '--output_path',
    help='path to output test images, defaults to images/out',
    default='images/out')
parser.add_argument(
    '-s',
    '--score_threshold',
    type=float,
    help='threshold for bounding box scores, default .3',
    default=.3)
parser.add_argument(
    '-iou',
    '--iou_threshold',
    type=float,
    help='threshold for non max suppression IOU, default .5',
    default=.5)


def _main(args):

    model_path = os.path.expanduser(args.model_path)
    assert model_path.endswith('.h5'), 'Keras model must be a .h5 file.'
    anchors_path = os.path.expanduser(args.anchors_path)
    classes_path = os.path.expanduser(args.classes_path)
    test_path = os.path.expanduser(args.test_path)
    output_path = os.path.expanduser(args.output_path)

    sess = K.get_session()  # TODO: Remove dependence on Tensorflow session.

    with open(classes_path) as f:
        class_names = f.readlines()
    class_names = [c.strip() for c in class_names]

    with open(anchors_path) as f:
        anchors = f.readline()
        anchors = [float(x) for x in anchors.split(',')]
        anchors = np.array(anchors).reshape(-1, 2)

    yolo_model = load_model(model_path)

    # Verify model, anchors, and classes are compatible
    num_classes = len(class_names)
    num_anchors = len(anchors)
    # TODO: Assumes dim ordering is channel last
    model_output_channels = yolo_model.layers[-1].output_shape[-1]
    assert model_output_channels == num_anchors * (num_classes + 5), \
        'Mismatch between model and given anchor and class sizes. ' \
        'Specify matching anchors and classes with --anchors_path and ' \
        '--classes_path flags.'
    print('{} model, anchors, and classes loaded.'.format(model_path))

    # Check if model is fully convolutional, assuming channel last order.
    model_image_size = yolo_model.layers[0].input_shape[1:3]
    is_fixed_size = model_image_size != (None, None)

    # Generate colors for drawing bounding boxes.
    hsv_tuples = [(x / len(class_names), 1., 1.)
                  for x in range(len(class_names))]
    colors = list(map(lambda x: colorsys.hsv_to_rgb(*x), hsv_tuples))
    colors = list(
        map(lambda x: (int(x[0] * 255), int(x[1] * 255), int(x[2] * 255)),
            colors))
    random.seed(10101)  # Fixed seed for consistent colors across runs.
    random.shuffle(colors)  # Shuffle colors to decorrelate adjacent classes.
    random.seed(None)  # Reset seed to default.

    # Generate output tensor targets for filtered bounding boxes.
    # TODO: Wrap these backend operations with Keras layers.
    yolo_outputs = yolo_head(yolo_model.output, anchors, len(class_names))
    input_image_shape = K.placeholder(shape=(2, ))
    boxes, scores, classes = yolo_eval(
        yolo_outputs,
        input_image_shape,
        score_threshold=args.score_threshold,
        iou_threshold=args.iou_threshold)

    #video系準備
    cap = cv2.VideoCapture("input.mp4")
    # Define the codec and create VideoWriter object
    fourcc = cv2.VideoWriter_fourcc(*'MJPG')
    ret, frame = cap.read()
    h, w, ch = frame.shape
    output_video = cv2.VideoWriter('output.avi',fourcc, 30.0, (w,h))

    while(cap.isOpened()):
        # フレームを取得
        ret, frame = cap.read()

        #BGRからRGBへ変換
        cv_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
        #PILに変換
        src_image_pil=Image.fromarray(cv_rgb)
        pil_normalize = src_image_pil.convert('RGB')
        image = pil_normalize

        if is_fixed_size:  # TODO: When resizing we can use minibatch input.
            resized_image = image.resize(
                tuple(reversed(model_image_size)), Image.BICUBIC)
            image_data = np.array(resized_image, dtype='float32')
        else:
        # Due to skip connection + max pooling in YOLO_v2, inputs must have
        # width and height as multiples of 32.
            new_image_size = (image.width - (image.width % 32),
                                image.height - (image.height % 32))
            resized_image = image.resize(new_image_size, Image.BICUBIC)
            image_data = np.array(resized_image, dtype='float32')
        #    print(image_data.shape)

        image_data /= 255.
        image_data = np.expand_dims(image_data, 0)  # Add batch dimension.

        out_boxes, out_scores, out_classes = sess.run(
            [boxes, scores, classes],
            feed_dict={
                yolo_model.input: image_data,
                input_image_shape: [image.size[1], image.size[0]],
                K.learning_phase(): 0
            })
        #print('Found {} boxes for {}'.format(len(out_boxes), image_file))


        font = ImageFont.truetype(
            font='font/FiraMono-Medium.otf',
            size=np.floor(3e-2 * image.size[1] + 0.5).astype('int32'))
        thickness = (image.size[0] + image.size[1]) // 300


        for i, c in reversed(list(enumerate(out_classes))):
            predicted_class = class_names[c]
            box = out_boxes[i]
            score = out_scores[i]

            label = '{} {:.2f}'.format(predicted_class, score)

            draw = ImageDraw.Draw(image)
            label_size = draw.textsize(label, font)

            top, left, bottom, right = box
            top = max(0, np.floor(top + 0.5).astype('int32'))
            left = max(0, np.floor(left + 0.5).astype('int32'))
            bottom = min(image.size[1], np.floor(bottom + 0.5).astype('int32'))
            right = min(image.size[0], np.floor(right + 0.5).astype('int32'))
            #print(label, (left, top), (right, bottom))

            if top - label_size[1] >= 0:
                text_origin = np.array([left, top - label_size[1]])
            else:
                text_origin = np.array([left, top + 1])

            # My kingdom for a good redistributable image drawing library.
            for i in range(thickness):
                draw.rectangle(
                    [left + i, top + i, right - i, bottom - i],
                    outline=colors[c])
            draw.rectangle(
                [tuple(text_origin), tuple(text_origin + label_size)],
                fill=colors[c])
            draw.text(text_origin, label, fill=(0, 0, 0), font=font)
            del draw
        
        #cvに変換して表示&保存
        cv_output=np.asarray(image)
        cv_output = cv2.cvtColor(cv_output, cv2.COLOR_BGR2RGB)
        cv2.imshow("view", cv_output)
        output_video.write(cv_output)
        # qキーが押されたら途中終了
        if cv2.waitKey(5) & 0xFF == ord('q'):
            break

    cap.release()
    output_video.release()
    cv2.destroyAllWindows()
    sess.close()

if __name__ == '__main__':
    _main(parser.parse_args())

読み込む動画データをソース上に直接書いてしまっているのですが、
同じフォルダにinput.mp4を置いてください。
(あとでコマンドラインの引き数に書き直す予定です)


実行する

python test_yolo_video.py model_data/yolo.h5


おわり