Main Content

オブジェクト検出のための境界ボックスの拡張

この例では、オブジェクト検出ワークフローの一環としてイメージと境界ボックスの一般的な拡張を行う方法について説明します。

オブジェクト検出器の学習データは、イメージおよび関連付けられた境界ボックス ラベルで構成されます。学習データを拡張する場合、イメージおよび関連付けられた境界ボックスに対して同一の変換を適用しなければなりません。この例では、3 種類の一般的な変換を示します。

この例では、複数の種類の変換を組み合わせて、データストア内の学習データに対する拡張の適用を実行する方法を説明します。

拡張した学習データを使用してネットワークに学習させることができます。オブジェクト検出ネットワークの学習方法の例については、Faster R-CNN 深層学習を使用したオブジェクトの検出 (Computer Vision Toolbox)を参照してください。

サンプル イメージと境界ボックスを読み取って表示します。この例では、さまざまな種類の拡張の効果を比較するため、各変換において同じ入力イメージと境界ボックスを使用します。

filenameImage = 'kobi.png';
I = imread(filenameImage);
bbox = [4 156 1212 830];
label = "dog";

イメージと境界ボックスを表示します。

annotatedImage = insertShape(I,"rectangle",bbox,"LineWidth",8);
imshow(annotatedImage)
title('Original Image and Bounding Box')

イメージと境界ボックスのサイズ変更

imresize を使用して、イメージを 2 分の 1 にスケール ダウンします。

scale = 1/2;
J = imresize(I,scale);

bboxresize を使用して、関連付けられた境界ボックスに同じスケーリングを適用します。

bboxResized = bboxresize(bbox,scale);

サイズ変更したイメージと境界ボックスを表示します。

annotatedImage = insertShape(J,"rectangle",bboxResized,"LineWidth",8);
imshow(annotatedImage)
title('Resized Image and Bounding Box')

イメージと境界ボックスのトリミング

トリミングは、データのサイズをネットワークの入力サイズに一致させるための一般的な前処理手順です。目的のサイズの出力イメージを作成するには、まず、関数randomWindow2d (Image Processing Toolbox)またはcenterCropWindow2d (Image Processing Toolbox)を使用して、トリミング ウィンドウのサイズと位置を指定します。イメージ内の目的の内容が確実に含まれるようにトリミング ウィンドウを選択してください。次に、imcrop を使用して、イメージとピクセル ラベル イメージを同じウィンドウにトリミングします。

トリミングした領域の目的のサイズを、[高さ, 幅] の形式の 2 要素のベクトルとして指定します。

targetSize = [1024 1024];

imcrop を使用して、イメージの中心からターゲット サイズでトリミングします。

win = centerCropWindow2d(size(I),targetSize);
J = imcrop(I,win);

bboxcrop を使用して、同じトリミング ウィンドウで境界ボックスをトリミングします。OverlapThreshold に 1 未満の値を指定して、トリミング ウィンドウが境界ボックスを完全に囲んでいない場合に、境界ボックスを破棄するのではなく、トリミング ウィンドウのサイズにトリミングされるようにします。オーバーラップのしきい値を使用すると、イメージ内のオブジェクトに対するトリミングの許容量を制御できます。たとえば、人物検出器の学習では、人物が半分以上トリミングされると役に立ちませんが、車は半分トリミングされても許容範囲内です。

[bboxCropped,valid] = bboxcrop(bbox,win,"OverlapThreshold",0.7);

トリミング ウィンドウ内側にあるラベルを保持します。

label = label(valid);

トリミングされたイメージと境界ボックスを表示します。

annotatedImage = insertShape(J,"rectangle",bboxCropped,"LineWidth",8);
imshow(annotatedImage)
title('Cropped Image and Bounding Box')

イメージと境界ボックスのトリミングとサイズ変更

多くの場合、トリミングとサイズ変更は一緒に行われます。bboxcropbboxresize を直列に使用すれば、よく使用される "トリミングとサイズ変更" の変換を実装できます。

イメージ内のランダムな位置からトリミング ウィンドウを作成します。イメージと境界ボックスを同じトリミング ウィンドウでトリミングします。

