ハードウェア技術者のスキルアップ日誌

某家電メーカーの技術者がスキルアップのために勉強したことを記録するブログです

Python入門 ー 個人的メモ

以前AidemyでPython入門を受講しましたが、YOLOなどのサンプルコードを
読んでいく中で自分の理解が足りなかった部分を改めて勉強しましたので、
学んだことを個人的な備忘録として記録しておきます。

まとめ方が悪い、見難い部分があると思いますがご了承ください。

Pythonの勉強には「みんなのPython」という本を使用しました。 

みんなのPython 第4版

みんなのPython 第4版

 

 

目次

 

ディクショナリー

キーと値で要素を管理(順番は関係なし)  ※リストには順番がある

f:id:masashi_k:20190331170908p:plain

●定義方法

ディクショナリ名={キー1:値1, キー2:値2, ・・・}

 ●値の取り出し方

ディクショナリ名[要素のキー]

 ●要素の入れ替え

ディクショナリ名[要素のキー] = 値   # 代入と同じ形

 ●新しい要素の追加

ディクショナリ名[新しいキー名] = 値   # 代入と同じ形

 
Tips

in演算子でディクショナリの中のキーを検索できる

def convert_number(num):
      # アラビア数字とローマ字の変換 対応表をディクショナリに定義
      roman_nums = {1:"Ⅰ", 2:"Ⅱ", 3:"Ⅲ", 4:"Ⅳ", 5:"Ⅴ",
                    6:"Ⅵ", 7:"Ⅶ", 8:"Ⅷ", 9:"Ⅸ"}
      if num in roman_nums:
                return roman_nums[num]
      else:
                return "[変換できません]"


for文にディクショナリを添えると繰り返し変数にキーを代入しながらループを実行する

# アラビア数字とローマ字の変換 対応表をディクショナリに定義
roman_nums = {1:"Ⅰ", 2:"Ⅱ", 3:"Ⅲ", 4:"Ⅳ", 5:"Ⅴ",
              6:"Ⅵ", 7:"Ⅶ", 8:"Ⅷ", 9:"Ⅸ"}
for key in roman_nums:      # キーをすべて取り出す
       print(key, roman_nums[key])

  

関数

●関数の定義

def 関数名(引数1, 引数2, ・・・) : 
     関数ブロック

    return 戻り値

 

●関数の使用

変数 = 関数名(引数1, 引数2, ・・・)

 ※「変数」に関数を実行した結果(戻り値)が代入される

 

●デフォルト引数

関数定義時に引数に値を代入しておく
引数の指定がない場合、自動的にこの値が使用される

def 関数名(引数1=デフォルト値, 引数2=デフォルト値, ・・・) : 
    関数ブロック

   return 戻り値

※デフォルト引数は引数の後ろの方で指定 前半はデフォルト値の指定がない変数

 

●変数の影響範囲

関数内外で同じ名前の変数を定義しても、別のものとして扱われる
関数の中から外で定義した変数は見えるが、関数の外から中で定義した変数は見えない

f:id:masashi_k:20190331203904p:plain

 

高階関数

Pythonでは関数もオブジェクトなので、データとして扱える
 ・関数を変数に代入する
 ・関数をほかの関数に引数として渡す
 ・関数の中で新しい関数を生成して返す
といったことが可能

・「関数をほかの関数に引数として渡す」の例

def execute(func, arg):         # 引数を一つだけ取る関数呼び出しを抽象化
    return func(arg)            # 引数として受け取った関数を実行

print(execute(int, “100”))

 

・「関数の中で新しい関数を生成して返す」の例

def logger(func):
    def inner(*args):         # *args : 引数リスト
        print(“引数:”, args)
        return func(*args)    # 引数として受けた関数を呼び出し
    return inner              # 関数内で定義したinner()を実行

  

メソッド

データに紐づいてデータに対する処理や操作をする関数
クラス内でdefで定義されているもの

データ.メソッド名(引数1, 引数2, …)

※各組み込み型に対して標準で準備されているメソッドもあるし、
 ユーザーで新たなメソッドを定義できる

・リスト型のメソッド 
https://qiita.com/shiren/items/c89e002bc44343b387dc

・文字列型のメソッド
http://motw.mods.jp/Python/str_methods.html

・set型のメソッド
http://python-tech.com/python_used_set/

