Kerasディープラーニング戦闘(14)-R-CNNターゲットを最初から検出

0.序文

R-CNNRegions with CNN features)は、R-CNN一連あり、「ディープラーニング」と従来の「コンピュータービジョン」を組み合わせて、候補領域Region proposalに基づいてターゲットオブジェクトを検出します( )。オブジェクト検出の基礎
では、候補領域の概念が画像から候補領域を生成する方法を見てきました。このセクションでは、候補領域を利用して、画像内のターゲットオブジェクトの検出とローカリゼーションを完了します。

1.R-CNNターゲット検出モデル

1.1データセット分析

モデルをトレーニングするには、データセットVOCtrainval_11-May-2012.tarをダウンロードして使用します。このデータセットには、画像内のオブジェクトと、オブジェクト検出で一般的に使用されるデータセットの1つである対応するバウンディングボックスが含まれています。
まず、データセットの関連情報を分析して、ニューラルネットワークモデルをより適切に構築します。例として、次の画像と、画像内の対応するバウンディングボックスの座標とオブジェクトカテゴリを取り上げます。

データセットの例

オブジェクトのクラスラベルとバウンディングボックスの座標はXMLファイルXML次のようにファイルから抽出できます。

XMLサンプルファイル
xml["annotation"]["object"]リストの場合、画像には複数のターゲットオブジェクトがあります。xml["annotation"]["object"]["bndbox"]画像に存在するターゲットオブジェクトのバウンディングボックスが含まれます。バウンディングボックスの座標には、、、、”xmin”およびが含まれます。これらは”ymin”、それぞれバウンディングボックスの左上隅とます画像に存在するオブジェクトのクラスラベルを抽出するために使用できます。簡単にするために、このセクションでは、使用するデータセットには、画像ごとにローカライズされるターゲットオブジェクトが1つだけ含まれています。”xmax””ymax”(xmin, ymin)(xmax, ymax)xml["annotation"]["object"]["name"]

1.2R-CNNモデル分析

トレーニングデータセットを理解した後、次に、R-CNNターゲットます。

  • 画像内の候補領域を抽出します
  • 候補領域と実際のターゲットオブジェクト領域の間の距離を計算します。
    • 基本的に、候補領域とオブジェクトの実際の領域の交差率Intersection over Union、 )は、距離メトリックとして計算されますIoU
  • 交差点と和集合の比率が特定のしきい値よりも大きい場合、候補領域には特定するターゲットが含まれていると見なすことができます。それ以外の場合は、特定するターゲットが含まれていないと見なされます。
    • これは、候補領域ごとにラベルを生成することを意味します(配置するターゲットが含まれているかどうかに関係なく)。ここで、候補領域の画像が入力であり、出力ラベルはIoUしきい値
  • 画像のサイズを変更し、各候補領域画像をVGG16モデル候補領域画像の特徴を抽出します
  • 候補領域の位置とターゲットオブジェクトの実際の位置を比較して候補領域の境界ボックスを修正し、それを後続のトレーニングの新しい候補領域として使用します
  • 分類モデルを確立して、候補領域の特徴を、その領域にターゲットオブジェクトが含まれているかどうかにマッピングします。
  • 候補領域の画像を使用して、候補オブジェクトの入力特徴を、候補バウンディングボックスを修正するために使用されるオブジェクトの正確なバウンディングボックスを抽出するために必要なオフセットにマッピングする回帰モデルを構築します。
  • 最終的に取得されたバウンディングボックスで非最大抑制を使用します。
    • 非最大抑制を使用すると、重複する多数の候補領域が削減され1、オブジェクトを含む可能性が最も高い候補のみが保持されます。
  • 非最大抑制を使用することにより、上記の戦略を拡張して、複数のターゲットオブジェクトを含む画像のオブジェクト検出モデルを構築できます。

上記のプロセスの概略図を以下に示します。

R-CNN

次に、前のセクションで紹介したに基づいR-CNNたオブジェクト検出アルゴリズムを実装し。

2.R-CNNオブジェクト検出を最初から実装します