cropSize = [1024 1024];
win = randomWindow2d(size(I),cropSize);
J = imcrop(I,win);
croppedBox = bboxcrop(bbox,win,"OverlapThreshold",0.5);

イメージとボックスのサイズをターゲット サイズに変更します。

targetSize = [512 512];
J = imresize(J,targetSize);
croppedAndResizedBox = bboxresize(croppedBox,targetSize./cropSize);

トリミングしてサイズ変更されたイメージと境界ボックスを表示します。

annotatedImage = insertShape(J,"rectangle",croppedAndResizedBox,"LineWidth",8);
imshow(annotatedImage)
title('Crop and Resized Image and Bounding Box')

イメージと境界ボックスのワープ

関数 randomAffine2d (Image Processing Toolbox) は、回転、平行移動、スケーリング (サイズ変更)、反転、せん断の組み合わせからランダムな 2 次元アフィン変換を作成します。imwarp (Image Processing Toolbox) を使用して、イメージをワープさせます。bboxwarp を使用して、境界ボックスをワープさせます。関数 affineOutputView (Image Processing Toolbox) を使用して、ワープされた出力の空間の範囲と解像度を制御します。

この例では、スケーリングと回転の 2 つのランダムなアフィン変換について示します。

ランダムなスケーリング

[1.5,1.8] の範囲からランダムに選択したスケール係数を使用して入力イメージと境界ボックスのサイズを変更するスケール変換を作成します。この変換は、水平方向と垂直方向に同じスケール係数を適用します。

tform = randomAffine2d("Scale",[1.5 1.8]);

アフィン変換用の出力表示を作成します。

rout = affineOutputView(size(I),tform);

imwarp を使用してイメージを再スケーリングし、bboxwarp を使用して境界ボックスを再スケーリングします。OverlapThreshold の値に 0.5 を指定します。

J = imwarp(I,tform,"OutputView",rout);
bboxScaled = bboxwarp(bbox,tform,rout,"OverlapThreshold",0.5);

スケーリングされたイメージと境界ボックスを表示します。

annotatedImage = insertShape(J,"rectangle",bboxScaled,"LineWidth",8);
imshow(annotatedImage)
title('Scaled Image and Bounding Box')

ランダムな回転

[-15,15] 度の範囲からランダムに選択した角度を使用してイメージとボックス ラベルを回転させるランダムな回転変換を作成します。

tform = randomAffine2d("Rotation",[-15 15]);

imwarpbboxwarp 用の出力表示を作成します。

rout = affineOutputView(size(I),tform);

imwarp を使用してイメージを回転させ、bboxwarp を使用して境界ボックスを回転させます。OverlapThreshold の値に 0.5 を指定します。

J = imwarp(I,tform,"OutputView",rout);
bboxRotated = bboxwarp(bbox,tform,rout,"OverlapThreshold",0.5);

トリミングされたイメージと境界ボックスを表示します。bboxwarp によって返される境界ボックスは、常にイメージの座標軸に揃えられることに注意してください。境界ボックスのサイズと縦横比は、回転したオブジェクトに合わせて変更されます。

annotatedImage = insertShape(J,"rectangle",bboxRotated,"LineWidth",8);
imshow(annotatedImage)
title('Rotated Image and Bounding Box')

データストア内の学習データに対する拡張の適用

データストアは、データの集合の読み取りや拡張に便利です。イメージと境界ボックスのデータを格納するデータストアを作成し、一連の演算を使用してデータを拡張します。

イメージと境界ボックスのデータを格納するデータストアの作成

サンプル データストアのサイズを増やすために、イメージと境界ボックスとラベルのファイル名を複製します。

numObservations = 4;
images = repelem({filenameImage},numObservations,1);
bboxes = repelem({bbox},numObservations,1);
labels = repelem({label},numObservations,1);

学習用のイメージ ファイルから imageDatastore を作成します。境界ボックスとラベルのデータを table に結合し、この table から boxLabelDatastore を作成します。

imds = imageDatastore(images);

tbl = table(bboxes,labels);
blds = boxLabelDatastore(tbl);

イメージ データストアとボックス ラベル データストアを結合し、イメージとボックス ラベルのペアを関連付けます。

trainingData = combine(imds,blds);

