ロボット、電子工作、AIなどの記録

ロボット、電子工作、AIなど、やったことを書こうかと

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


おわり

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

以前chainer版は試しました。
今回はtensorflowで実装されたYOLOv2をやってみたいと思います。

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

ここを参考にしました
YOLOv2(Keras/TensorFlow)でディープラーニングによる画像の物体検出を行う - Qiita
GitHub - allanzelener/YAD2K: YAD2K: Yet Another Darknet 2 Keras

[準備]
参考HPがlinuxのやりかたなのでwinでの手順を書いていきます。
まず先ほどのリンクの2番目からzipをDL→解凍。
次に以下のURLをクリックしてDLし、先ほど解凍したフォルダに移動。
http://pjreddie.com/media/files/yolo.weights

次に以下のURLをクリックして同じフォルダに"yolo.cfg"を作成して中身をコピペ
https://raw.githubusercontent.com/pjreddie/darknet/master/cfg/yolo.cfg

ライブラリ追加インストール

pip install numpy h5py pillow
pip install tensorflow-gpu
pip install keras

[物体認識テスト]
まずモデルを生成します

python yad2k.py yolo.cfg yolo.weights model_data/yolo.h5

実行するとwindowsならエラーがでます。
以下エラー内容(自分のをコピーし忘れたので人のを借用しました)

表示内容
____________________________________________________________________________________________________ 
Layer (type) Output Shape Param #Connected to
====================================================================================================
 lambda_1 (Lambda) (None, 3, 160, 320) 0
lambda_input_1[0][0]
____________________________________________________________________________________________________ 
convolution2d_1 (Convolution2D) (None, 1, 40, 16) 327696

.....
.....続く

エラー内容
Traceback (most recent call last):
  File "./yad2k.py", line 270, in <module>
    _main(parser.parse_args())
  File "./yad2k.py", line 254, in _main
    model.save('{}'.format(output_path))
  File "C:\Users\DanMo\Anaconda3\envs\py35cv3\lib\site-packages\keras\engine\topology.py", line 2506, in save
    save_model(self, filepath, overwrite, include_optimizer)
  File "C:\Users\DanMo\Anaconda3\envs\py35cv3\lib\site-packages\keras\models.py", line 106, in save_model
    'config': model.get_config()
  File "C:\Users\DanMo\Anaconda3\envs\py35cv3\lib\site-packages\keras\engine\topology.py", line 2322, in get_config
    layer_config = layer.get_config()
  File "C:\Users\DanMo\Anaconda3\envs\py35cv3\lib\site-packages\keras\layers\core.py", line 661, in get_config
    function = func_dump(self.function)
  File "C:\Users\DanMo\Anaconda3\envs\py35cv3\lib\site-packages\keras\utils\generic_utils.py", line 176, in func_dump
    code = marshal.dumps(func.__code__).decode('raw_unicode_escape')
UnicodeDecodeError: 'rawunicodeescape' codec can't decode bytes in position 195-196: truncated \uXXXX

どうやらwinだと特殊文字があってだめとのこと。
エラーが出ているkerasを書き換えに行きます。
Anacondaを使っている人は以下にインストールしたフォルダがあります
 C:\Users\ユーザー名\Anaconda3\Lib\site-packages\keras\utils 
ここの”generic_utils.py”を開き 175行目をコメントアウト、その下に新たに書き込みます

175    #code = marshal.dumps(func.__code__).decode('raw_unicode_escape')
176    code = marshal.dumps(func.__code__).replace(b'\\',b'/').decode('raw_unicode_escape')

セーブしたら再度実行

python yad2k.py yolo.cfg yolo.weights model_data/yolo.h5

今回はできました(194MBのファイルが生成される)

次に本題の物体認識です。

python test_yolo.py model_data/yolo.h5

実行するとimagesフォルダのoutフォルダに結果が表示されました。

f:id:weekendproject9:20170918180348j:plain:w350
f:id:weekendproject9:20170918180822j:plain:w350
f:id:weekendproject9:20170918180755j:plain:w350
f:id:weekendproject9:20170918180829j:plain:w350


2017/09/19 [追記]

PermissionError: [Errno 13] Permission denied:~~~~
というエラーが出ていたので対処しました。

・67行付近のoutputフォルダを生成しているifをコメントアウトを以下に変更

    c_path  = os.getcwd()#カレントディレクトリ取得
    output_file = "out"#出力フォルダ名
    o_path =  os.path.join(c_path,output_file)#出力フォルダパス生成
    if not os.path.exists(o_path):
        print('Creating output path {}'.format(o_path))
        os.mkdir(o_path)#生成

・205行付近の画像保存を以下に変更

  image.save(os.path.join(o_path, image_file), quality=90)