2.1トレーニングデータセットを表示する

関連するライブラリをインポートし、ターゲット検出モデルを構築する前に、まずデータセットディレクトリを構築し、データセットの合計データ量を表示します。

import json, scipy, os, time
import sys, cv2, xmltodict
import matplotlib.pyplot as plt
# import tensorflow as tf
import selectivesearch
import numpy as np
import pandas as pd
import gc, scipy, argparse
from copy import deepcopy

xmls_root ="VOCtrainval_11-May-2012/VOCdevkit/VOC2012/"  # 数据集父目录
annotations = xmls_root + "Annotations/"
jpegs = xmls_root + "JPEGImages/"
XMLs = os.listdir(annotations)
print(XMLs[:10])
print(len(XMLs))

次に、データセットのアイデアを取得するには、データセット内のXMLファイルxmltodictし、ライブラリを使用して解析し、出力を表示します:

ix = np.random.randint(len(XMLs))
sample_xml = XMLs[ix]
sample_xml = '{}/{}'.format(annotations, sample_xml)
with open(sample_xml, "rb") as f:    # notice the "rb" mode
    d = xmltodict.parse(f, xml_attribs=True)
print(d)

ご覧のとおり、入力関連情報は次のとおりです。

OrderedDict([('annotation', OrderedDict([('filename', '2010_000495.jpg'), ('folder', 'VOC2012'), ('object', OrderedDict([('name', 'motorbike'), ('bndbox', OrderedDict([('xmax', '482'), ('xmin', '13'), ('ymax', '328'), ('ymin', '32')])), ('difficult', '0'), ('occluded', '0'), ('pose', 'Left'), ('truncated', '0')])), ('segmented', '0'), ('size', OrderedDict([('depth', '3'), ('height', '375'), ('width', '500')])), ('source', OrderedDict([('annotation', 'PASCAL VOC2010'), ('database', 'The VOC2010 Database'), ('image', 'flickr')]))]))])

2.2ターゲット検出モデルR-CNNをゼロから実現する

(1)データセットに慣れたら、ターゲット検出モデルをゼロから実装し始めることができますR-CNN。まず、候補領域を計算IoUおよび(具体的な計算方法については、 「ターゲット検出の基本」を参照してください) 。

def calc_iou(candidate, current_y, img_shape):
    boxA = deepcopy(candidate)
    boxB = deepcopy(current_y)
    boxA[2] += boxA[0]
    boxA[3] += boxA[1]
    iou_img1 = np.zeros(img_shape)
    iou_img1[int(boxA[1]):int(boxA[3]), int(boxA[0]):int(boxA[2])] = 1
    iou_img2 = np.zeros(img_shape)
    iou_img2[int(boxB[1]):int(boxB[3]), int(boxB[0]):int(boxB[2])] = 1
    iou = np.sum(iou_img1*iou_img2) / (np.sum(iou_img1) + np.sum(iou_img2) - np.sum(iou_img1*iou_img2))
    return iou
    
def extract_candidates(img):
    """
    排除所有占图像面积不到 5% 的候选区域
    """
    img_lbl, regions = selectivesearch.selective_search(img, scale=100, min_size=100)
    img_area = np.prod(img.shape[:2])
    candidates = []
    for region in regions:
        if region['rect'] in candidates:
            continue
        if region['size'] < (0.05 * img_area):
            continue
        x, y, w, h = region['rect']
        candidates.append(list(region['rect']))
    return candidates

(2)事前にトレーニングされたVGG16モデル画像の特徴を抽出します。

from keras.applications import vgg16
from keras.utils.vis_utils import plot_model
from keras.applications.vgg16 import preprocess_input
vgg16_model = vgg16.VGG16(include_top=False, weights='imagenet')

(3)次に、オブジェクト検出モデルに必要な入力と対応する出力のリストを作成します。簡単にするために、1つのターゲットオブジェクトを含む画像のみを考慮します。
まず、必要なリストを初期化します。

train_data_length = 1000
final_cls = []
final_delta = []
iou_list = []
imgs = []

