コンピューター ビジョン: セマンティック セグメンテーションの理論と実践

セマンティックセグメンテーション

セマンティック セグメンテーションとは、画像をいくつかの領域に分割し、各領域にセマンティック ラベルを割り当てるタスクを指します。コンピュータビジョンにおける重要な技術であり、自動運転、医療画像解析、地理情報システムなどの分野で広く使用されています。

従来の画像セグメンテーション タスクとは異なり、セマンティック セグメンテーションでは画像をいくつかの領域に分割する必要があるだけでなく、各領域にセマンティック ラベルを割り当てる必要もあります。たとえば、自動運転では、セマンティック セグメンテーションにより、道路、車両、歩行者などのエリアをセグメント化し、対応するセマンティック ラベルを各エリアに割り当てることで、車両の自動運転を支援できます。医用画像分析では、セマンティック セグメンテーションにより、さまざまな組織構造 (臓器、筋肉、骨など) をセグメント化し、それらを分析および診断できます。

セマンティックセグメンテーションの実装方法は、主に領域ベースの方法とピクセルベースの方法に分けられます。領域ベースの方法では、画像を一連の領域に分割し、各領域を分類して、対応する意味ラベルを割り当てます。ピクセルベースの方法では、各ピクセルを直接分類し、対応する意味ラベルを割り当てます。現在、セマンティックセグメンテーションは深層学習に基づく手法が主流となっており、その中でも畳み込みニューラルネットワーク(CNN)に基づく手法が最も一般的です。一般的に使用されるセマンティック セグメンテーション モデルには、FCN、SegNet、U-Net、DeepLab などが含まれます。

コンピュータ ビジョンの分野では、セマンティック セグメンテーションに似た他の 2 つの重要な問題、つまりイメージ セグメンテーションとインスタンス セグメンテーションがあります。ここではそれらをセマンティック セグメンテーションと簡単に区別します。
画像セグメンテーションでは、画像をいくつかのコンポーネント領域に分割します。この種の問題の解決方法は通常、画像内のピクセル間の相関を利用します。トレーニング中に画像ピクセルに関するラベル情報は必要ありません。また、予測時にセグメント化された領域が必要なセマンティクスを持っていることも保証できません。
インスタンスのセグメンテーションは、同時検出とセグメンテーションとも呼ばれ、画像内の各ターゲット インスタンスのピクセル レベルの領域を識別する方法を研究します。セマンティック セグメンテーションとは異なり、インスタンス セグメンテーションでは、セマンティックだけでなく、さまざまなターゲット インスタンスも区別する必要があります。たとえば、画像内に 2 匹の犬がいる場合、インスタンス セグメンテーションでは、ピクセルが 2 匹の犬のどちらに属しているかを区別する必要があります。



データセット

セマンティック セグメンテーションにとって最も重要なデータセットの 1 つは Pascal VOC2012 です。

データセットをダウンロードする

%matplotlib inline
import os
import torch
import torchvision
from d2l import torch as d2l
#@save
d2l.DATA_HUB['voc2012'] = (d2l.DATA_URL + 'VOCtrainval_11-May-2012.tar',
                           '4e443f8a2eca6b1dac8a6c57641b67dd40621a49')

voc_dir = d2l.download_extract('voc2012', 'VOCdevkit/VOC2012')

データセットの読み取り

