Main Content

U-Net を使用する ARM Neon ターゲットでのセマンティック セグメンテーション アプリケーションのコード生成

この例では、深層学習を使用するイメージ セグメンテーション アプリケーションのコードを生成する方法を示します。codegen コマンドを使用して、U-Net の DAG ネットワーク オブジェクトに対して予測を実行するスタティック ライブラリを生成します。U-Net は、イメージ セグメンテーション用の深層学習ネットワークです。

U-Net を使用したイメージのセグメンテーションを codegen コマンドを使用せずに示す類似例については、深層学習を使用したマルチスペクトル イメージのセマンティック セグメンテーション (Image Processing Toolbox)を参照してください。

必要条件

  • NEON 拡張をサポートし、3 GB 以上の RAM を備えた ARM® プロセッサ

  • ARM Compute Library (ターゲット ARM ハードウェア上)

  • コンパイラおよびライブラリの環境変数

  • MATLAB® Coder™

  • MATLAB Coder Interface for Deep Learning サポート パッケージ

  • Deep Learning Toolbox™

この例で使用する ARM Compute Library のバージョンは、コード生成でサポートされている最新バージョンではない可能性があります。サポートされているライブラリのバージョンと環境変数の詳細については、MATLAB Coder を使用した深層学習の前提条件を参照してください。

この例は MATLAB Online ではサポートされていません。

U-Net の概要

U-Net [1] は畳み込みニューラル ネットワーク (CNN) の一種で、セマンティック イメージ セグメンテーション用に設計されています。U-Net では、最初の一連の畳み込み層に最大プーリング層が点在し、入力イメージの解像度を逐次下げていきます。これらの層に、一連の畳み込み層が続き、その中にアップサンプリング演算子が点在し、入力イメージの解像度を逐次上げていきます。これらの 2 つの一連の経路を組み合わせると U 字型の図が形成されます。U-Net ネットワークは、元は生物医学のイメージ セグメンテーション アプリケーションで予測を実行するために学習が行われていました。この例は、このネットワークで時間の経過に伴う森林被覆の変化を追跡できることを示しています。環境保護機関は、森林伐採を追跡し、地域の環境的生態学的健全性を評価し特定します。

深層学習ベースのセマンティック セグメンテーションにより、高解像度の航空写真から植被を正確に測定できます。このような計算の課題の 1 つに、視覚的に類似した特性のクラスを切り分けること、たとえば緑のピクセルを草、低木または樹木として区別することがあります。分類の精度を高めるために、一部のデータセットには各ピクセルに関する追加情報を提供するマルチスペクトル イメージが含まれています。たとえば、ハームリン ビーチ州立公園のデータセットでは、クラスをより明確に分離する近赤外チャネルでカラー イメージが補完されています。

この例では、各ピクセルを正しく分類するために、事前学習済みの U-Net ネットワークと共にハームリン ビーチ州立公園のデータ [2] を使用します。

この例で使用する U-Net は、次の一連の 18 クラスに属するピクセルをセグメント化するように学習されています。

0. Other Class/Image Border      7. Picnic Table         14. Grass
1. Road Markings                 8. Black Wood Panel     15. Sand
2. Tree                          9. White Wood Panel     16. Water (Lake)
3. Building                     10. Orange Landing Pad   17. Water (Pond)
4. Vehicle (Car, Truck, or Bus) 11. Water Buoy           18. Asphalt (Parking Lot/Walkway)
5. Person                       12. Rocks
6. Lifeguard Chair              13. Other Vegetation

エントリポイント関数 segmentationUnetARM

エントリポイント関数 segmentationUnetARM は、multispectralUnet.mat ファイルに含まれている multispectralUnet ネットワークを使用して、入力イメージに対してパッチ単位のセマンティック セグメンテーションを実行します。この関数は、ネットワーク オブジェクトを multispectralUnet.mat ファイルから永続変数 mynet に読み込み、以降の予測呼び出しではその永続変数を再利用します。

type('segmentationUnetARM.m')
%  OUT = segmentationUnetARM(IM) returns a semantically segmented
%  image, which is segmented using the network multispectralUnet. This segmentation
%  is performed on the input image patchwise on patches of size 256,256.
%
% Copyright 2019-2020 The MathWorks, Inc.
function out = segmentationUnetARM(im)

%#codegen