画像を繰り返し処理し、1つのターゲットオブジェクトを含む画像のみを検討します。

for ix, xml in enumerate(XMLs[:train_data_length]):
    print('Extracted data from {} xmls...'.format(ix), end='\r')
    xml_file = annotations + xml
    fname = xml.split('.')[0]
    with open(xml_file, 'rb') as f:
        xml = xmltodict.parse(f, xml_attribs=True)
        l = []
        if isinstance(xml["annotation"]["object"], list):
            # 本任务仅考虑包含单目标对象的图像
            continue

上記のコードでは、最初にトラバースしてxmlファイル画像に複数のオブジェクトが含まれているかどうかを確認します(xml["annotation"]["object"]の出力がリストの場合、画像には複数のオブジェクトが含まれています)。
オブジェクトの位置座標を正規化します。これは、正規化されたバウンディングボックスが比較的安定しており、画像の形状が変わっても正規化されたオブジェクトのバウンディングボックスが変わらないためです。たとえば、オブジェクトxminが画像xの軸全体20%yにある場合、画像を拡大縮小しても同じになります(ただし、生のピクセル値の位置を処理する場合は、画像のときに値が変更されますスケーリングされます):50%xmin

        bndbox = xml["annotation"]["object"]["bndbox"]
        for key in bndbox:
            bndbox[key] = float(bndbox[key])
        x1, x2, y1, y2 = [bndbox[key] for key in ['xmin', 'xmax', 'ymin', 'ymax']]

        img_size = xml["annotation"]["size"]
        for key in img_size:
            img_size[key] = float(img_size[key])
        w, h = img_size['width'], img_size['height']

        # 根据图片尺寸归一化边界框坐标
        x1 /= w
        x2 /= w
        y1 /= h
        y2 /= h
        label = xml["annotation"]["object"]["name"]
        y = [x1, y1, x2-x1, y2-y1, label] # 图像左上角x和y坐标,图像宽度和高度,目标对象标签

extract_candidates()関数を使用して、画像内の候補領域を抽出します。

        # 图片路径
        file_name = jpegs + fname + '.jpg'
        # 读取并预处理图片
        img = cv2.resize(cv2.imread(file_name), (224, 224))
        candidates = extract_candidates(img)

上記のコードでは、このextract_candidates関数て、サイズ変更された画像の領域提案を抽出します。
候補領域をトラバースして、各候補領域と画像内のオブジェクトの実際の領域境界ボックスとの交差率を計算し、実際の境界ボックスと候補オブジェクトの間の対応するオフセットを計算して、回帰モデルを修正します。候補領域の境界ボックス:

        for jx, candidate in enumerate(candidates):
            current_y2 = [int(i*224) for i in [x1, y1, x2, y2]]
            iou = calc_iou(candidate, current_y2, (224, 224))
            candidate_region_coordinates = c_x1, c_y1, c_w, c_h = np.array(candidate)/224

            dx = c_x1 - x1
            dy = c_y1 - y1
            dw = c_w - (x2 - x1)
            dh = c_h - (y2 - y1)

            final_delta.append([dx, dy, dw, dh])

計算では、事前トレーニングを使用して各候補領域の特徴をVGG16 抽出IoUし、実際のバウンディングボックスとの相関に基づいてクラスラベルを割り当てます。

            if iou > 0.3:
                final_cls.append(label)
            else:
                final_cls.append('background')
            
            # 使用VGG16作为特征提取网络
            l = int(c_x1 * 224)
            r = int((c_x1 + c_w) * 224)
            t = int(c_y1 * 224)
            b = int((c_y1 + c_h) * 224)

            img2 = img[t:b,l:r,:3]
            img3 = cv2.resize(img2,(224,224)) # /255
            img3 = preprocess_input(img3.reshape(1,224,224,3))
            img4 = vgg16_model.predict(img3)
            imgs.append(img4)
            iou_list.append(iou)

入力配列と出力配列を作成します。