[参考]
YOLOv2(TensorFlow)を使ってリアルタイムオブジェクト認識をしてみる - Qiita
UnicodeDecodeError · Issue #60 · allanzelener/YAD2K · GitHub
python - Keras model.to_json() error: 'rawunicodeescape' codec can't decode bytes in position 94-98: truncated \uXXXX - Stack Overflow
keras/generic_utils.py at master · fchollet/keras · GitHub

chainer v1系列最終版"chainer1.24"をインストールする

2017/9/17時点でpipでインストールするchainerはv2.0.1です。
これをv1系列の最終版の1.24にバージョンを落とします。

環境:
 win10
 python3.5.2

[pipでインストールする方法]

 pip install chainer==1.24

[ソースからインストールする方法]
まずこちらの一番下からzipをDL.
Release v1.24.0 · chainer/chainer · GitHub

フォルダ移動
 cd chainer-1.24.0\chainer-1.24.0
インストール
 python setup.py install

バージョン確認します

>python

>>> import chainer
>>> print(chainer.__version__)
1.24.0
>>>

無事インストールされました。
また、ubuntu16.04でも確認しましたがインストールできました。


おわり

chainerでYOLOv2をやってみる [訓練済みモデル実行編]

2017年7月ごろに一度YOLOv2でwebカメラから物体認識をさせたのですが、
今回違う環境で動かそうとしたところエラーが出たので対処しました。

環境:
 win10
 chainer 1.21.0
 chainer 2.0.1


使用させてただいたプログラムはこちら
https://github.com/leetenki/YOLOv2

ここでDLして持ってきたら解凍。
次にすでに訓練されているモデルデータをDLするんですが、
YOLOv2/YOLOv2_execute.md at master · leetenki/YOLOv2 · GitHub
ここの

1、yolov2学習済みweightsファイルをダウンロードする。
 wget http://pjreddie.com/media/files/yolo.weights

2、以下のコマンドでweightsファイルをchainer用にパースする。
 python yolov2_darknet_parser.py yolo.weights

をしたところ、

<span style="color: #666666">C:\Users\YOLOv2-master\YOLOv2-master>python yolov2_da
rknet_parser.py yolo.weights
loading yolo.weights
loading initial model...
1 992
2 19680
3 93920
4 102368
5 176608
6 472544
7 505824
8 801760
9 1983456
10 2115552
11 3297248
12 3429344
13 4611040
14 9333728
15 9860064
16 14582752
17 15109088
18 19831776
19 29273056
20 38714336
Traceback (most recent call last):
  File "yolov2_darknet_parser.py", line 85, in <module>
    exec(txt)
  File "<string>", line 1, in <module>
ValueError: cannot reshape array of size 12265129 into shape (1024,3072,3,3)</span>

というエラーが出ました。
学習サイズが合っていないとのことなので合うサイズを持ってきます。

ここのお方がサイズが合ったものを提供してくださっています。
yolo.weights - Google ドライブ
ありがたくダウンロードして、YOLOv2-masterのフォルダに置く。
再度、chainer用にパースする

C:\Users\YOLOv2-master\YOLOv2-master>python yolov2_darknet_parser.py yolo.weights
loading yolo.weights
loading initial model...
1 992
2 19680
3 93920
4 102368
5 176608
6 472544
7 505824
8 801760
9 1983456
10 2115552
11 3297248
12 3429344
13 4611040
14 9333728
15 9860064
16 14582752
17 15109088
18 19831776
19 29273056
20 38714336
21 67029984
22 67465609
save weights file to yolov2_darknet.model

これでOK。

写真かWebカメラで動作確認。

python yolov2_darknet_predict.py data/people.png
python yolov2_darknet_camera.py 

f:id:weekendproject9:20170913184745p:plain:w250

※chainerのバージョンが1の系列なら実行可能です
※chainerが2のバージョンだと記述内容が変更になったためエラーで実行できません


おわり


[参考]
how to use yolov2_darknet_parser.py ? · Issue #2 · leetenki/YOLOv2 · GitHub

ubuntu16.04にopencv3.3.0をインストールする(できなかった。。)

2017/09/07時点で最新のOpencvをUbuntu16.04にインストールしようとしました。
結果出来ませんでした。参考までにここに2種類載せます。

まず①、②の前にこちらを入れます。
opencvで使用するかなりの部分を入れてます(たぶn)。
このまま入れると環境によっては次回ログイン時に
 "The system is running in low-graphics mode."
って出てきて画面が映らなくなります。

