Python3.6 AIで物体検出、第二弾!keras-yolo3で人を検出してカウントする方法!

まずは前回の記事より一通りkeras-yolo3以外の環境を整えます。
http://hiroslog.com/post/380

次にkeras-yolo3をcloneします。

git clone https://github.com/qqwweee/keras-yolo3.git

cd keras-yolo3

keras-yolo3ディレクトリに移動し

学習済みモデルをダウンロード
wget https://pjreddie.com/media/files/yolov3.weights

kerasで利用できるように変換しyolo.h5として保存
python3.6 convert.py yolov3.cfg yolov3.weights model_data/yolo.h5

そして

yolo_video.py

yolo.py
を編集します。僕の場合、yoloのサンプルにあるような対話型としたくなかったので10秒ごとに解析した画像を保存するように変更しました。
sleepを入れただけなので厳密には10秒ではなく処理の時間等入ってしまっているので正確に10秒ごとにという風になると別のやリ方になります。
今回はとりあえず大体10秒ごとに画像を保存しなおせればという感じでゆるくやってみます。
最終的に作りたいものはラズパイのカメラモジュールで10秒ごとに撮影した画像をpythonで10秒ごとに何人いるか解析しwebから人数と画像を閲覧できるようにするという感じです。
まずは10秒ごとに解析という部分を作ります。

yolo_video.py

import sys
import argparse
import numpy as np
import time # 追加
import random # 追加
from yolo import YOLO, detect_video
from PIL import Image

def detect_img(yolo):

    try :
        while True:
            #テストなのでランダムに画像を取得し解析した画像を保存。約10秒ごとに保存された解析画像が切り替わるか確認
            num = random.randint(1,10)
            if num < 4:
                img = 'image1.jpg'
            elif num < 7:
                img = 'image2.jpg'
            else:
                img = 'image3.jpg'

            try:
                image = Image.open(img)
            except:
                print('Open Error! Try again!')
                continue
            else:
                r_image = yolo.detect_image(image)
                # r_image.show()

                # 画像の書き出し、10秒ごとに書き出す
                import cv2 
                cv2.imwrite("output_image.jpg", np.asarray(r_image)[..., ::-1])
                time.sleep(10)

        yolo.close_session()

    except KeyboardInterrupt:
        # cotrol + c で停止できるように KeyboardInterruptで例外をキャッチ
        print("停止しました")


FLAGS = None

if __name__ == '__main__':
    # class YOLO defines the default value, so suppress any default here
    parser = argparse.ArgumentParser(argument_default=argparse.SUPPRESS)
    '''
    Command line options
    '''
    parser.add_argument(
        '--model', type=str,
        help='path to model weight file, default ' + YOLO.get_defaults("model_path")
    )

    parser.add_argument(
        '--anchors', type=str,
        help='path to anchor definitions, default ' + YOLO.get_defaults("anchors_path")
    )

    parser.add_argument(
        '--classes', type=str,
        help='path to class definitions, default ' + YOLO.get_defaults("classes_path")
    )

    parser.add_argument(
        '--gpu_num', type=int,
        help='Number of GPU to use, default ' + str(YOLO.get_defaults("gpu_num"))
    )

    parser.add_argument(
        '--image', default=False, action="store_true",
        help='Image detection mode, will ignore all positional arguments'
    )
    '''
    Command line positional arguments -- for video detection mode
    '''
    parser.add_argument(
        "--input", nargs='?', type=str,required=False,default='./path2your_video',
        help = "Video input path"
    )

    parser.add_argument(
        "--output", nargs='?', type=str, default="",
        help = "[Optional] Video output path"
    )

    FLAGS = parser.parse_args()

    detect_img(YOLO(**vars(FLAGS)))

yolo.py
のdetect_image部分を変更、今回直接いじってしまいましたがオーバーライドしてやったほうがいいかも


    def detect_image(self, image):
        start = timer()

        if self.model_image_size != (None, None):
            assert self.model_image_size[0]%32 == 0, 'Multiples of 32 required'
            assert self.model_image_size[1]%32 == 0, 'Multiples of 32 required'
            boxed_image = letterbox_image(image, tuple(reversed(self.model_image_size)))
        else:
            new_image_size = (image.width - (image.width % 32),
                              image.height - (image.height % 32))
            boxed_image = letterbox_image(image, new_image_size)
        image_data = np.array(boxed_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 = self.sess.run(
            [self.boxes, self.scores, self.classes],
            feed_dict={
                self.yolo_model.input: image_data,
                self.input_image_shape: [image.size[1], image.size[0]],
                K.learning_phase(): 0
            })

        print('Found {} boxes for {}'.format(len(out_boxes), 'img'))

        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

        person_count = 0

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

            label = '{} {:.2f}'.format(predicted_class, score)
            label_name = '{}'.format(predicted_class) # 追加
            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 label_name == 'person':
                person_count+=1
            # / 追加ここまで

            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=self.colors[c])
            draw.rectangle(
                [tuple(text_origin), tuple(text_origin + label_size)],
                fill=self.colors[c])
            draw.text(text_origin, label, fill=(0, 0, 0), font=font)
            del draw

        end = timer()
        print(end - start)
        print('この画像内の人の数は' + str(person_count) + '人です') # 追加
        return image

上記編集後に

#python3.6 yolo_video.py

と実行すればok

(416, 416, 3)
Found 6 boxes for img
pottedplant 0.86 (0, 119) (107, 367)
frisbee 0.32 (256, 419) (289, 427)
person 0.93 (305, 198) (402, 322)
person 0.99 (224, 191) (337, 366)
person 0.99 (177, 211) (308, 399)
person 1.00 (58, 180) (340, 578)
3.2963331720002316
この画像内の人の数は4人です

keras-yolo3で解析されたoutput_image.jpgが保存される。

物体検出で前回の記事で時間がかかりすぎて使えないと書いたのですが
それはcronで定期実行する場合で、どうしてもプログラムを初めから実行することになるので初回実行時はインスタンスの作成?に時間がかかり1,2分待つことになるのですが
上記コードのように無限ループでsleepを入れることでこういう定期実行を実現するようです。そうすることで解析自体は5秒くらいで終わります。

次はopencvによる画像解析を自作のカスケード分類器を使ってやってみたいと思います。

シェアする

  • このエントリーをはてなブックマークに追加

フォローする