SE_BOKUのまとめノート的ブログ

SE_BOKUが知ってること・勉強したこと・考えたことetc

静止画像(写真)から物体検出した結果をきりとり保存する方法/tensorflow v1+keras-yolo3

f:id:arakan_no_boku:20191222204916p:plain

※この記事はtensorflow1.0の環境でしか動作しません。 

目次

静止画像(写真)から物体検出した結果をきりとる

keras-yolo3を使って、静止画像(写真)から写っている物体を検出し、検出した物体を四角形で囲むという処理を行いました。

arakan-pgm-ai.hatenablog.com

処理した結果は、例えば、このようになります。

f:id:arakan_no_boku:20220401225357p:plain

フォークとかグラスとかを識別しているのがわかります。

この識別結果だけを切り取って画像ファイルに保存できれば、面白いなと思ったのでやってみることにしました。

 

切り取る領域はコンソールログから得る

 物体検出した画像を切りとる機能はyolo3にありません。

ただ、実行時にコンソールに表示されるログに使えそうな情報は出力されます。

例えば、こんな情報です。

Input image filename:sample.jpg
(416, 416, 3)
Found 11 boxes for img
diningtable 0.97 (38, 7) (777, 471)
cake 0.71 (318, 224) (493, 398)
knife 0.57 (657, 256) (755, 455)
knife 0.96 (696, 248) (794, 472)
knife 0.98 (617, 254) (686, 477)
fork 0.50 (60, 292) (95, 436)
fork 0.76 (84, 279) (135, 505)
fork 0.93 (20, 297) (71, 504)
wine glass 0.59 (1, 0) (72, 81)
wine glass 0.98 (458, 5) (586, 206)
wine glass 0.99 (528, 10) (681, 253)
1.1462113127001103

「Found 11 boxes for img」に続く行に、検出した画像の情報がでてます。

例えば「cake 0.71 (318, 224) (493, 398)」なら。

  • cake:検出したものの名称
  • 0.71:一致確率
  • (318,224)(493,398):検出した画像の位置情報

です。

そして、この位置情報は「left, top, right, bottom」の順で並んでいます。

これは使えます。 

とりあえず検出した物体を切り取る 

画像を切り取るだけなら、Pythonの画像処理ライブラリPillow(PIL)のImageモジュールにあるメソッドcrop()でできます。

im.crop*1

 みたいな感じですね。

上記のログの一部分「cake 0.71 (318, 224) (493, 398)」を利用して、切り抜きをするのであれば、こんなコーディングになります。

    im = Image.open('./sample.jpg')
    croped = im.crop((318, 224, 493, 398))
    croped.save('./' + 'cake.jpg')

見ての通り、 ログの情報をソースにそのまま打ち込んでいるだけです。

でもこれだけで。

f:id:arakan_no_boku:20190314001042j:plain

上記の画像から、cakeと誤認識している「ナフキン」の画像が切り取れてます。

f:id:arakan_no_boku:20190317130617j:plain

検出した物体の切り取りを少しだけ汎用的にしたサンプル

上記のやり方で、手作業でちょこちょこやれば切り取れますが、いまいちスマートではないです。

やっぱり、11個検出されてた画像を全部切り出すのに、11回手作業はいやなので、もう少し汎用的にソースを書き直してみます。

from PIL import Image
import re

logstr='''diningtable 0.97 (38, 7) (777, 471)
cake 0.71 (318, 224) (493, 398)
knife 0.57 (657, 256) (755, 455)
knife 0.96 (696, 248) (794, 472)
knife 0.98 (617, 254) (686, 477)
fork 0.50 (60, 292) (95, 436)
fork 0.76 (84, 279) (135, 505)
fork 0.93 (20, 297) (71, 504)
wine glass 0.59 (1, 0) (72, 81)
wine glass 0.98 (458, 5) (586, 206)
wine glass 0.99 (528, 10) (681, 253)
'''
#元画像を開く
im = Image.open('./sample.jpg')
#ヒアドキュメントを改行で分割
list_str = logstr.splitlines()
cnt = 0
#一行ずつ処理
for s in list_str:
    cnt += 1
    #空白以外の余分な文字「(),」を消す
    rs = re.sub('[\(\)\,]','',s)
    #空白で分割する
    ls = re.split('[\s]',rs)
    ol = []
    nm = ""
    for x in ls:
        #数字はそのまま処理する
        if(x.isalpha()):
            nm = nm + x
        #数字のみで構成されていないものは名称とみなして連結する
        else:
            ol.append(x)
    ol.append(nm)
    #画像を切り取って保存する
    croped = im.crop((int(ol[1]), int(ol[2]),int(ol[3]),int(ol[4])))
    croped.save('./' + str(cnt) + '_' + ol[5] + '.jpg')

ソースの補足説明です。

まず、ログの検出結果部分だけを「改行ごと」、ヒアドキュメントに貼り付けて、変数にセットしています。

logstr = '''diningtable 0.97 (38, 7) (777, 471)
cake 0.71 (318, 224) (493, 398)
knife 0.57 (657, 256) (755, 455)
knife 0.96 (696, 248) (794, 472)
knife 0.98 (617, 254) (686, 477)
fork 0.50 (60, 292) (95, 436)
fork 0.76 (84, 279) (135, 505)
fork 0.93 (20, 297) (71, 504)
wine glass 0.59 (1, 0) (72, 81)
wine glass 0.98 (458, 5) (586, 206)
wine glass 0.99 (528, 10) (681, 253)
'''

 ログをコピーしてきて、貼り付けるだけ・・にするためです。

Pythonのヒアドキュメントはこういう時に、超便利でいいですね。

それを改行コードで分割して1行ずつのリストにして、1行分ずつ処理をするわけです。

list_str = logstr.splitlines()

各行は、基本空白で区切られてますが、余分な情報があります。

「(」と「)」と「,」 です。

なので、それらを除去してから、空白で分割するという2手間かけてます。

#空白以外の余分な文字「(),」を消す
rs = re.sub('[\(\)\,]','',s)
#空白で分割する
ls = re.split('[\s]',rs)

あとは、結果のリストに追加していって、crop()メソッドで処理するだけです。

ただ、名称が「wine glass」のように空白区切りのものがあるので、数字だけで構成されていないものは、名前とみなして連結しているわけです。

for x in ls:
    #数字はそのまま処理する
    if(x.isalpha()):
        nm = nm + x
        #数字のみで構成されていないものは名称とみなして連結する
    else:
        ol.append(x)
        ol.append(nm)

まあ、こんなもんですね。

現在のkeras-yolo3のログの仕様にあわせたオーダーメードみたいな処理ですけど、いちおう機能します。

サンプルで切り取った物体の画像です

元の物体検出結果の画像がこうです。

f:id:arakan_no_boku:20220401225357p:plain

そこから、きりとった画像です。

f:id:arakan_no_boku:20190317134232j:plain

まあまあじゃないですかね。

ではでは。

*1:left, top, right, bottom