・ディクショナリ型のメソッド
https://code-graffiti.com/dictionary-in-python/

  

クラス

オブジェクトの設計図のこと
組み込み型に対するオブジェクトとクラスに対するインスタンスは似たような関係

f:id:masashi_k:20190331210210p:plain

●クラスの定義

classの後にクラス名を続けてクラスを定義

f:id:masashi_k:20190331210549p:plain

 例:直方体をクラスで表現

class Prism:
     def __init__(self, width, height, depth):     # 定義時にアトリビュートを自動で作成
     # アトリビュートは.(ドット)を使って定義 もらった引数をアトリビュートに渡す
            self.width = width
            self.height = height
            self.depth = depth
     def content(self):    # メソッドの定義
            return self.width*self.height*self.depth     # 体積を計算して返す

※__init__()は初期化メソッド インスタンス生成時に自動で呼ばれるメソッド
 メソッドには必ず引数としてselfを指定 selfはインスタンス自体を指す

 

●クラスの使用

クラスを関数のように呼び出してインスタンスを生成

例:定義したPrismというクラスを使用

p1 = Prism(10, 20, 30)    # インスタンスの作成 p1がインスタンス
# 定義したメソッドを使用
p1.content()     # 結果:6000

# .(ドット)アトリビュート名で値を取り出せる
p1.height    # 結果:20

f:id:masashi_k:20190331211930p:plain

カプセル化

クラス内で使用するアトリビュートを外から書き換えられないようにすること
クラス内で使用するメソッドを外から使用できないようにすること

「名前の先頭にアンダースコア(_)が1つついたアトリビュートやメソッドは
クラスの内部だけで使用するためにある」というルールがある
  例:_size

より厳しくアトリビュートやメソッドへのアクセスを制限する場合
名前の前にアンダースコアを二つ付ける
  例:__size

 

●クラスの継承

あるクラスを雛形にして別のクラスを作る
 親クラス:スーパークラス
 子クラス:サブクラス

f:id:masashi_k:20190331212707p:plain

●継承のさせ方

class クラス名(スーパークラス名1 [, スーパークラス名2, …]):


例:Prismを継承してCubeというクラスを新たに作る

class Cube(Prism)
       def __init__(self, length):    # 同名のメソッドを定義すると上書きされる
             self.width = self.height = self.depth = length

c = Cube(20)
# スーパークラスで定義したcontentメソッドはそのまま使用可
c.content()     # 結果:8000

 

スーパークラスの取得

super()を使うとスーパークラスを呼び出せる
__init__()をサブクラスで上書きしても元のスーパークラスの関数を使用できる

例:super()関数を使う

class Cube(Prism)
       def __init__(self, length):
             super().__init__(length, length, length)

 

●スロット

アトリビュートの追加を制限
指定した文字列のアトリビュートのみ追加が可能

__slot__ = [‘a’, ‘b’] # a, bというアトリビュートは追加OK
               # それ以外を追加しようとするとエラーになる

 

●プロパティ

データを変更したり、参照する専用のメソッド
  データを設定するメソッド:セッター(setter)
  データを取り出すメソッド:ゲッター(getter)