persistent mynet;

if isempty(mynet)
    mynet = coder.loadDeepLearningNetwork('trainedUnet/multispectralUnet.mat');
end

% The input data has to be padded to the size compatible
% with the network Input Size. This input_data is padded inorder to
% perform semantic segmentation on each patch of size (Network Input Size)
[height, width, nChannel] = size(im);
patch = coder.nullcopy(zeros([256, 256, nChannel-1]));
% 
padSize = zeros(1,2);
padSize(1) = 256 - mod(height, 256);
padSize(2) = 256 - mod(width, 256);
% 
% Pad image must have have dimensions as multiples of network input dimensions
im_pad = padarray (im, padSize, 0, 'post');
[height_pad, width_pad, ~] = size(im_pad);
% 
out = zeros([size(im_pad,1), size(im_pad,2)], 'uint8');

for i = 1:256:height_pad    
    for j =1:256:width_pad        
        for p = 1:nChannel -1             
            patch(:,:,p) = squeeze( im( i:i+255,...
                                            j:j+255,...
                                            p));            
        end
         
        % pass in input
        segmentedLabels = activations(mynet, patch, 'Segmentation-Layer');
        
        % Takes the max of each channel (6 total at this point)
        [~,L] = max(segmentedLabels,[],3);
        patch_seg = uint8(L);
        
        % populate section of output
        out(i:i+255, j:j+255) = patch_seg;
       
    end
end

% Remove the padding
out = out(1:height, 1:width);

事前学習済みの U-Net DAG ネットワーク オブジェクトの取得

multispectralUnet.mat ファイルをダウンロードして U-Net DAG ネットワーク オブジェクトを読み込みます。

if ~exist('trainedUnet/multispectralUnet.mat','file')
    trainedUnet_url = 'https://www.mathworks.com/supportfiles/vision/data/multispectralUnet.mat';
    downloadUNet(trainedUnet_url,pwd);
end
ld = load("trainedUnet/multispectralUnet.mat");
net = ld.net;

この DAG ネットワークには、畳み込み層、最大プーリング層、深さ連結層、ピクセル分類出力層など、58 個の層が含まれています。深層学習ネットワーク アーキテクチャを対話的に可視化して表示するには、関数analyzeNetwork (Deep Learning Toolbox)を使用します。

analyzeNetwork(net);

入力データの準備

ハームリン ビーチ州立公園のデータをダウンロードします。

if ~exist(fullfile(pwd,'data'),'dir')
    url = 'http://home.cis.rit.edu/~cnspci/other/data/rit18_data.mat';
    downloadHamlinBeachMSIData(url,pwd+"/data/");
end

データを MATLAB に読み込んで確認します。

load(fullfile(pwd,'data','rit18_data','rit18_data.mat'));

データを確認します。

whos test_data

イメージには 7 個のチャネルがあります。RGB カラー チャネルは 4 番目、5 番目および 6 番目のイメージ チャネルです。最初の 3 つのチャネルは近赤外域に対応し、熱の痕跡に基づいて、イメージの異なる成分を強調表示します。チャネル 7 は有効なセグメンテーション領域を示すマスクです。

マルチスペクトル イメージ データは numChannels x width x height 配列に配置されます。MATLAB では、マルチチャネル イメージは width x height x numChannels 配列に配置されます。チャネルが 3 番目の次元になるようにデータを形状変更するには、補助関数 switchChannelsToThirdPlane を使用します。

test_data = switchChannelsToThirdPlane(test_data);

データの構造が正しいことを確認します (チャネルが最後に来る)。

whos test_data

この例では、変数 test_data に含まれているハームリン ビーチ州立公園の完全なデータセットのトリミング バージョンを使用します。test_data の高さと幅をトリミングして、この例で使用する変数 input_data を作成します。

test_datacropRGB = imcrop(test_data(:,:,1:3),[2600, 3000, 2000, 2000]);
test_datacropInfrared = imcrop(test_data(:,:,4:6),[2600, 3000, 2000, 2000]);
test_datacropMask = imcrop(test_data(:,:,7),[2600, 3000, 2000, 2000]);
input_data(:,:,1:3) = test_datacropRGB;
input_data(:,:,4:6) = test_datacropInfrared;
input_data(:,:,7) = test_datacropMask;

変数 input_data を確認します。

whos('input_data');