#@save
def read_voc_images(voc_dir, is_train=True):
    """读取所有VOC图像并标注"""
    txt_fname = os.path.join(voc_dir, 'ImageSets', 'Segmentation',
                             'train.txt' if is_train else 'val.txt')
    mode = torchvision.io.image.ImageReadMode.RGB
    with open(txt_fname, 'r') as f:
        images = f.read().split()
    features, labels = [], []
    for i, fname in enumerate(images):
        features.append(torchvision.io.read_image(os.path.join(
            voc_dir, 'JPEGImages', f'{
      
      fname}.jpg')))
        labels.append(torchvision.io.read_image(os.path.join(
            voc_dir, 'SegmentationClass' ,f'{
      
      fname}.png'), mode))
    return features, labels

train_features, train_labels = read_voc_images(voc_dir, True)

以下に、最初の 5 つの入力イメージとそのラベルをプロットします。ラベル イメージでは、白と黒はそれぞれ境界線と背景を表し、他の色は異なるカテゴリに対応します。

n = 5
imgs = train_features[0:n] + train_labels[0:n]
imgs = [img.permute(1,2,0) for img in imgs]
d2l.show_images(imgs, 2, n);

ここに画像の説明を挿入
次に、RGB カラー値とクラス名を列挙します。

VOC_COLORMAP = [[0, 0, 0], [128, 0, 0], [0, 128, 0], [128, 128, 0],
                [0, 0, 128], [128, 0, 128], [0, 128, 128], [128, 128, 128],
                [64, 0, 0], [192, 0, 0], [64, 128, 0], [192, 128, 0],
                [64, 0, 128], [192, 0, 128], [64, 128, 128], [192, 128, 128],
                [0, 64, 0], [128, 64, 0], [0, 192, 0], [128, 192, 0],
                [0, 64, 128]]

#@save
VOC_CLASSES = ['background', 'aeroplane', 'bicycle', 'bird', 'boat',
               'bottle', 'bus', 'car', 'cat', 'chair', 'cow',
               'diningtable', 'dog', 'horse', 'motorbike', 'person',
               'potted plant', 'sheep', 'sofa', 'train', 'tv/monitor']

上記で定義した 2 つの定数を使用すると、ラベル内の各ピクセルのクラス インデックスを簡単に検索できます。上記の RGB カラー値からカテゴリ インデックスへのマッピングを構築する voc_colormap2label 関数と、Pascal VOC2012 データセット内のカテゴリ インデックスに RGB 値をマッピングする voc_label_indices 関数を定義します。

#@save
def voc_colormap2label():
    """构建从RGB到VOC类别索引的映射"""
    colormap2label = torch.zeros(256 ** 3, dtype=torch.long)
    for i, colormap in enumerate(VOC_COLORMAP):
        colormap2label[
            (colormap[0] * 256 + colormap[1]) * 256 + colormap[2]] = i
    return colormap2label

具体的には、コードは RGB カラー値を一意の整数に変換する方法を使用します。つまり、R、G、B の 3 つのチャネルの値を 25 6 2 256^ 2 で乗算します25 6225 6 1 256^125 6125 6 0 256^025 60 を入力し、それらを加算します。これは、RGB カラー値の 3 つの値を一意の整数に変換します。

#@save
def voc_label_indices(colormap, colormap2label):
    """将VOC标签中的RGB值映射到它们的类别索引"""
    colormap = colormap.permute(1, 2, 0).numpy().astype('int32')
    idx = ((colormap[:, :, 0] * 256 + colormap[:, :, 1]) * 256
           + colormap[:, :, 2])
    return colormap2label[idx]

具体的には、関数はまず「カラーマップ」テンソルの RGB 値をサイズ (H, W) の整数テンソル「idx」に変換します。これは、3 つのチャネル R、G、B の値を25 6 2 256^2で乗算したものです。25 6225 6 1 256^125 6125 6 0 256^025 60 を入力し、それらを加算します。この整数は、「colormap2label」テンソルから対応するクラス インデックスを抽出するためのインデックスとして使用されます。これらのカテゴリ インデックスは、「カラーマップ」テンソルと同じ形状の新しいテンソル「idx」を形成します。各要素は「カラーマップ」テンソルのピクセルに対応します。

ここに画像の説明を挿入

データの前処理

セマンティック セグメンテーションでは、これを行うには、予測されたピクセル クラスを入力イメージの元のサイズに再マッピングする必要があります。このようなマッピングは、特に意味的にセグメント化された異なる領域では十分に正確ではない可能性があります。この問題を回避するには、画像を再スケーリングするのではなく、固定サイズにトリミングします。具体的には、入力画像の同じ領域をトリミングし、画像拡張でランダムなトリミングを使用してラベルを付けます。

#@save
def voc_rand_crop(feature, label, height, width):
    """随机裁剪特征和标签图像"""
    rect = torchvision.transforms.RandomCrop.get_params(
        feature, (height, width))
    feature = torchvision.transforms.functional.crop(feature, *rect)
    label = torchvision.transforms.functional.crop(label, *rect)
    return feature, label

imgs = []
for _ in range(n):
    imgs += voc_rand_crop(train_features[0], train_labels[0], 200, 300)

imgs = [img.permute(1, 2, 0) for img in imgs]
d2l.show_images(imgs[::2] + imgs[1::2], 2, n);

この関数は、torchvision.transforms.RandomCrop.get_params() を使用してランダムなトリミング四角形を取得し、次に torchvision.transforms.function.crop() 関数を使用して入力フィーチャ イメージとラベル イメージをトリミングします。最後に、この関数は切り取られた特徴画像とラベル画像を返します。具体的には、この関数は次の手順でランダムなトリミングを実現します。

  1. torchvision.transforms.RandomCrop.get_params() を使用して、高さと幅をそれぞれ持つランダムなトリミング四角形を取得します。
  2. torchvision.transforms.function.crop() を使用して入力フィーチャ イメージとラベル イメージをトリミングします。トリミングされた四角形は、前の手順で取得したランダムな四角形です。
  3. トリミングされたフィーチャ画像とラベル画像を返します。

カスタム セマンティック セグメンテーション データセット クラス

#@save
class VOCSegDataset(torch.utils.data.Dataset):
    """一个用于加载VOC数据集的自定义数据集"""

    def __init__(self, is_train, crop_size, voc_dir):
        self.transform = torchvision.transforms.Normalize(
            mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])
        self.crop_size = crop_size
        features, labels = read_voc_images(voc_dir, is_train=is_train)
        self.features = [self.normalize_image(feature)
                         for feature in self.filter(features)]
        self.labels = self.filter(labels)
        self.colormap2label = voc_colormap2label()
        print('read ' + str(len(self.features)) + ' examples')

    def normalize_image(self, img):
        return self.transform(img.float() / 255)

    def filter(self, imgs):
        return [img for img in imgs if (
            img.shape[1] >= self.crop_size[0] and
            img.shape[2] >= self.crop_size[1])]

    def __getitem__(self, idx):
        feature, label = voc_rand_crop(self.features[idx], self.labels[idx],
                                       *self.crop_size)
        return (feature, voc_label_indices(label, self.colormap2label))

    def __len__(self):
        return len(self.features)

これは、Pascal VOC データセットをロードするためのカスタム データセット クラス VOCSegDataset です。データセット クラスは次のメソッドを提供します。

init (self、is_train、crop_size、voc_dir): データセットの初期化に使用されるコンストラクター。この関数は 3 つのパラメータを受け取ります。

  1. is_train: このデータセットがトレーニング用かテスト用かを示すブール値。
  2. Crop_size: ランダムにトリミングされた画像のサイズを示すタプル。
  3. voc_dir: データセットのストレージパス。

Normalize_image(self, img): 画像を正規化するために使用されます。

filter(self, imgs): サイズが Crop_size より小さい画像を除外するために使用されます。

getitem (self, idx): データセット内の項目を取得するために使用されます。この関数はインデックス パラメーター idx を受け取り、タプル (特徴、ラベル) を返します。ここで、特徴は特徴イメージを表し、ラベルは対応するラベル イメージを表します。

len (self): データセット内のサンプル数を取得するために使用されます。

コンストラクターでは、データセット クラスはまず read_voc_images() 関数を使用してデータセット内の画像とラベルを読み取り、filter() メソッドを使用してサイズが Crop_size より小さい画像をフィルターで除外します。次に、データセット クラスは、normalize_image() メソッドを使用して各フィーチャ イメージを正規化し、voc_rand_crop() メソッドと voc_label_indices() メソッドを使用して、フィーチャ イメージとラベル イメージのランダムなトリミングとラベル マッピングを実現します。最後に、データセット クラスは、getitem () メソッドでトリミングされたフィーチャとラベルのイメージを返します。

このデータセット クラスの主な機能は、Pascal VOC データセットを PyTorch のデータセット タイプに変換することです。これにより、PyTorch が提供する DataLoader クラスを使用して、モデルのトレーニング時にデータをバッチ処理できるようになります。

crop_size = (320, 480)
voc_train = VOCSegDataset(True, crop_size, voc_dir)
voc_test = VOCSegDataset(False, crop_size, voc_dir)
batch_size = 64
train_iter = torch.utils.data.DataLoader(voc_train, batch_size, shuffle=True,
                                    drop_last=True,
                                    num_workers=d2l.get_dataloader_workers())
for X, Y in train_iter:
    print(X.shape)
    print(Y.shape)
    break

すべてのコンポーネントを統合する

#@save
def load_data_voc(batch_size, crop_size):
    """加载VOC语义分割数据集"""
    voc_dir = d2l.download_extract('voc2012', os.path.join(
        'VOCdevkit', 'VOC2012'))
    num_workers = d2l.get_dataloader_workers()
    train_iter = torch.utils.data.DataLoader(
        VOCSegDataset(True, crop_size, voc_dir), batch_size,
        shuffle=True, drop_last=True, num_workers=num_workers)
    test_iter = torch.utils.data.DataLoader(
        VOCSegDataset(False, crop_size, voc_dir), batch_size,
        drop_last=True, num_workers=num_workers)
    return train_iter, test_iter

完全畳み込みネットワーク

完全畳み込みモデルは、入力画像をピクセル レベルで分類またはセグメント化し、入力画像と同じサイズのセグメント化結果を出力できる特別な畳み込みニューラル ネットワークです。完全畳み込みモデルは通常、畳み込み層、デコンボリューション層、およびプーリング層で構成されます。畳み込み層は画像の特徴を抽出するために使用され、デコンボリューション層は特徴画像を元の画像サイズに復元するために使用され、プーリングはレイヤーは、フィーチャ画像のサイズを縮小するために使用されます。

完全畳み込みモデルは、セマンティック セグメンテーション、インスタンス セグメンテーション、エッジ検出などの画像セグメンテーション タスクに最も一般的に使用されます。このうち、セマンティック セグメンテーション タスクは、画像内の各ピクセルを対応するカテゴリに割り当てることであり、インスタンス セグメンテーション タスクは、画像内の各オブジェクト インスタンスを異なるカテゴリに割り当てることです。

完全畳み込みモデルの古典的な構造は U-Net で、エンコーダーとデコーダーの 2 つの部分で構成されます。エンコーダーは通常、畳み込み層とプーリング層を組み合わせて使用​​して、画像の特徴を抽出し、特徴画像のサイズを削減します。デコーダは、デコンボリューション レイヤーとスキップ接続を使用して、特徴画像を元の画像サイズに復元し、エンコーダの特徴画像をデコーダの特徴画像と融合します。

完全畳み込みモデルをトレーニングする場合、通常、クロスエントロピー損失関数を使用してモデル出力と真のラベルの差を測定し、バックプロパゲーション アルゴリズムを使用してモデル パラメーターを更新します。完全畳み込みモデルの出力はセグメント化された画像であるため、通常、損失関数を計算するときにベクトルに平坦化する必要があります。

建設モデル

完全畳み込みネットワーク モデルの最も基本的な設計を見てみましょう。以下の図に示すように、完全な畳み込みネットワークは、最初に畳み込みニューラル ネットワークを使用して画像の特徴を抽出し、次に1 × 1 1\times 1を渡します。1×1畳み込み層はチャネルの数をカテゴリの数に変換し、最後に転置された畳み込み層を通じて特徴マップの高さと幅を入力画像のサイズに変換します。したがって、モデル出力は入力イメージと同じ高さと幅を持ち、最終出力チャネルにはその空間位置のピクセルのクラス予測が含まれます。
ここに画像の説明を挿入
以下では、ImageNet データセットで事前トレーニングされた ResNet-18 モデルを使用して画像特徴を抽出し、このネットワークを pretrained_net と表します。ResNet-18 モデルの最後の数層には、グローバル平均プーリング層と完全接続層が含まれていますが、これらは完全な畳み込みネットワークでは必要ありません。
ここに画像の説明を挿入
次に、完全な畳み込みネットワーク ネットを作成します。これは、最後のグローバル平均プーリング層と出力に最も近い完全接続層を除く、ResNet-18 のほとんどの事前トレーニング層を複製します。

net = nn.Sequential(*list(pretrained_net.children())[:-2])

高さ 320、幅 480 の入力が与えられた場合、ネットの前方伝播により、入力の高さと幅が元の1 32 \frac{1}{32}に縮小されます。321,即10和15。

X = torch.rand(size=(1, 3, 320, 480))
net(X).shape

ここに画像の説明を挿入
次に1 × 1 1\times 1を使用します1×1 つの畳み込み層は、出力チャネルの数を Pascal VOC2012 データセットのクラス数 (21 クラス) に変換します。最後に、特徴マップの高さと幅を 32 倍にして、入力画像の高さと幅に戻す必要があります。( 320 − 64 + 16 ∗ 2 + 32 ) / 32 = 10 (320-64+16*2+32)/32=10より( 32064+162+32 ) /32=10および( 480 − 64 + 16 ∗ 2 + 32 ) / 32 = 15 (480-64+16*2+32)/32=15( 48064+162+32 ) /32=15 の場合、ストライドは32 3232畳み込み層を転置し、畳み込みカーネルの高さと幅を64 6464 、 16 16で満たされています16ストライドがssの場合にそれがわかります。ss / 2 s/2s /2 (sssは整数)、畳み込みカーネルの高さと幅は2 s 2s2 s、転置コンボリューション カーネルは入力の高さと幅をsss回。

転置された畳み込み層を初期化する

画像処理では、画像を拡大する、つまりアップサンプリングが必要な場合があります。共一次補間は、一般的に使用されるアップサンプリング手法の 1 つであり、転置畳み込み層の初期化にもよく使用されます。

アップサンプリングは、低解像度の画像または信号を高解像度に上げる方法です。共一次補間は、一般的に使用されるアップサンプリング手法の 1 つで、既存の低解像度画像を補間することで高解像度の画像を生成できます。

双一次補間では、低解像度画像が 2 回サンプリングされること、つまり各ピクセルが2 × 2 2 \times 2になることを前提としています。2×2ピクセル ブロック。新しい各ピクセル ブロック内のピクセルについて、周囲の 4 つの既知のピクセル値から双一次補間が計算されます。具体的には、ターゲット画像(x, y) 内の新しいピクセル (x, y( x ,y )のグレー値は、次の手順で計算できます。

ターゲット ピクセル( x , y ) (x, y)を検索します。( x ,y ) は、元の低解像度画像の( x 1 , y 1 ) (x_1, y_1)( ×1y1)( x 2 , y 1 ) (x_2, y_1)( ×2y1)( x 1 , y 2 ) (x_1, y_2)( ×1y2)( x 2 , y 2 ) (x_2, y_2)( ×2y2),其中 ( x 1 , y 1 ) (x_1, y_1) ( ×1y1)( x 2 , y 2 ) (x_2, y_2)( ×2y2)は( x , y ) (x, y)に最も近いです( x ,y ) ,( x 1 , y 2 ) (x_1, y_2)( ×1y2)( x 2 , y 1 ) (x_2, y_1)( ×2y1)は他の 2 つのピクセルです。

対象ピクセル( x , y ) (x, y)を計算します( x ,y )と 4 つの既知のピクセル間の距離、つまりd 1 = ( x − x 1 ) 2 + ( y − y 1 ) 2 d_{1} = \sqrt{(x-x_1)^2 + (y -y_1) ^2}d1=( ×バツ12+( yy12 d 2 = ( x − x 2 ) 2 + ( y − y 1 ) 2 d_{2} = \sqrt{(x-x_2)^2 + (y-y_1)^2}d2=( ×バツ22+( yy12 d 3 = ( x − x 1 ) 2 + ( y − y 2 ) 2 d_{3} = \sqrt{(x-x_1)^2 + (y-y_2)^2}d3=( ×バツ12+( yy22 d 4 = ( x − x 2 ) 2 + ( y − y 2 ) 2 d_{4} = \sqrt{(x-x_2)^2 + (y-y_2)^2}d4=( ×バツ22+( yy22

対象ピクセル( x , y ) (x, y)を計算します( x ,y )では、周囲の 4 つのピクセルのグレー値が加重平均に使用され、重みはターゲット ピクセルと 4 つの既知のピクセルの間の距離に反比例します。たった今:

f ( x , y ) = 1 d 1 d 3 f ( x 1 , y 1 ) + 1 d 2 d 3 f ( x 2 , y 1 ) + 1 d 1 d 4 f ( x 1 , y 2 ) + 1 d 2 d 4 f ( x 2 , y 2 ) f(x, y) = \frac{1}{d_{1}d_{3}}f(x_1, y_1) + \frac{1}{d_{2 }d_{3}}f(x_2, y_1) + \frac{1}{d_{1}d_{4}}f(x_1, y_2) + \frac{1}{d_{2}d_{4}} f(x_2, y_2)f ( x ,y )=d1d31f ( x1y1+d2d31f ( x2y1+d1d41f ( x1y2+d2d41f ( x2y2

其中 f ( x 1 , y 1 ) f(x_1, y_1) f ( x1y1)f ( x 2 , y 1 ) f(x_2, y_1)f ( x2y1)f ( x 1 , y 2 ) f(x_1, y_2)f ( x1y2)f ( x 2 , y 2 ) f(x_2, y_2)f ( x2y2)は、それぞれ 4 つの既知のピクセルのグレー値を表します。

双一次補間を使用すると、低解像度の画像を高解像度にアップサンプリングして、より鮮明な画像を得ることができます。

def bilinear_kernel(in_channels, out_channels, kernel_size):
    factor = (kernel_size + 1) // 2
    if kernel_size % 2 == 1:
        center = factor - 1
    else:
        center = factor - 0.5
    og = (torch.arange(kernel_size).reshape(-1, 1),
          torch.arange(kernel_size).reshape(1, -1))
    filt = (1 - torch.abs(og[0] - center) / factor) * \
           (1 - torch.abs(og[1] - center) / factor)
    weight = torch.zeros((in_channels, out_channels,
                          kernel_size, kernel_size))
    weight[range(in_channels), range(out_channels), :, :] = filt
    return weight

バイリニア補間コンボリューションカーネルを生成する関数です。その入力には、入力チャネルの数、出力チャネルの数、およびコンボリューション カーネルのサイズが含まれ、その出力は、この関数によって生成された双線形補間コンボリューション カーネルを表す形状のテンソル (in_channels、out_channels、kernel_size、kernel_size) です。 。

具体的には、関数は最初にコンボリューション カーネルの中心の位置を計算し、次に形状 (kernel_size, kernel_size) のテンソル フィルトを生成します。ここで、 filt の各要素は、双線形補間コンボリューション カーネル内の対応する位置の重みを表します。最後に、関数は入力チャネルと出力チャネルの数に従って形状のテンソル重み (in_channels、out_channels、kernel_size、kernel_size) を生成します。ここで、重みの各要素は、双線形補間コンボリューション カーネル内の対応する位置の重みを表します。具体的には、weight[i, j, :, :] は、i 番目の入力チャネルから j 番目の出力チャネルまでの双線形補間カーネルを表します。

この関数は、畳み込みニューラル ネットワークで双線形補間畳み込み層を定義するために使用でき、入力テンソルをより高い解像度にアップサンプリングします。

完全畳み込みネットワークは、双線形補間によるアップサンプリングで転置畳み込み層を初期化します。1 × 1の場合、1\times 11×1畳み込み層では、Xavier 初期化パラメータを使用します。

W = bilinear_kernel(num_classes, num_classes, 64)
net.transpose_conv.weight.data.copy_(W);

訓練

def loss(inputs, targets):
    return F.cross_entropy(inputs, targets, reduction='none').mean(1).mean(1)

num_epochs, lr, wd, devices = 5, 0.001, 1e-3, d2l.try_all_gpus()
trainer = torch.optim.SGD(net.parameters(), lr=lr, weight_decay=wd)
d2l.train_ch13(net, train_iter, test_iter, loss, trainer, num_epochs, devices)

予測する

予測するときは、各チ​​ャネルの入力画像を正規化し、畳み込みニューラル ネットワークで必要な 4 次元の入力形式に変換する必要があります。

def predict(img):
    X = test_iter.dataset.normalize_image(img).unsqueeze(0)
    pred = net(X.to(devices[0])).argmax(dim=1)
    return pred.reshape(pred.shape[1], pred.shape[2])

各ピクセルの予測クラスを視覚化するために、予測クラスをデータセット内のラベル付けされた色にマッピングし直します。

def label2image(pred):
    colormap = torch.tensor(VOC_COLORMAP, device=devices[0])
    X = pred.long()
    return colormap[X, :]

テスト データセット内の画像のサイズと形状は異なります。このモデルではストライド 32 の転置畳み込み層を使用しているため、入力画像の高さまたは幅が 32 で割り切れない場合、転置畳み込み層の出力の高さまたは幅は入力画像のサイズから外れます。 。この問題を解決するには、画像内の高さと幅が 32 の整数倍である複数の長方形領域をインターセプトし、これらの領域内のピクセルに対して順方向伝播をそれぞれ実行します。これらの領域の結合は入力イメージを完全にカバーする必要があることに注意してください。ピクセルが複数の領域でカバーされている場合、異なる領域の順伝播における転置畳み込み層の出力の平均値を、カテゴリを予測するためのソフトマックス演算の入力として使用できます。

簡単にするために、いくつかの大きなテスト画像のみを読み取り、画像の左上隅から開始して320 × 480 320\times 480の形状を切り取ります。320×予測には480のエリアが使用されます。これらのテスト画像については、インターセプトされた領域を 1 つずつ出力し、次に予測結果を出力し、最後にラベル付けされたカテゴリを出力します。

ここに画像の説明を挿入

おすすめ

転載: blog.csdn.net/qq_51957239/article/details/131058002