property([ゲッター [, セッター])

 
例:プロパティの定義、使用

class Prop: def __init__(self): self.__x = 0 # アトリビュートを作る def getx(self): # ゲッター:アトリビュートを返す return self.__x def setx(self, x) # セッター:アトリビュートを書き換え self.__x = x # xというアトリビュートに対して代入や参照を行うとgetx(), setx()というメソッドが呼ばれる x = property(getx, setx) # プロパティを設定 i = Prop() # インスタンスを作る i.x # 結果:0 # アトリビュートxを参照 ゲッターを使用→アトリビュート__xの値を返す i.x = 10 # xに代入 セッターを使用→アトリビュート__xに数値10を代入 i.x # 結果:10 # ゲッターを使用→アトリビュート__xの値を返す

  

モジュール

Pythonではスクリプトファイルをそのままモジュールとして使用できる
.pyの前までがモジュール名

 

●モジュールをインポートするときの挙動
インデントされていない位置に定義されている命令を実行

例: testmodule.py

#!/usr/bin/env python
import sys                        #標準ライブラリをインポート

a = 1                             #変数を定義
b = “some string”

def foo():                        #関数を定義
       print(“This is the function ‘foo’”)

print(“this is the top level”)    #文字列を表示

if __name__ == ‘__main__’:        #ファイル実行時のみ実行される(python testmodule.py)
       print(“this is the code block”)

 

●モジュールインポートの方法

例:「bookmark.py」というファイルにBookmarkクラスを定義した場合

# モジュールのインポート
import bookmark    # bookmark : module

# クラスのインポート
from bookmark import Bookmark    # bookmark : module, Bookmark : class

 

●パッケージ

モジュールとなるファイルを収めたディレクト
(3rd partyのモジュールパッケージもある numpyなど)

 

f:id:masashi_k:20190331215716p:plain

上図のmoduleaをインポートする場合、階層構造をドット(.)で区切って表記

import packagea.modulea

 関数funcaの呼び出しはpackagea.modulea.funca()

または

from packagea import modulea

 関数funcaの呼び出しはmodula.funca()

※パッケージとして使いたいディレクトリには__init__.pyという名前のファイルを置く

【読書記録】ー 超AI時代の生存戦略

 少し古い本ですが、落合陽一さんの超AI時代の生存戦略という本を読みましたので、学んだことを記録しておきます。

タイトルに惹かれて買ってしまったのですが、発売日は2年も前。
AIに関する世の中の流れにようやくキャッチアップ出来てきたなと感じていたのですが、自分の感度の低さを痛感させられます。
もっと情報に対して深く思考していかなくては!!

超AI時代の生存戦略 ―― シンギュラリティ<2040年代>に備える34のリスト

超AI時代の生存戦略 ―― シンギュラリティ<2040年代>に備える34のリスト

 

それでは早速、エッセンスをまとめていきます。

 

AI時代で生き残るためになすべきこと

f:id:masashi_k:20190330235439p:plain

世の中の変化や技術の進化に対してリスクを考えず、まずトライするようにしたい。
冒頭の反省にもつながるが、変化に抵抗すると行動が遅れ、周りに対して先んじることができなくなる。
デジタル時代は先行者利益がかなり大きい印象があるので、波に乗り遅れないこと。

 

ホリエモンも言っているが、これからは自分が好きなことをやって生きていく時代。
他の人がやっていない、他の人との差を出す、かつコンテンツになるものは
自分が好きで没頭できること。没頭するからうまくなるし、価値が出る。
何をやるかを戦略的に選んで、それを形にし、SNSで発信する。

 

マーケティングに対する考え方 ー 売れるものを作るためには

f:id:masashi_k:20190331000702p:plain

落合さんのマーケティングに関する考え方に共感したので特出しでメモ。

 マーケティング能力=テクノロジーのアプリケーション
技術をどう使うか、製品や技術に触れたときにそれを転用できないかを考える。
これができていれば商品に想定顧客、使用シーン、どんな価値を提供できるかが反映され、商品自体がマーケティングをやることになる。不必要な広告を減らせる。
このプロセスを経ずに作った商品はマーケティングをやっても売れない。
価格競争に巻き込まれてしまうので、「誰が欲しい?」「なぜそれが必要?」「どういうストーリーで伝える?」ということは常に考えていなくてはならない。

 

感想

今から2年も前にこのような指南書が出ていたとは、というのが正直な感想です。
書かれていることは今の自分の認識とほぼ同じでこれを当時に読んでいればもっと早く今取り組んでいるような動きを始められたのだろうと思います。

そういう意味では、情報に対する感度という自分の不足している点を認識させられたのがこの本を読んでの最大の学びですね。

遅れを取り戻すべく、今まで以上に加速して情報のインプット、思考、アウトプットのサイクルを回していき、自分が目指す姿に近づけるよう努力していきたいです。

 

学習済みの一般物体検出(YOLOv3)をPC上で動かしてみた

前回のYOLOv2に引き続き、今回はYOLOv3を動かすことにチャレンジしましたので、実施内容を記録しておきます。

masaeng.hatenablog.com

 

フレームワークはKerasを用います。

動作環境
 OS:Windows 10 Home (64bit)
 Python 3.5
 Anaconda 4.2.0
 Keras 2.2.4

 

手順

GITHUBに上がっているこちらの学習済みモデルをダウンロードし、任意の場所に解凍します。
https://github.com/mashyko/keras-yolo3

 

<学習済みモデル>

入力画像サイズ:416x416
学習データセット:COCO classes
検出クラス:80クラス
"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"

 

②こちらから学習済みの重みファイル(yolov3.weights)をダウンロードし、zipファイルを解凍し、keras-yolo3-masterフォルダの直下に置きます。

https://pjreddie.com/media/files/yolov3.weights

 

③Anacondaプロンプトを起動し、プロジェクトディレクトリに移動します。
以下のコマンドでDarknet Yolo modelの重みをKerasの重みに変換します。
これにより、プロジェクトディレクトリにyolo.h5という重みファイルが生成されます。

$ python convert.py yolov3.cfg yolov3.weights model_data/yolo.h5

 

④以下のコマンドを実行すると画像入力に対して物体検出を行います。

$ python yolo.py

 

以下のように画像の入力待ちとなるので、物体検出を行いたい画像を指定します。
imagesフォルダの下にテスト画像があるため、それを指定する場合はimages/dog.jpgのように入力します。

Using TensorFlow backend.
model_data/yolo.h5 model, anchors, and classes loaded.
Input image filename:

画像を指定すると、 バウンディングボックスと信頼度付きの検出結果画像が表示されます。

入力画像

f:id:masashi_k:20190321161406j:plain

出力画像

f:id:masashi_k:20190322001901p:plain

検出結果の信頼度を見るとYOLOv2の時の値より信頼度が高くなっています。性能が向上していることが伺えます。

 

⑤画像入力ではなく、Webカメラの映像に対してリアルタイムに物体検出させる場合は以下のようなyolo_cam.pyというスクリプトを作成し、実行します。

from yolo import YOLO
from yolo import detect_video

if __name__ == '__main__':
    video_path = 1
    output_path = './output.avi'
    # detect_video(YOLO(), video_path, output_path)
    detect_video(YOLO(),video_path)

 

実行コマンド

$ python yolo_camera.py

 

Youtubeの映像をカメラで撮影し、その映像に対して物体検出

YOLOv3

f:id:masashi_k:20190322010622p:plain

 YOLOv2

f:id:masashi_k:20190321232746p:plain

YOLOv3の方が細かな人やバイク、車などまで検出できていることがわかります。
YOLOv3の方が解像度が粗いのは入力画像の解像度の違いによるものです。

 

YOLOv2とYOLOv3の比較

YOLOv2で試した映像と同じものをYOLOv3で処理すると性能が大きく向上していることがわかります。実際に計算量や精度がどれほど違うのかを調べてみました。

f:id:masashi_k:20190323005636p:plain

 ※mAP = Mean Average Precision
[参照サイト:https://petitviolet.hatenablog.com/entry/20110901/1314853107]

 

YOLOv3の入力画像サイズをYOLOv2より小さくした場合でも検出精度は大きく上回っています。この時の推論時間は両者でほぼ変わりません。SSDと比較しても非常に優れた結果となっています。

 

参考サイト

TensorflowでObeject Detection

YOLO: Real-Time Object Detection

 

 

学習済みの一般物体検出(YOLOv2)をローカルPC上で動かしてみた

以前、学習済みの一般物体検出としてSSDを動かしてみましたが、同様にYOLOにもトライしてみましたので、結果を記録しておきたいと思います。

masaeng.hatenablog.com

 

YOLOの解説はこちらをご参照ください。

dev.classmethod.jp

qiita.com

 

YOLOは現時点、version3まで出ていますが、今回はversion2について実施しました。フレームワークはKerasを用います。

動作環境
 OS:Windows 10 Home (64bit)
 Python 3.5
 Anaconda 4.2.0
 Keras 2.2.4

 

手順

GITHUBに上がっているこちらの学習済みモデルをダウンロードし、任意の場所に解凍します。

https://github.com/allanzelener/YAD2K

f:id:masashi_k:20190321154738p:plain

<学習済みモデル>

入力画像サイズ:608x608
学習データセットPascal VOC
検出クラス:20クラス
"aeroplane", "bicycle", "bird", "boat", "bottle", "bus", "car", "cat",
"chair", "cow", "diningtable", "dog", "horse", "motorbike", "person",
"pottedplant", "sheep", "sofa", "train", "tvmonitor"

 

②こちらから学習済みの重みファイル(yolo.weights)をダウンロードし、zipファイルを解凍し、YAD2K-masterフォルダの直下に置きます。

http://pjreddie.com/media/files/yolo.weights

 

③こちらからDarknet版のYOLOv2をダウンロードしてzipファイルを解凍し、/cfg/yolov2.cfgをYAD2K-master直下に配置します。

https://github.com/pjreddie/darknet

f:id:masashi_k:20190321160749p:plain

 

④Anacondaプロンプトを起動し、プロジェクトディレクトリに移動します。
以下のコマンドを実行し、cfg, weightsファイルからKerasのモデルを生成します。

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

 

⑤test_yolo.pyの64行目からを以下のように変更します。

<変更前>
    if not os.path.exists(output_path):
        print('Creating output path {}'.format(output_path))
        os.mkdir(output_path)
<変更後> 
    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(output_path))
        os.mkdir(o_path)

 

⑥test_yolo.pyの197行目を以下のように変更します。

<変更前>
  image.save(os.path.join(output_path, image_file), quality=90)
<変更後>
  image.save(os.path.join(o_path, image_file), quality=90)

※⑤、⑥を実施しないとエラーが出ます。 (PermissionError)

 

⑦以下のコマンドを実行するとimageフォルダの画像に対して物体検出を行います。結果はoutフォルダにバウンディングボックス、信頼度付きの画像として保存されます。

$ python test_yolo.py model_data/yolo.h5

入力画像

f:id:masashi_k:20190321161406j:plain

検出結果
f:id:masashi_k:20190321161425j:plain

Webカメラの画像を入力し、リアルタイムに物体検出を行うため、test_yolo_cam.pyを新たに作成します。作成したソースコードは以下の通りです。

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

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

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)