生成される実行可能ファイルに入力として渡されるテキスト ファイルに入力データを書き込みます。

WriteInputDatatoTxt(input_data);
[height, width, channels] = size(input_data);

スタティック ライブラリ用のコード生成構成オブジェクトの設定

ARM ベースのデバイスをターゲットにしたコードを生成するには、ライブラリ用の構成オブジェクトを作成します。実行可能プログラム用の構成オブジェクトは作成しないでください。C++ ソース コードの生成用の構成オブジェクトのみを設定します。

cfg = coder.config('lib');
cfg.TargetLang = 'C++';
cfg.GenCodeOnly = true;

深層学習コード生成用の構成オブジェクトの設定

coder.ARMNEONConfig オブジェクトを作成します。ターゲット ARM プロセッサのライブラリ バージョンとアーキテクチャを指定します。たとえば、ターゲット ボードが ARMv8 アーキテクチャと ARM Compute Library Version 20.02.1 を備えた HiKey/Rock960 ボードであるとします。

dlcfg = coder.DeepLearningConfig('arm-compute');
dlcfg.ArmComputeVersion = '20.02.1';
dlcfg.ArmArchitecture = 'armv8';

コード生成構成オブジェクト cfgDeepLearningConfig プロパティを深層学習構成オブジェクト dlcfg に割り当てます。

cfg.DeepLearningConfig = dlcfg;

codegen を使用した C++ ソース コードの生成

codegen -config cfg segmentationUnetARM -args {ones(size(input_data),'uint16')} -d unet_predict -report

コードは、ホスト コンピューター上の現在の作業ディレクトリにある unet_predict フォルダーに生成されます。

packNGo を使用した zip ファイルの生成

関数 packNGo は、すべての関連ファイルを zip 圧縮ファイルにパッケージ化します。

zipFileName = 'unet_predict.zip'; 
bInfo = load(fullfile('unet_predict','buildInfo.mat'));
packNGo(bInfo.buildInfo, {'fileName', zipFileName,'minimalHeaders', false, 'ignoreFileMissing',true});

生成された zip ファイルの名前は unet_predict.zip です。

ターゲット ハードウェアへの生成された zip ファイルのコピー

zip ファイルをターゲット ハードウェア ボードにコピーします。zip ファイルの内容をフォルダーに解凍し、zip ファイルをハードウェアから削除します。

次のコマンドで、置換を行います。

  • password をパスワードで置き換える

  • username をユーザー名で置き換える

  • targetname をデバイスの名前で置き換える

  • targetDir をファイルのコピー先フォルダーで置き換える

Linux® プラットフォームで、zip ファイルをターゲット ハードウェアに転送して解凍するには、次のコマンドを実行します。

if isunix, system(['sshpass -p password scp -r '  fullfile(pwd,zipFileName) ' username@targetname:targetDir/']), end
if isunix, system('sshpass -p password ssh username@targetname "if [ -d targetDir/unet_predict ]; then rm -rf targetDir/unet_predict; fi"'), end
if isunix, system(['sshpass -p password ssh username@targetname "unzip targetDir/' zipFileName ' -d targetDir/unet_predict"']), end
if isunix, system(['sshpass -p password ssh username@targetname "rm -rf  targetDir/' zipFileName '"']), end

Windows® プラットフォームで、zip ファイルをターゲット ハードウェアに転送して解凍するには、次のコマンドを実行します。

if ispc, system(['pscp.exe -pw password -r '  fullfile(pwd,zipFileName) ' username@targetname:targetDir/']), end
if ispc, system('plink.exe -l username -pw password targetname "if [ -d targetDir/unet_predict ]; then rm -rf targetDir/unet_predict; fi"'), end
if ispc, system(['plink.exe -l username -pw password targetname "unzip targetDir/' zipFileName ' -d targetDir/unet_predict"']), end
if ispc, system(['plink.exe -l username -pw password targetname "rm -rf  targetDir/' zipFileName '"']), end

ターゲット ハードウェアへのサポート ファイルのコピー

次のファイルをホスト コンピューターからターゲット ハードウェアにコピーします。

  • 入力データ input_data.txt

  • ライブラリを作成するための makefile unet_predict_rtw.mk

  • 実行可能プログラムをビルドするための makefile makefile_unet_arm_generic.mk