$ sudo apt-get install cmake git libgtk2.0-dev pkg-config libavcodec-dev libavformat-dev libswscale-dev
$ sudo apt-get install libtbb2 libtbb-dev libjpeg-dev libpng-dev libtiff-dev libjasper-dev libdc1394-22-dev libavcodec-dev libavformat-dev libswscale-dev libv4l-dev  libxvidcore-dev libx264-dev libgtk-3-dev libatlas-base-dev gfortran

①の方法
Opencvの最新である3.3.0のソースURLを確認します。
Releases - OpenCV library

ホームに戻り、opencvを取ってきます。

本体を取得
$ cd ~
$ wget -O opencv.zip https://github.com/opencv/opencv/archive/3.3.0.zip 
$ unzip opencv.zip

追加で取得
$ cd ~
$ wget -O opencv_contrib.zip https://github.com/opencv/opencv_contrib/archive/3.3.0.zip
$ unzip opencv_contrib.zip

ビルド
$ cd opencv-3.3.0
$ mkdir build
$ cd build

$ cmake -D CMAKE_BUILD_TYPE=RELEASE \
      -D CMAKE_INSTALL_PREFIX=/usr/local \
      -D INSTALL_PYTHON_EXAMPLES=ON \
      -D INSTALL_C_EXAMPLES=OFF \
      -D OPENCV_EXTRA_MODULES_PATH=~/opencv_contrib-3.3.0/modules \
      -D PYTHON_EXECUTABLE=~/.virtualenvs/cv/bin/python \
      -D BUILD_EXAMPLES=ON ..

ここで僕の環境では以下のエラーが出たのでここからは未確認の作業です。
 エラー:sys/videoio.h - not found
どうやらGCCのヴァージョンを最新にすればいいらしいです。

$ make -j4
$ sudo make install
$ sudo ldconfig

名前の変更
*cv2.cpython-35m-x86_64-linux-gnu.soが以下の場所にない場合は探す必要あり

$ cd /usr/local/lib/python3.5/site-packages/
$ sudo mv cv2.cpython-35m-x86_64-linux-gnu.so cv2.so

②の方法

$ cd ~
$ wget -O opencv.zip https://github.com/Itseez/opencv/archive/3.3.0.zip
$ unzip opencv.zip
$ cd opencv-3.3.0
$ mkdir release
$ cd release
$ cmake -DBUILD_TIFF=ON -DBUILD_opencv_java=OFF -DWITH_CUDA=OFF -DENABLE_AVX=ON -DWITH_OPENGL=ON -DWITH_OPENCL=ON -DWITH_IPP=ON -DWITH_TBB=ON -DWITH_EIGEN=ON -DWITH_V4L=ON -DWITH_VTK=OFF -DBUILD_TESTS=OFF -DBUILD_PERF_TESTS=OFF -DCMAKE_BUILD_TYPE=RELEASE -DBUILD_opencv_python2=OFF -DCMAKE_INSTALL_PREFIX=$(python3 -c "import sys; print(sys.prefix)") -DPYTHON3_EXECUTABLE=$(which python3) -DPYTHON3_INCLUDE_DIR=$(python3 -c "from distutils.sysconfig import get_python_inc; print(get_python_inc())") -DPYTHON3_PACKAGES_PATH=$(python3 -c "from distutils.sysconfig import get_python_lib; print(get_python_lib())") ..


f:id:weekendproject9:20170913143728p:plain
 ↑ pythonの項目が入っている ↑

$ make -j4
$ sudo make install
$ sudo ldconfig

おわり

参考:
https://medium.com/@debugvn/installing-opencv-3-3-0-on-ubuntu-16-04-lts-7db376f93961
https://www.scivision.co/anaconda-python-opencv3/
http://pythonopencv.com/install-opencv-3-3-and-python2-7-3-5-bindings-on-ubuntu-16-04/

USB/ACコンセント、アダプター、バッテリーからSTMマイコンを起動させる

秋月などで売っているSTMマイコンは、回路上の初期設定でパソコンとの通信を確立しないと動作しません。
(電流制限のため)

このため、コンセントやモバイルバッテリーから給電したいときは動作しません。

[解決方法]
今回のマイコン:STM F303k8
STMマイコン上部のSB1を見つけます(写真赤丸)
f:id:weekendproject9:20170912164105j:plain

SB1をショートさせます。
f:id:weekendproject9:20170912164144j:plain

PC以外の電源にマイコンをつなげて起動してるか確認します。

おわり

Raspberrypi OS (stretch) のリポジトリを日本に変更する

2017/09/08時点でラズベリーパイ最新OS(stretch)のリポジトリを日本に変更します。
ラズパイOS:
Download Raspbian for Raspberry Pi

ここに変更可能な一覧があります。
RaspbianMirrors - Raspbian

今回はJAISTにしました。

deb http://ftp.jaist.ac.jp/raspbian/ stretch main contrib non-free rpi

これで完了。


おわり