targets = pd.DataFrame(final_cls, columns=['label'])
# 使用get_dummies方法获取标签,因为这些类别值是分类文本值
labels = pd.get_dummies(targets['label']).columns
y_train = pd.get_dummies(targets['label']).values.astype(float)
x_train = np.array(imgs)
x_train = x_train.reshape(x_train.shape[0], x_train.shape[2], x_train.shape[3], x_train.shape[4])

(4)モデルをビルド、コンパイルし、モデルを適合させます。

from keras.models import Sequential
from keras.layers import Flatten, Dropout, Dense, Conv2D
model = Sequential()
model.add(Flatten(input_shape=((7, 7, 512))))
model.add(Dense(1024, activation='relu'))
model.add(Dense(y_train.shape[1], activation='softmax'))

model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['acc'])

history = model.fit(x_train/x_train.max(), y_train,
                validation_split=0.1,
                epochs=10,
                batch_size=32,
                verbose=1)

テストデータセットの分類精度を達成できます80%入力配列x_train.max()を正規化のために除算します。入力データを正規化すると、モデルをより高速にトレーニングできます。
(5)画像分類モデルの結果を検証するために、データセットから画像を選択します(トレーニングデータセットの画像を使用しないように注意してください)。
テスト用の画像を選択します。

import matplotlib.patches as mpatches

ix = np.random.randint(train_data_length, len(XMLs))
filename = jpegs + XMLs[ix].replace('xml', 'jpg')

テスト関数test_predictionsを記述し、テスト画像に対して画像前処理を実行して候補を抽出し、サイズ変更された候補に対してモデル予測を実行し、予測された背景クラス領域を除外し、最後に背景クラスを除くオブジェクトクラスの領域境界を最も高い確率で描画します最終候補地域として:

def test_predictions(file_name):
    image = cv2.imread(file_name)
    img = cv2.resize(image, (224, 224))
    candidates = extract_candidates(img)

上記のコードでは、入力画像のサイズを変更し、そこから候補領域を抽出します。次に、読み取った画像をプロットし、予測されたクラスとそれに対応する確率を格納するリストを初期化します。

    _, ax = plt.subplots(1, 2)
    ax[0].imshow(cv2.cvtColor(image, cv2.COLOR_BGR2RGB))
    ax[0].set_title(file_name.split('/')[-1])

    pred_class = []
    pred = []

次に、これらの候補領域をループしてサイズを変更し、トレーニング済みVGG16モデル。さらに、VGG16のて、候補領域の画像がさまざまなカテゴリに属する​​確率を取得します。

    for ix, candidate in enumerate(candidates):
        l, t, w, h = np.array(candidate).astype(int)
        img2 = img[t:t+h, l:l+w, :3]
        img3 = cv2.resize(img2, (224, 224)) #/255
        img3 = preprocess_input(img3.reshape(1, 224, 224, 3))
        img4 = vgg16_model.predict(img3)
        final_pred = model.predict(img4/x_train.max())
        pred.append(np.max(final_pred))
        pred_class.append(np.argmax(final_pred))

次に、抽出された候補領域オブジェクトに非背景オブジェクトが含まれる可能性が最も高い領域を最終候補領域とします。ここで、予測ラベル1のあるは背景画像に対応し、これに対応する座標を計算します。元の画像の領域:

    pred = np.array(pred)
    pred_class = np.array(pred_class)
    pred2 = pred[pred_class!=1]
    pred_class2 = pred_class[pred_class!=1]
    candidates2 = np.array(candidates)[pred_class!=1]
    x, y, w, h = candidates2[np.argmax(pred2)]
    new_x = int(x * image.shape[1] / 224)
    new_y = int(y * image.shape[0] / 224)
    new_w = int(w * image.shape[1] / 224)
    new_h = int(h * image.shape[0] / 224)

最後に、画像と最終候補領域の長方形の境界ボックスを描画します。

    ax[1].set_title(labels[pred_class2[np.argmax(pred2)]])
    ax[1].imshow(cv2.cvtColor(image, cv2.COLOR_BGR2RGB))
    rect = mpatches.Rectangle((new_x, new_y), new_w, new_h, fill=False, edgecolor='red', linewidth=1)
    ax[1].add_patch(rect)