次のコマンドで、置換を行います。

  • password をパスワードで置き換える

  • username をユーザー名で置き換える

  • targetname をデバイスの名前で置き換える

  • targetDir をファイルのコピー先フォルダーで置き換える

Linux® プラットフォームで、サポート ファイルをターゲット ハードウェアに転送するには、次のコマンドを実行します。

if isunix, system('sshpass -p password scp unet_predict_rtw.mk username@targetname:targetDir/unet_predict/'), end
if isunix, system('sshpass -p password scp input_data.txt username@targetname:targetDir/unet_predict/'), end
if isunix, system('sshpass -p password scp makefile_unet_arm_generic.mk username@targetname:targetDir/unet_predict/'), end

Windows® プラットフォームで、サポート ファイルをターゲット ハードウェアに転送するには、次のコマンドを実行します。

if ispc, system('pscp.exe -pw password unet_predict_rtw.mk username@targetname:targetDir/unet_predict/'), end
if ispc, system('pscp.exe -pw password input_data.txt username@targetname:targetDir/unet_predict/'), end
if ispc, system('pscp.exe -pw password makefile_unet_arm_generic.mk username@targetname:targetDir/unet_predict/'), end

ターゲット ハードウェアでのライブラリのビルド

ターゲット ハードウェアでライブラリをビルドするには、生成された makefile を ARM ハードウェアで実行します。

環境変数 ARM_COMPUTELIBLD_LIBRARY_PATH をターゲット ハードウェアで設定していることを確認してください。MATLAB Coder を使用した深層学習の前提条件を参照してください。変数 ARM_ARCH は、ARM アーキテクチャに基づいてコンパイラ フラグを渡すために makefile で使用されます。変数 ARM_VER は、ARM Compute Library のバージョンに基づいてコードをコンパイルするために makefile で使用されます。

Linux ホスト プラットフォームでは、次のコマンドを実行してライブラリをビルドします。

if isunix, system(['sshpass -p password ssh username@targetname "make -C targetDir/unet_predict/ -f unet_predict_rtw.mk ARM_ARCH=' dlcfg.ArmArchitecture ' ARM_VER=' dlcfg.ArmComputeVersion ' "']), end

Windows ホスト プラットフォームでは、次のコマンドを実行してライブラリをビルドします。

if ispc, system(['plink.exe -l username -pw password targetname "make -C targetDir/unet_predict/ -f unet_predict_rtw.mk ARM_ARCH=' dlcfg.ArmArchitecture ' ARM_VER=' dlcfg.ArmComputeVersion ' "']), end

ターゲットでの実行可能ファイルの作成

次のコマンドで、targetDir をライブラリの生成先フォルダーで置き換えます。変数 heightwidth および channels は、入力データの次元を表します。

main_unet_arm_generic.cpp は、関数 segmentationUnetARM を呼び出してその関数に入力イメージを渡す C++ メイン ラッパー ファイルです。このラッパー ファイルを使用してライブラリをビルドし、実行可能ファイルを作成します。

Linux ホスト プラットフォームで実行可能ファイルを作成するには、次のコマンドを実行します。

if isunix, system('sshpass -p password scp main_unet_arm_generic.cpp username@targetname:targetDir/unet_predict/'), end
if isunix, system(['sshpass -p password ssh username@targetname "make -C targetDir/unet_predict/ IM_H=' num2str(height) ' IM_W=' num2str(width) ' IM_C=' num2str(channels) ' -f makefile_unet_arm_generic.mk"']), end

Windows ホスト プラットフォームで実行可能ファイルを作成するには、次のコマンドを実行します。

if ispc, system('pscp.exe -pw password main_unet_arm_generic.cpp username@targetname:targetDir/unet_predict/'), end
if ispc, system(['plink.exe -l username -pw password targetname "make -C targetDir/unet_predict/ IM_H=' num2str(height) ' IM_W=' num2str(width) ' IM_C=' num2str(channels) ' -f makefile_unet_arm_generic.mk"']), end

ターゲット ハードウェアでの実行可能ファイルの実行

入力イメージ ファイル input_data.txt を使用して、ターゲット ハードウェアで実行可能ファイルを実行します。

Linux ホスト プラットフォームでは、次のコマンドを実行します。

if isunix, system('sshpass -p password ssh username@targetname "cd targetDir/unet_predict/; ./unet input_data.txt output_data.txt"'), end

