物体検出を試してみた

5 min read読了の目安(約4700字

勉強のため物体検出をやってみましたのでまとめます。

物体検出は、デジタル画像処理やコンピュータビジョンに関連する技術の一つで、デジタル画像・動画内に映っている特定のクラス(人間、建物、車といったカテゴリー)の物体を検出するものである。

https://ja.wikipedia.org/wiki/物体検出#:~:text=物体検出(ぶったいけん,検出するものである。

TorchVision の FastRCNNPredictor を使用しています。

FastRCNN
RoI pooling layerという、SPPのpyramid構造を取り除いたシンプルな幅可変poolingを行う
classification/bounding box regressionを同時に学習させるための multi-task loss によって1回で学習ができるようにする(ad hocでない)
オンラインで教師データを生成する工夫
multi-task lossの導入により、Back Propagationが全層に適用できるようになったため、全ての層の学習が可能に。
実行速度は、VGG16を用いたR-CNNより9倍の学習速度、213倍の識別速度で、 SPPnetの3倍の学習速度、10倍の識別速度を達成。

https://qiita.com/mshinoda88/items/9770ee671ea27f2c81a9

データセットはCOCOの2017ValImagesを使っています。

コードは下記サイトのものを基本的に使用しています。

https://basicincome30.com/pytorch-od-pretrained-model-image

Google Colab で実行できるようにしていますので、こちらをご参照ください。

Open In Colab

データセットの画像の表示

データセットの画像からサンプルベースで5つに絞っています。

images = glob.glob('val2017/*.jpg')
images = random.sample(images, 5)

SIZE = 360 # 画像の縦幅を指定

for i in tqdm(range(len(images))):
    image = cv2.imread(images[i])

    # 表示サイズ
    plt.figure(figsize = (1,1))

    # リサイズ
    height = image.shape[0]
    width = image.shape[1]

    # サイズがバラバラな画像の縦をそろえる
    image = cv2.resize(image , (int(width / height * SIZE), SIZE))
    cv2_imshow(image)
    plt.axis('off')
    plt.show()

以下はそのうちの2つ。

推論

元の参考サイトでは推論に用いる画像は2つで1つずつ画像を指定していましたが、model内の引数を内包表記にして複数の画像を推論できるようにしました。

# 画像の変換
transform = transforms.Compose([
    torchvision.transforms.ToTensor()
])

score_threshold = 0.9
 
model.eval()
with torch.no_grad():
    predictions = model(
        [transform(cv2.imread(path)).to(device) for path in images]
    )
 
    # filter out predictions with low scores
    _predictions = []
    for pred in predictions:
        mask = pred['scores'] >= score_threshold
        _predictions.append(
            {
                'boxes': pred['boxes'][mask],
                'labels': pred['labels'][mask],
                'scores': pred['scores'][mask],
            }
        )
    predictions = _predictions

物体検出で表示する文字やバウンディングボックスの設定

バウンディングボックス
画像、シェイプ、テキストを囲む長方形の枠線です。

https://helpx.adobe.com/jp/photoshop-elements/key-concepts/bounding-box.html

OpenCVで画像に表示する文字はスケールが同じでもフォントによってサイズが異なるそうなので注意しましょう。

色や文字の太さも調整するといいでしょう。

https://hawk-tech-blog.com/python-opencv-draw-string/

http://opencv.jp/opencv-2svn/cpp/drawing_functions.html
font = cv2.FONT_HERSHEY_PLAIN
font_scale = 2 # フォントサイズ
color = (255, 255, 255) # フォントやバウンディングボックスの色
thickness = 2 # フォントの太さ
delta = 3

物体検出

inputs = [cv2.imread(path) for path in images]
for i,img in enumerate(inputs):
 
    for (_box, _label, _score) in zip(predictions[i]['boxes'], 
        predictions[i]['labels'], predictions[i]['scores']):
 
        box = {k:int(v) for (k,v) in zip(['x0', 'y0', 'x1', 'y1'], _box.tolist())}

        label = _label.item()
        label_name = COCO_INSTANCE_CATEGORY_NAMES[label]
        score = _score.item()
 
        img = cv2.rectangle(img, (box['x0'], box['y0']), (box['x1'], box['y1']), 
                            color, thickness)
        img = cv2.putText(img, '{} {}'.format(label_name, '{:.1%}'.format(score)), 
                (box['x0'], box['y0']-delta), 
                font, font_scale, color, thickness, cv2.LINE_AA)
 
    # 表示サイズ
    plt.figure(figsize = (1,1))

    # リサイズ
    height = img.shape[0]
    width = img.shape[1]

    # サイズがバラバラな画像の縦をそろえる
    img = cv2.resize(img , (int(width / height * SIZE), SIZE)) 
    cv2_imshow(img)
    plt.axis("off")
    plt.show()

見切れたり重なっているところはうまく検出できていませんが、下記のように物体検出ができました。

既に学習済みの分類しか検出できないため、いずれファインチューニングも試してみたいと思います。

以上になります、最後までお読みいただきありがとうございました。