(6)新しい画像をパラメータ値として使用して、test_predictions関数。

file_name = '10.png'
test_predictions(file_name)
plt.show()

試験結果

ご覧のとおり、モデルは画像内のオブジェクトのクラスを正確に計算できますが、ターゲットオブジェクト(最終候補領域)を含む可能性が最も高いバウンディングボックスを修正する必要があります。これはまさに次のステップで行う必要があることです-ターゲットオブジェクトのバウンディングボックスを調整します。

2.3ターゲットオブジェクトのバウンディングボックスを調整します

(1)事前にトレーニングされたVGG16抽出されたます。

model2 = Sequential()
model2.add(Flatten(input_shape=((7, 7, 512))))
model2.add(Dense(1024, activation='relu'))
model2.add(Dense(4, activation='linear'))

model2.compile(loss='mean_absolute_error', optimizer='adam')
# 预测候选区域图像类别
pred = model.predict(x_train/x_train.max())
pred_class = np.argmax(pred, axis=1)

(2)バウンディングボックスのオフセットを予測するモデルを構築する場合、ターゲットオブジェクトを含む可能性のある画像の領域に対してバウンディングボックスのオフセットが予測されることを確認するだけで済みます。

for i in range(1000):
    samp=random.sample(range(len(x_train)),500)
    x_train2 = [x_train[i] for i in samp if pred_class[i]!=1]
    x_train2 = np.array(x_train2)
    final_delta2 = [final_delta[i] for i in samp if pred_class[i]!=1]
    model2.fit(x_train2/x_train.max(), np.array(final_delta2),
                validation_split = 0.1,
                epochs=1,
                batch_size=32,
                verbose=1)

上記のコードでは、入力配列データセットを反復処理し、非バックグラウンドクラスである可能性のある領域のみを含む新しいデータセットを作成します。さらに、モデルを微調整するために、前のトレーニング手順を繰り返し1000ます。
(3)画像パスをパラメータとして使用し、画像カテゴリを予測し、バウンディングボックスを修正する関数を作成します。

def test_predictions2(file_name):
    image = cv2.imread(file_name)
    img = cv2.resize(image, (224, 224))
    candidates = extract_candidates(img)
    _, ax = plt.subplots(1, 2)
    ax[0].imshow(cv2.cvtColor(image, cv2.COLOR_BGR2RGB))
    ax[0].set_title(file_name.split('/')[-1])
    pred = []
    pred_class = []
    del_new = []

    for ix, candidate in enumerate(candidates):
        l, t, w, h = np.array(candidate).astype(int)
        img2 = img[t:t+h, l:l+w, :3]
        img3 = cv2.resize(img2, (224, 224)) # /255
        img3 = preprocess_input(img3.reshape(1, 224, 224, 3))
        img4 = vgg16_model.predict(img3)
        final_pred = model.predict(img4/x_train.max())
        delta_new = model2.predict(img4/x_train.max())[0]
        pred.append(np.max(final_pred))
        pred_class.append(np.argmax(final_pred))
        del_new.append(delta_new)
    
    pred = np.array(pred)
    pred_class = np.array(pred_class)
    non_bgs = (pred_class!=1)
    pred = pred[non_bgs]
    pred_class = pred_class[non_bgs]
    del_new = np.array(del_new)
    del_new = del_new[non_bgs]
    del_pred = del_new * 224
    candidates = C = np.array(candidates)[non_bgs]
    C = np.clip(C, 0, 224)
    C[:, 2] += C[:, 0]
    C[:, 3] += C[:, 1]

    bbs_pred = candidates - del_pred
    bbs_pred = np.clip(bbs_pred, 0, 224)
    bbs_pred[:, 2] -= bbs_pred[:, 0]
    bbs_pred[:, 3] -= bbs_pred[:, 1]
    final_bbs_pred = bbs_pred[np.argmax(pred)]

    x, y, w, h = final_bbs_pred
    new_x = int(x * image.shape[1] / 224)
    new_y = int(y * image.shape[0] / 224)
    new_w = int(w * image.shape[1] / 224)
    new_h = int(h * image.shape[0] / 224)

    ax[1].imshow(cv2.cvtColor(image, cv2.COLOR_BGR2RGB))
    rect = mpatches.Rectangle((new_x,new_y), new_w, new_h, fill=False, edgecolor='red', linewidth=1)
    ax[1].add_patch(rect)
    ax[1].set_title(labels[pred_class[np.argmax(pred)]])