# カメラの起動
cap = cv2.VideoCapture(0)
if not cap.isOpened():
    raise IOError(("Couldn't open video file or webcam. If you're "
    "trying to open a webcam, make sure you video_path is an integer!"))

# Compute aspect ratio of video
vidw = cap.get(cv2.CAP_PROP_FRAME_WIDTH)
vidh = cap.get(cv2.CAP_PROP_FRAME_HEIGHT)
vidar = vidw/vidh

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)

    #    if not os.path.exists(output_path):
    #        print('Creating output path {}'.format(output_path))
    #        os.mkdir(output_path)
    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(output_path))
        os.mkdir(o_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)

    while(True):

        # 動画ストリームからフレームを取得
        ret, cv_img = cap.read()
        if not ret:
            print("Done!")
            return

        image = cv2pil(cv_img)
        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'.format(len(out_boxes)))

        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]

            # ignore low score results
            if(score < 0.5):
                continue

            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

        #image.save("test.jpg", quality=90)
        cv_img2 = pil2cv(image)

        # 表示
        cv2.imshow("Show FLAME Image", cv_img2)

        # escを押したら終了。
        k = cv2.waitKey(10);
        if k == 27:  break;

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

def cv2pil(image):
    ''' OpenCV型 -> PIL型 '''
    new_image = image.copy()
    if new_image.ndim == 2:  # モノクロ
        pass
    elif new_image.shape[2] == 3:  # カラー
        new_image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
    elif new_image.shape[2] == 4:  # 透過
        new_image = cv2.cvtColor(image, cv2.COLOR_BGRA2RGBA)
    new_image = Image.fromarray(new_image)
    return new_image