結合したデータストアから、最初のイメージとそれに関連付けられたボックス ラベルを読み取ります。

data = read(trainingData);
I = data{1};
bboxes = data{2};
labels = data{3};

イメージとボックス ラベルのデータを表示します。

annotatedImage = insertObjectAnnotation(I,'rectangle',bbox,labels, ...
    'LineWidth',8,'FontSize',40);
imshow(annotatedImage)

データ拡張の適用

関数 transform を使用して、学習データにデータ拡張を適用します。この例では、2 つの別個の拡張を学習データに適用します。

最初の拡張では、イメージの色にジッターを付加した後、イメージとボックス ラベルのペアに対してランダムな水平反転と回転を適用します (両者に同一の変換を適用します)。これらの演算は、この例の終わりにある補助関数 jitterImageColorAndWarp で定義されます。

augmentedTrainingData = transform(trainingData,@jitterImageColorAndWarp);

拡張されたデータをすべて読み取ります。

data = readall(augmentedTrainingData);

拡張されたイメージとボックス ラベル データを表示します。

rgb = cell(numObservations,1);
for k = 1:numObservations
    I = data{k,1};
    bbox = data{k,2};
    labels = data{k,3};
    rgb{k} = insertObjectAnnotation(I,'rectangle',bbox,labels,'LineWidth',8,'FontSize',40);
end
montage(rgb)

2 番目の拡張では、イメージとボックス ラベルをターゲット サイズに再スケーリングします。これらの演算は、この例の終わりにある補助関数 resizeImageAndLabel で定義されます。

targetSize = [300 300];
preprocessedTrainingData = transform(augmentedTrainingData,...
    @(data)resizeImageAndLabel(data,targetSize));

前処理されたデータをすべて読み取ります。

data = readall(preprocessedTrainingData);

前処理されたイメージとボックス ラベル データを表示します。

rgb = cell(numObservations,1);
for k = 1:numObservations
    I = data{k,1};
    bbox = data{k,2};
    labels = data{k,3};
    rgb{k} = insertObjectAnnotation(I,'rectangle',bbox,labels, ...
        'LineWidth',8,'FontSize',15);
end
montage(rgb)

拡張の補助関数

補助関数 jitterImageColorAndWarp は、イメージ データにランダムなカラー ジッターを適用した後、イメージとボックス ラベル データに同一のアフィン変換を適用します。変換は、ランダムな水平方向の反転と回転で構成されます。入力 data と出力 out は 2 要素の cell 配列です。配列の最初の要素はイメージ データで、2 番目の要素はボックス ラベル データです。

function out = jitterImageColorAndWarp(data)
% Unpack original data.
I = data{1};
boxes = data{2};
labels = data{3};

% Apply random color jitter.
I = jitterColorHSV(I,"Brightness",0.3,"Contrast",0.4,"Saturation",0.2);

% Define random affine transform.
tform = randomAffine2d("XReflection",true,'Rotation',[-30 30]);
rout = affineOutputView(size(I),tform);

% Transform image and bounding box labels.
augmentedImage = imwarp(I,tform,"OutputView",rout);
[augmentedBoxes, valid] = bboxwarp(boxes,tform,rout,'OverlapThreshold',0.4);
augmentedLabels = labels(valid);

% Return augmented data.
out = {augmentedImage,augmentedBoxes,augmentedLabels};
end

補助関数 resizeImageAndLabel は、イメージがターゲット サイズに一致するようにスケール係数を計算した後、imresize を使用してイメージのサイズを変更し、bboxresize を使用してボックス ラベルのサイズを変更します。入力データと出力データは 2 要素の cell 配列です。配列の最初の要素はイメージ データで、2 番目の要素はボックス ラベル データです。

function data = resizeImageAndLabel(data,targetSize)
scale = targetSize./size(data{1},[1 2]);
data{1} = imresize(data{1},targetSize);
data{2} = bboxresize(data{2},scale);
end

参考

(Computer Vision Toolbox) | (Computer Vision Toolbox) | (Computer Vision Toolbox) | | (Image Processing Toolbox) | (Image Processing Toolbox) | (Image Processing Toolbox)

関連する例

詳細