(4)モデルをテストし、1つのオブジェクトのみを含むテスト画像も抽出します。

single_object_images = []
for ix, xml in enumerate(XMLs[N:]):
    xml_file = annotations + xml
    fname = xml.split('.')[0]
    with open(xml_file, 'rb') as f:
        xml = xmltodict.parse(f, xml_attribs=True)
        l = []
        if isinstance(xml["annotation"]["object"], list):
            continue
        single_object_images.append(xml["annotation"]["filename"])
        if ix > 100:
            break

画像を繰り返し処理し、単一のターゲットオブジェクトを含む画像を特定して見つけます。
(5)最後に、テストイメージを予測します。

# 使用测试集图像
# ix = 2
# filename = jpegs + single_object_images[ix]
# test_predictions2(filename)
# plt.show()
# 使用其他图像
file_name = '10.png'
test_predictions2(file_name)
plt.show()

バウンディングボックスを調整する

上に示したように、バウンディングボックス調整モデルを使用すると、バウンディングボックスを修正できます。ただし、バウンディングボックスをさらに修正する必要があり、これを実現するためにさらに多くのデータをトレーニングできます。

3.非最大抑制(NMS)

このセクションで実装されているR-CNNオブジェクトバックグラウンド以外の候補領域のみを考慮し、オブジェクト検出タスクにはオブジェクト確率が最も高い候補領域のみを使用します。ただし、画像内に複数のオブジェクトが存在する場合、上記の戦略を使用しても、複数のオブジェクトを見つけることはできません。
画像内のできるだけ多くのオブジェクトを抽出できるようにするために、非最大抑制を使用して候補領域から候補をフィルタリングし、複数のオブジェクトの検出を実行できます。

3.1非最大抑制

ターゲット検出モデルでは、通常、複数の候補領域が取得され、これらの候補領域のカテゴリとオフセットが予測され、最後に、候補領域とオフセットを使用して予測された境界ボックスが取得されます。ただし、候補領域の数が多い場合は、同じオブジェクトでより類似した予測境界ボックスが取得される可能性があります。ノットを簡潔に保つために、通常、同様の予測バウンディングボックスを削除する必要があります。一般的な方法は、非最大抑制(non-maximum suppressionNMS)と呼ばれます。

3.2非最大抑制アルゴリズムフロー

non-maximum suppression非最大抑制を実装するためのさまざまなアルゴリズムがあります。実装する非最大抑制( 、 )アルゴリズムNMS次のとおりです。

  • (1)画像から候補領域を抽出します
  • (2)候補領域を形成し、画像に含まれるオブジェクトのクラスを予測する
  • (3)候補オブジェクトがバックグラウンドクラスオブジェクトでない場合、候補オブジェクトを保持します
  • (4)バックグラウンド以外のすべてのクラス候補について、オブジェクトを含む確率に従ってランク付けします。
  • (5)に従ってIoU、最初の候補オブジェクト(降順でクラス確率でソートされたシーケンス)を他のすべての候補と比較します
  • (6)他の候補領域と最初の候補領域との重複領域が特定のしきい値より大きい場合は、それを破棄します
  • (7)残りの候補オブジェクトの中で、オブジェクトを再び含む可能性が最も高い候補領域オブジェクトを検討します。
  • (8)步骤 (7) フィルタリングされた候補領域リストで、最初の候補オブジェクトを残りの候補領域オブジェクトと引き続き比較します。
  • 步骤 (5) ~ (8)(9)比較する候補がなくなるまで繰り返します
  • (10)前の手順を実行した後、最後に保持された候補オブジェクトをバウンディングボックスとして描画します

次のサブセクションでは、を使用して上記の非最大抑制アルゴリズムをPython実装ます。