def pil2cv(image):
    ''' PIL型 -> OpenCV型 '''
    new_image = np.array(image)
    if new_image.ndim == 2:  # モノクロ
        pass
    elif new_image.shape[2] == 3:  # カラー
        new_image = new_image[:, :, ::-1]
    elif new_image.shape[2] == 4:  # 透過
        new_image = new_image[:, :, [2, 1, 0, 3]]
    return new_image

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

 

⑨以下のコマンドを実行するとWebカメラの映像が表示され、検出された物体に信頼度と共にバウンディングボックスが表示されます。

$ python test_yolo_cam.py model_data/yolo.h5

 

Youtubeの映像をカメラで撮影し、その映像に対して物体検出

f:id:masashi_k:20190321232746p:plain

歩行者や自転車、奥の車は小さく検出できていませんが、大きく映っているバスや車は精度よく検出できていることがわかります。

 

感想

YOLOv2のオープンソースWebカメラの映像に対して適応するサンプルコードがなかったため、今回いろいろなものを参考にして自作しました。

やはり、自分である目的のコードを作成するには検出プログラムの詳細を理解しなければならないため、ただ動かすだけよりスキルアップになったと感じます。

今後はオリジナルのものを作ることに注力できればと思います。

また、YOLOversion3が動作する環境も作成したので、その内容も次回投稿します。

 