Windows ホスト プラットフォームでは、次のコマンドを実行します。

if ispc, system('plink.exe -l username -pw password targetname "cd targetDir/unet_predict/; ./unet input_data.txt output_data.txt"'), end

unet 実行可能ファイルは入力データを受け入れます。input_data のサイズが大きいため (2001 x 2001 x 7)、入力イメージはパッチで処理する方が簡単です。この実行可能ファイルは入力イメージを複数のパッチに分割し、パッチはそれぞれネットワーク入力サイズに対応します。実行可能ファイルは、一度に 1 つの特定パッチ内のピクセルに対して予測を実行してから、すべてのパッチを結合します。

ターゲット ハードウェアから MATLAB への出力の転送

生成された出力ファイル output_data.txt をコピーして現在の MATLAB セッションに戻します。Linux プラットフォームでは、次を実行します。

if isunix, system('sshpass -p password scp username@targetname:targetDir/unet_predict/output_data.txt ./'), end

Windows プラットフォームで同じアクションを実行するには、次を実行します。

if ispc, system('pscp.exe -pw password username@targetname:targetDir/unet_predict/output_data.txt ./'), end

変数 segmentedImage に出力データを格納します。

segmentedImage = uint8(importdata('output_data.txt'));
segmentedImage = reshape(segmentedImage,[height,width]);

セグメント化されたイメージの有効な部分だけを抽出するには、そのイメージに入力データのマスク チャネルを乗算します。

segmentedImage = uint8(input_data(:,:,7)~=0) .* segmentedImage;

関数 medfilt2 を使用してノイズと散在ピクセルを取り除きます。

segmentedImageCodegen = medfilt2(segmentedImage,[5,5]);

U-Net のセグメント化されたデータの表示

次のコード行は、クラス名のベクトルを作成します。

classNames = net.Layers(end).Classes;
disp(classNames);

セグメント化された RGB テスト イメージにラベルを重ね合わせ、このセグメント化されたイメージにカラー バーを追加します。

入力データを表示します。

figure(1);
imshow(histeq(input_data(:,:,1:3)));
title('Input Image');

cmap = jet(numel(classNames));
segmentedImageOut = labeloverlay(imadjust(input_data(:,:,4:6),[0 0.6],[0.1 0.9],0.55),segmentedImage,'Transparency',0,'Colormap',cmap);
figure(2);
imshow(segmentedImageOut);

セグメント化されたデータを表示します。

title('Segmented Image using Codegen on ARM');
N = numel(classNames);
ticks = 1/(N*2):1/N:1;
colorbar('TickLabels',cellstr(classNames),'Ticks',ticks,'TickLength',0,'TickLabelInterpreter','none');
colormap(cmap)

セグメント化されたオーバーレイ イメージを表示します。

segmentedImageOverlay = labeloverlay(imadjust(input_data(:,:,4:6),[0 0.6],[0.1 0.9],0.55),segmentedImage,'Transparency',0.7,'Colormap',cmap);
figure(3);
imshow(segmentedImageOverlay);
title('Segmented Overlayed Image');

参考文献

[1] Ronneberger, Olaf, Philipp Fischer, and Thomas Brox. "U-Net: Convolutional Networks for Biomedical Image Segmentation." arXiv preprint arXiv:1505.04597, 2015.

[2] Kemker, R., C. Salvaggio, and C. Kanan. "High-Resolution Multispectral Dataset for Semantic Segmentation." CoRR, abs/1703.01918, 2017.

[3] 使用する基準入力データは、ハームリン ビーチ州立公園のデータの一部です。以下の手順を使用すると、さらに評価するためのデータをダウンロードできます。

if ~exist(fullfile(pwd,'data'))
    url = 'http://home.cis.rit.edu/~cnspci/other/data/rit18_data.mat';
    downloadHamlinBeachMSIData(url,pwd+"/data/");
end

[4] Kemker, Ronald, Carl Salvaggio, and Christopher Kanan. "Algorithms for Semantic Segmentation of Multispectral Remote Sensing Imagery Using Deep Learning." ISPRS Journal of Photogrammetry and Remote Sensing, Deep Learning RS Data, 145 (November 1, 2018): 60-77. https://doi.org/10.1016/j.isprsjprs.2018.04.014.

参考

| | |

関連するトピック