3.3非最大抑制の実装

(1)画像から背景以外のオブジェクトを含むすべての領域を信頼性の高い方法で抽出します。

file_name = '8.png'
image = cv2.imread(file_name)
img = cv2.resize(image, (224, 224))
img_area = img.shape[0] * img.shape[1]
candidates = extract_candidates(img)
plt.imshow(cv2.cvtColor(image, cv2.COLOR_BGR2RGB))
plt.show()

画像を読み込む

(2)候補領域を前処理します-それらをVGG16モデル、次に各候補領域のクラスと領域の境界ボックスを予測します。

pred = []
pred_class = []
del_new = []

for ix, candidate in enumerate(candidates):
    l, t, w, h = np.array(candidate).astype(int)
    img2 = img[t:t+h, l:l+w, :3]
    img3 = cv2.resize(img2, (224, 224)) # /255
    img3 = preprocess_input(img3.reshape(1, 224, 224, 3))
    img4 = vgg16_model.predict(img3)
    final_pred = model.predict(img4/x_train.max())
    delta_new = model2.predict(img4/x_train.max())[0]
    pred.append(np.max(final_pred))
    pred_class.append(np.argmax(final_pred))
    del_new.append(delta_new)

pred = np.array(pred)
pred_class = np.array(pred_class)

(3)非背景候補領域とそれに対応するバウンディングボックスオフセットを抽出し、フィルタリングして、すべての非背景候補領域のカテゴリ確率、カテゴリ、およびバウンディングボックスオフセットを取得します(予測されるカテゴリは背景クラスを1表し)。

non_bgs = ((pred_class!=1))
pred = pred[non_bgs]
pred_class = pred_class[non_bgs]

del_new = np.array(del_new)
del_new = del_new[non_bgs]
del_pred = del_new * 224

(4)バウンディングボックスのオフセット値を使用して候補領域を修正します。ただしxmaxymax座標がを超えないように224します。さらに、バウンディングボックスの幅と高さが負にならないようにする必要があります。

candidates = C = np.array(candidates)[non_bgs]
C = np.clip(C, 0, 224)
C[:, 2] += C[:, 0]
C[:, 3] += C[:, 1]

bbs_pred = candidates - del_pred
bbs_pred = np.clip(bbs_pred, 0, 224)

bbs_pred[:, 2] -= bbs_pred[:, 0]
bbs_pred[:, 3] -= bbs_pred[:, 1]
bbs_pred = np.clip(bbs_pred, 0, 224)

bbs_pred2 = bbs_pred[(bbs_pred[:, 2] > 0) & (bbs_pred[:, 3] > 0)]
pred = pred[(bbs_pred[:, 2] > 0) & (bbs_pred[:, 3] >0)]
pred_class = pred_class[(bbs_pred[:, 2] > 0) & (bbs_pred[:, 3] > 0)]

(5)最後に、ターゲットオブジェクトの画像とバウンディングボックスを描画します。

fig, ax = plt.subplots(ncols=1, nrows=1, figsize=(6, 6))
ax.imshow(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))
for ix, (x, y, w, h) in enumerate(bbs_pred2):
    new_x = int(x * image.shape[1] / 224)
    new_y = int(y * image.shape[0] / 224)
    new_w = int(w * image.shape[1] / 224)
    new_h = int(h * image.shape[0] / 224)
    rect = mpatches.Rectangle((new_x,new_y), new_w, new_h, fill=False, edgecolor='red', linewidth=1)

    ax.add_patch(rect)
plt.show()

候補エリア

(6)バウンディングボックスで非最大抑制を実行するnms_boxesには、しきい値(2つのバウンディングボックスの最小交差)、バウンディングボックスの座標、および各バウンディングボックスに関連付けられた確率値に従って実行する関数を定義しますNMS
各バウンディングボックスの、、、および値、それらに対応する領域を計算しx、確率で並べ替えます。ywh

def nms_boxes(threshold, boxes, scores):
    x = boxes[:, 0]
    y = boxes[:, 1]
    w = boxes[:, 2]
    h = boxes[:, 3]
    areas = w * h
    order = scores.argsort()[::-1]