参考サイト

YOLOv2(Keras / TensorFlow)でディープラーニングによる画像の物体検出を行う - Qiita

tensorflowで物体認識(YOLOv2)をやってみる - ロボット、電子工作、IoT、AIなどの開発記録

【Python】Pillow ↔ OpenCV 変換 - Qiita

Deep Learning - 学習済みモデルをラズベリーパイ上で動かす

以前、からあげさんのチュートリアルに沿って学習済みのディープラーニングモデルをローカルPC上で動かし、推論を行いました。

masaeng.hatenablog.com

 

今回は同じ学習済みモデルをラズベリーパイ上で動かしてみました。
ラズベリーパイはこちらのキットを購入しました。
別途マイクロSDカード(16GB以上)が必要ですのでご注意ください。

 

ラズベリーパイの環境設定

ラズパイの初期設定方法はいろいろなところで紹介されているので写真等の詳しい説明は省略します。
以下のサイトが写真付きで分かりやすいです。

 

qiita.com

 

実際の実施内容は以下の通り

1.SDカードフォーマッターでSDカードをフォーマットします。形式はFAT32
  SDメモリカードフォーマッター - SD Association

2.以下のサイトからNOOBSをダウンロードし、解凍します。
  Download NOOBS for Raspberry Pi

3.解凍したフォルダの中身をSDカードにすべてコピーします。

4.ラズパイにSDカード、USBマウス、キーボード、ディスプレイ(HDMI接続)を挿し、電源を入れます。

5.NOOBSが起動するので、Rasbianをインストールします。キーボード設定は日本語にしました。インストールには30分ほどかかります。

6.インストールが完了するとRasbianが自動で立ち上がります。

7.GUIに沿って言語、タイムゾーン、パスワード、WiFiの設定を行います。

8.左上のRaspberry Piマークのアイコンをクリックし、「設定」→「Raspberry Piの設定」に入る。「インターフェース」タブをクリックし、SSHを有効にします。
これでラズベリーパイにPCからリモート接続できるようになります。ラズベリーパイ専用のディスプレイがない場合は便利です。

9.ターミナルを起動します。

 10.以下のコマンドを入力し、システムをアップデートする。

$ sudo apt-get update
$ sudo apt-get upgrade

 11.以下のコマンドを実行し、ラズパイにOpenCV、TensorFlow、Kerasをインストールします。

$ wget https://github.com/mt08xx/files/raw/master/opencv-rpi/libopencv3_3.4.0-20180115.1_armhf.deb
$ sudo apt install -y ./libopencv3_3.4.0-20180115.1_armhf.deb
$ sudo ldconfig
$ pip3 install numpy==1.13
$ sudo apt-get install libblas-dev liblapack-dev python3-dev libatlas-base-dev gfortran python3-setuptools
$ sudo pip3 install https://github.com/lhelontra/tensorflow-on-arm/releases/download/v1.8.0/tensorflow-1.8.0-cp35-none-linux_armv7l.whl
$ sudo apt-get install python3-h5py
$ sudo pip3 install keras==2.1.6

これで初期設定は完了です。

 

学習済みモデルのダウンロード

からあげさんのチュートリアルの中のリンクから入手できるkeras-piをダウンロードし、zipファイルを解凍します。

解凍したkeras-pi-master内のmodelディレクトリの中に自分で学習して作成したモデルデータ、ラベル、重みデータを上書きします。

 ※以下はWindowsの画面ですが、Rasbian上でも同様です。f:id:masashi_k:20190219232249p:plain

Webカメラの映像でリアルタイム識別

Webカメララズベリーパイに接続し、ターミナル上でinspect_camera_pi.pyを実行すると、カメラの映像がディスプレイに出力され、判定結果がターミナル上に表示されます。

PC上での結果と同じようにラズベリーパイでリアルタイム識別できます。