確率が最も高い候補領域と残りの候補領域の交差率を計算します。

    keep = []
    while order.size > 0:
        i = order[0]
        keep.append(i)
        xx1 = np.maximum(x[i], x[order[1:]])
        yy1 = np.maximum(y[i], y[order[1:]])
        xx2 = np.maximum(x[i]+w[i], x[order[1:]]+w[order[1:]])
        yy2 = np.maximum(y[i]+h[i], y[order[1:]]+h[order[1:]])
        w1 = np.maximum(0.0, xx2-xx1+1)
        h1 = np.maximum(0.0, yy2-yy1+1)
        inter = w1 * h1
        iou = inter / (areas[i] + areas[order[1:]] - inter)

IoU指定されたしきい値未満の候補領域を選択NMSし、前のセクションで紹介したアルゴリズムフローに従って候補領域リストを循環的にフィルタリングし、リスト内でカテゴリ確率が最も高い候補領域に従って候補領域リストの次のグループを決定します。

        inds = np.where(iou <= threshold)[0]
        order = order[inds+1]

保存する必要のある候補インデックスを返します。

    keep = np.array(keep)
    return keep

計算を実行NMSするnms_boxes

keep_box_ixs = nms_boxes(0.3, bbs_pred2, pred)

print(pred, keep_box_ixs)

実行NMS後された他のすべての同様の候補領域バウンディングボックスが削除されたことがわかります。

fig, ax = plt.subplots(ncols=1, nrows=1, figsize=(6, 6))
ax.imshow(cv2.cvtColor(image, cv2.COLOR_BGR2RGB))

for ix, (x, y, w, h) in enumerate(bbs_pred2):
    if ix not in keep_box_ixs:
        continue
    new_x = int(x * image.shape[1] / 224)
    new_y = int(y * image.shape[0] / 224)
    new_w = int(w * image.shape[1] / 224)
    new_h = int(h * image.shape[0] / 224)
    rect = mpatches.Rectangle(
        (new_x, new_y), new_w, new_h, fill=False, edgecolor='red', linewidth=1,)

    ax.add_patch(rect)
    centerx = new_x# + new_w/2
    centery = new_y + 20# + new_h - 10
    plt.text(centerx, centery,labels[pred_class[ix]]+" "+str(round(pred[ix],2)),fontsize = 20,color='red')

plt.show()

NMS実行後の結果画像

まとめ

R-CNNこれは、候補領域に基づく古典的なターゲット検出アルゴリズムであり、ターゲット検出の分野に畳み込みニューラルネットワークを導入します。このペーパーでは、最初にR-CNNモデル、次にを使用Kerasしてゼロからターゲット検出モデルを実装R-CNNし、最後に非最大抑制を導入して同様の予測バウンディングボックスを削除します。

シリーズリンク

Kerasディープラーニングプラクティス(1)-ニューラルネットワークの基盤とモデルトレーニングプロセスの詳細な説明
Kerasディープラーニングプラクティス(2)-Kerasを使用してニューラルネットワークを構築する
Kerasディープラーニングプラクティス(3)-ニューラルネットワークパフォーマンス最適化テクノロジー
Kerasディープラーニングプラクティス( 4)-ディープラーニングで一般的に使用されるアクティベーション関数と損失関数の詳細な
説明(7)-畳み込みニューラルネットワークの詳細な説明と実装Kerasディープラーニングの実践(8)-データ拡張を使用したニューラルネットワークのパフォーマンスの向上Kerasディープラーニングの実践(9 )-畳み込みニューラルネットワークの制限Kerasディープラーニングプラクティス(10))-転送学習Kerasディープラーニングプラクティス(11)-ニューラルネットワーク中間層出力の視覚化Kerasディープラーニングプラクティス(12)-顔の特徴点の検出Kerasディープラーニングプラクティス(13 )-ターゲット検出の基本の詳細な説明







おすすめ

転載: blog.csdn.net/LOVEmy134611/article/details/125566214