ラズベリーパイの処理能力でもフレーム落ちなどなく、十分に識別できていました。

 

SSDラズベリーパイ上で動かす

以前、SSDをPC上で動かしてみたのですが、これをラズベリーパイ上で動かしてみました。

masaeng.hatenablog.com

このブログの手順通りにラズベリーパイ上で学習済みモデル、重みのダウンロード、 ssd.py, testing_utils/videotest.pyのコードの修正を行います。

Webカメララズベリーパイに接続後、以下のコマンドを実行します。

$ cd testing_utils/
$ python3 videotest_example.py

すると、Web cameraの映像が立ち上がり、検知結果のバウンディングボックスが表示されます。ですが、PC上で動作させたときと比べると激重でなかなか映像が更新されませんでした。

keras-piの計算量よりSSDの方が計算量が多く、ラズベリーパイの能力では処理時間がかかりすぎるようです。

 

まとめ、感想

学習済みモデルをラズベリーパイで動作させることにトライしてみましたが、軽いネットワークにしないと、ラズベリーパイの処理能力では実用的なリアルタイム識別は難しそうだと感じました。

こちらの記事だとTiny-Yolov2であれば約1fpsが出ているようです。

qiita.com

 

ラズベリーパイで本格的な画像認識をやりたい場合はMovidiusのNCSが必要なようです。 機会があればMovidiusでアクセラレートさせた画像認識もやってみたいと思います。

karaage.hatenadiary.jp

 

学習済みの一般物体検出(SSD)をローカルPC上で動かしてみた

前回は性能は不十分ながらも自分で学習させたモデルをPC上で動かしてみましたが、
誤検知している様子を見てても面白くないので、学習済みのオープンソースモデルを動かしてみます。

 

今回は有名な一般物体認識の一つであるSSDを使います。
SSDの詳細についてはこちら。

arxiv.org

qiita.com

 

また、フレームワークはKerasを用います。

動作環境
 OS:Windows 10 Home (64bit)
 Python 3.5
 Anaconda 4.2.0
 Keras 2.2.4

 

手順

GITHUBに上がっているこちらの学習済みモデルをダウンロードし、任意の場所に解凍します。

https://github.com/mashyko/ssd_keras_ver2_1

f:id:masashi_k:20190221230503p:plain

<学習済みモデル>

入力画像サイズ:300x300
学習データセットPascal VOC
検出クラス:21クラス
"background", "aeroplane", "bicycle", "bird", "boat", "bottle", "bus", "car", "cat", "chair", "cow", "diningtable", "dog", "horse", "motorbike", "person", "pottedplant", "sheep", "sofa", "train", "tvmonitor"


②こちらから学習済みの重みファイル(weights_SSD300.hdf5)をダウンロードし、zipファイルを解凍します。

https://mega.nz/#F!7RowVLCL!q3cEVRK9jyOSB9el3SssIA


③①で作成したssd_keras_ver2_1-masterの直下に②の重みデータを格納します。

f:id:masashi_k:20190221231110p:plain

ssd.pyとtesting_utilsフォルダの下にあるvideotest.pyを以下のように変更します。
 Keras v1で記載されているのでV2ではエラーになります。

ssd.py

#12行目
from keras.layers import merge
#を以下のように変更
from keras.layers import concatenate

#253, 260, 267, 284行目
merge -> concatenate
#に変更

#258, 265, 272, 287行目
mode='concat'
#はすべて削除

#258, 265, 272, 287行目
concat_axis=1 => axis=1
#のように変更

videotest.py

#82行目
vid = cv2.VideoCapture(1) -> vid = cv2.VideoCapture(0)
#のように変更
#0はPC内蔵のカメラ、1は外付けUSBカメラ

 

⑤Anacondaプロンプトを起動し、以下を実行します。

$ cd testing_utils/
$ python videotest_example.py

すると、Web cameraの映像が立ち上がり、検知結果のバウンディングボックスが表示されます。プロンプト上でたくさんWarningが出てきますが、無視してOKです。
検知のフレームレートは1-2fpsです。

f:id:masashi_k:20190221234743p:plain


実際に検知させてみた様子


SSDでの一般物体検出

Youtubeの動画をWebカメラで撮影し、その映像にSSDをかけてみました。
1-2fpsしか処理速度がないため非常にカクカクした映像になっていますが、車や人、バイクなどを認識できています。
また、動画をカメラで撮影しているので、スピードが速い動きは不鮮明に映ってしまいますがそれでもある程度検出できているのがわかります。


感想

学習済みのモデルを使うことで簡単に一般物体検出を動かすことができました。
次はラズベリーパイ上でディープラーニングを動かすことにチャレンジしてみたいと思います。

 

参考URL

http://www.koi.mashykom.com/tensorflow.html

https://qiita.com/ttskng/items/4f67f4bbda2568229956

https://qiita.com/ttskng/items/4f67f4bbda2568229956

【読書記録】アウトプット大全

アウトプット大全という本を読みましたので学んだことを記録します。

著者の樺沢紫苑さんは日本一情報発信する精神科医という肩書があるそうで、本の中で多くのアウトプットノウハウを紹介されています。

今年の自分のテーマであるアウトプットについての本なので多くのことを学べればと思い、読んでみました。

学びを結果に変えるアウトプット大全 (Sanctuary books)

学びを結果に変えるアウトプット大全 (Sanctuary books)

 

 

アウトプットは自己成長のカギ

f:id:masashi_k:20190224013749p:plain

アウトプットの重要性は認識しつつも、今までの自分を振り返ると圧倒的にインプット過多だった。

インプットの量を減らしてもアウトプットの時間を意識して増やしていかないといけない。

 

アウトプットの3要素 話す・書く・行動する

f:id:masashi_k:20190224013745p:plain

話す

インプットした内容をとにかく人に話すこと。雑談のネタにもなる。

自分の意見や感想を足して話すことがポイント。(ブログの投稿でも同様)

自ら自己開示をすることで上司、部下間の人間関係がよくなる。失敗談は特にいい。

やりたくないことは断ることも重要。自分の時間を作る、自分のペースで生きることにつながる。

書く

書くことは脳を活性化させるため、忘れにくくなる。読書でも書き込みながら攻めの読書をすべき。そして、感想はブログに記載。

気づきがあったときは30秒以内でメモ。時間とともに忘れてしまう。これはいつも反省すること。スマホのメモでもいいので、何でも書き留めるようにしたい。

文章力を高めるにはとにかく訓練。いきなり書き始めず、構成を意識する。ここを固めてから文章を書き始めると、結果として時短になるし、まとまった文章になる。
構成を作る段階では手書きでイメージを膨らませ、Wordのアウトライン機能で構成を作成。

文章術に関する書籍として以下が紹介されている。 

伝わる文章が「速く」「思い通り」に書ける 87の法則 (Asuka business & language book)

伝わる文章が「速く」「思い通り」に書ける 87の法則 (Asuka business & language book)

 

 

行動する

話す、書く以外のすべてのアウトプットが「行動する」。

教えることが知識の定着など一番自分のためになる。勉強会を開催する。

自分がやりたいことに関してビジョンを掲げておく。どんな世の中の姿を目指したいのか?そうすれば共感した人が応援してくれて、一人でやるより夢の実現に近づく。

ブログはマニアックに書いてよい。それが他の人と差を出すポイント。「マツコの知らない世界」に出ているような人はそのマニアックさで注目されている。自分の好きなことをトコトン追及する。

 

感想

アウトプットに関していろんな視点から、特に著者が精神科医であることから医学的観点からの分析も絡めてまとめられています。

自分にとって初めてのノウハウなどはあまりなかったですが、アウトプットを行う上で大事にしたい考えを再確認できたと思っています。

アウトプットは大事なことはわかるものの、今までなかなかできていなかったのに対して、今年からアウトプットを増やしていこうと思っている自分を後押ししてもらったような気がします。

引き続き、ブログでのアウトプットを続けていきたいですね。

 

ちなみに、この本には著者樺沢先生の講演の動画や実際に使用されているフォーマットなどを無料で見ることができるURLのリンクが入っています。これにアクセスすると、樺沢先生のメルマガに登録されるようになっています。

この仕組みは一瞬、え?と不快に思いましたがメルマガで樺沢先生がどのようにアウトプットされているかがわかります。その量は自分ができそうな量よりも圧倒的で、もっとアウトプットせねば!と刺激を受けました。

そのような意味でも無料コンテンツはぜひご確認ください。