packNGo を使用した顔追跡のコード生成
この例では、関数 packNGo を使用してKLT アルゴリズムを使用した顔の検出と追跡の例からコードを生成する方法を説明します。関数packNGo
(MATLAB Coder)は、MATLAB がインストールされていない他の開発環境でプロジェクトの移動、解凍およびリビルドを行えるよう、すべての関連ファイルを zip 圧縮ファイルにパッケージ化します。この例ではまた、packNGo コンテンツ用に makefile を作成し、ソース ファイルをリビルドして、最後にスタンドアロン実行可能ファイルを MATLAB 環境の外で実行する方法についても説明します。
この例には MATLAB® Coder™ のライセンスが必要です。
この例は、本体部分が上部にあり、ヘルパー ルーチンが入れ子関数の形式で下部に置かれた関数です。
function FaceTrackingKLTpackNGoExample()
C++ コンパイラの設定
この例を実行するには C++ コンパイラへのアクセスが必要です。コンパイラは 'mex -setup c++' コマンドを使用して構成しなければなりません。詳細については、C++ コンパイラの選択を参照してください。MATLAB ホストにアプリケーションを展開する場合、OpenCV ライブラリのビルドに使用されるコンパイラと互換性のある C++ コンパイラを使用します。詳細については、OpenCV ライブラリを使用する関数に対応する移植可能な C コードの生成を参照してください。
アルゴリズムにおける計算部分を独立した MATLAB 関数として分割
MATLAB Coder で C コードを生成するには、MATLAB コードが関数の形式を取っていなければなりません。この例のメイン アルゴリズムのコードは、FaceTrackingKLTpackNGo_kernel.m という関数内で定義されています。このファイルはKLT アルゴリズムを使用した顔の検出と追跡から派生しています。MATLAB コードを変更してコード生成に対応させる方法については、特徴のマッチングとレジストレーションを使ったコード生成の紹介の例を参照してください。
fileName = 'FaceTrackingKLTpackNGo_kernel.m'; visiondemo_dir = pwd; currentDir = pwd; % Store the current directory fileName = fullfile(visiondemo_dir, fileName);
packNGo
用のコード生成引数の構成
コード生成後の段階で packNGo の関数呼び出しを伴う EXE 出力を得るために、コード生成構成オブジェクトを作成します。
codegenArgs = createCodegenArgs(visiondemo_dir);
コード生成環境の設定
出力ディレクトリの名前を変更します。
codegenOutDir = fullfile(visiondemo_dir, 'codegen');
mkdir(codegenOutDir);
必要なファイルにアクセスできるよう、既存のディレクトリへのパスを追加します。
currentPath = addpath(visiondemo_dir); pathCleanup = onCleanup(@()path(currentPath)); cd(codegenOutDir); dirChange = onCleanup(@()cd(currentDir));
パッケージ化された zip ファイルの作成
codegen コマンドを packNGo の関数呼び出しと共に呼び出します。
fprintf('-> Generating Code (it may take a few minutes) ....\n');
codegen(codegenArgs{:}, fileName);
-> Generating Code (it may take a few minutes) .... Code generation successful.
codegen コマンドを使用する代わりに、codegen
(MATLAB Coder)を使用してダイアログを開き、コード生成プロジェクトを起動することもできます。関数 packNGo と共にコード生成後のコマンドを使用して、zip ファイルを作成します。
スタンドアロン実行可能ファイルのビルド
zip ファイルを新しいフォルダーに解凍します。zip ファイルにはソース ファイル、ヘッダー ファイル、ライブラリ、ビルド情報オブジェクトを含む MAT ファイル、データ ファイルが含まれています。unzipPackageContents
および他の補助関数は付録に含まれています。
zipFileLocation = codegenOutDir;
fprintf('-> Unzipping files ....\n');
unzipFolderLocation = unzipPackageContents(zipFileLocation);
-> Unzipping files ....
テンプレート makefile から、プラットフォームに依存する makefile を作成します。
fprintf('-> Creating makefile ....\n');
[~, fname, ~] = fileparts(fileName);
makefileName = createMakeFile(visiondemo_dir, unzipFolderLocation, fname);
-> Creating makefile ....
プロジェクトのビルドと実行に必要なコマンドを作成します。
fprintf('-> Creating ''Build Command'' and ''Run command'' ....\n'); [buildCommand, runCommand] = createBuildAndRunCommands(zipFileLocation,... unzipFolderLocation,makefileName,fname);
-> Creating 'Build Command' and 'Run command' ....
build コマンドを使ってプロジェクトをビルドします。
fprintf('-> Building executable....\n');
buildExecutable(unzipFolderLocation, buildCommand);
-> Building executable....
実行可能ファイルの実行と展開
実行可能ファイルを実行し、正しく機能することを確認します。
cd(unzipFolderLocation); system(runCommand);
アプリケーションを別のマシンに展開するには、実行可能ファイルとライブラリ ファイルをコピーします。
isPublishing = ~isempty(snapnow('get')); if ~isPublishing % skip printing out directory to html page fprintf('Executable and library files are located in the following folder:\n%s\n', unzipFolderLocation); fprintf('To re-execute run the following commands:\n'); fprintf('1. cd(''%s'')\n', unzipFolderLocation); fprintf('2. system(''%s'')\n', runCommand); end
付録 - 補助関数
% Configure coder to create executable. Use packNGo at post code % generation stage. function codegenArgs = createCodegenArgs(folderForMainC) % Create arguments required for code generation. % For standalone executable a main C function is required. The main.c % created for this example is compatible with the content of the file % visionFaceTrackingKLTpackNGo_kernel.m mainCFile = fullfile(folderForMainC,'main.c'); % Handle path with space if contains(mainCFile, ' ') mainCFile = ['"' mainCFile '"']; end cfg = coder.config('exe'); cfg.PostCodeGenCommand = 'packNGo(buildInfo,''packType'',''hierarchical'');'; cfg.CustomSource = mainCFile; cfg.CustomInclude = folderForMainC; cfg.EnableOpenMP = false; codegenArgs = {'-config', cfg}; end % Create a folder and unzip the packNGo content into it. function unzipFolderLocation = unzipPackageContents(zipFileLocation) % Unzip the packaged zip file. unzipFolderLocationName = 'unzipPackNGo'; mkdir(unzipFolderLocationName); % Get the name of the zip file generated by packNGo. zipFile = dir('*.zip'); assert(numel(zipFile)==1); unzip(zipFile.name,unzipFolderLocationName); % Unzip internal zip files created in hierarchical packNGo. zipFileInternal = dir(fullfile(unzipFolderLocationName,'*.zip')); assert(numel(zipFileInternal)==3); for i=1:numel(zipFileInternal) unzip(fullfile(unzipFolderLocationName,zipFileInternal(i).name), ... unzipFolderLocationName); end unzipFolderLocation = fullfile(zipFileLocation,unzipFolderLocationName); end % Create platform dependent makefile from template makefile. Use % buildInfo to get info about toolchain. function makefileName = createMakeFile(visiondemo_dir, unzipFolderLocation, fname) % Create Makefile from buildInfo. binfo = load(fullfile(pwd, 'codegen', 'exe', fname, 'buildInfo.mat')); lastDir = cd(unzipFolderLocation); dirCleanup = onCleanup(@()cd(lastDir)); % Get the root directory that contains toolbox/vision sub-directories matlabDirName = getRootDirName(unzipFolderLocation); % Get defines horzcat_with_space = @(cellval)sprintf('%s ',cellval{:}); defs = horzcat_with_space(getDefines(binfo.buildInfo)); % Get source file list if ispc [~, cFiles] = system(['dir /s/b ' '*.c']); [~, cppFiles] = system(['dir /s/b ' '*.cpp']); else [~, cFiles] = system(['find ./ ' '-name ' '''*.c''']); [~, cppFiles] = system(['find ./ ' '-name ' '''*.cpp''']); end cIndx = strfind(cFiles, '.c'); cppIndx = strfind(cppFiles, '.cpp'); srcFilesC = []; srcFilesCPP = []; for i = 1:length(cIndx) if i == 1 startIdx = 1; endIdx = cIndx(i); else startIdx = cIndx(i-1)+1; endIdx = cIndx(i); end [~, b, ~] = fileparts(cFiles(startIdx:endIdx)); srcFilesC = [srcFilesC ' ' b '.c']; %#ok<AGROW> end for i = 1:length(cppIndx) if i == 1 startIdx = 1; endIdx = cppIndx(i); else startIdx = cppIndx(i-1)+1; endIdx = cppIndx(i); end [~, b, ~] = fileparts(cppFiles(startIdx:endIdx)); srcFilesCPP = [srcFilesCPP ' ' b '.cpp']; %#ok<AGROW> end srcFiles = [srcFilesC ' ' srcFilesCPP]; % Get platform dependent names if isunix % both mac and linux tmf = 'TemplateMakefilePackNGo_unix'; if ismac archDir = 'maci64'; dllExt = 'dylib'; else archDir = 'glnxa64'; dllExt = 'so'; end else tmf = 'TemplateMakefilePackNGo_win'; archDir = 'win64'; dllExt = 'dll'; end % Now that we have defines, lets create a platform dependent makefile % from template. fid = fopen(fullfile(visiondemo_dir,tmf)); filecontent = char(fread(fid)'); fclose(fid); newfilecontent = regexprep(filecontent,... {'PASTE_ARCH','PASTE_EXT','PASTE_DEFINES','PASTE_SRCFILES', 'PASTE_MATLAB'},... { archDir, dllExt, defs, srcFiles, matlabDirName}); makefileName = 'Makefile'; mk_name = fullfile(unzipFolderLocation,makefileName); if isunix if( ismac ) [status,sysHeaderPath] = system( 'xcode-select -print-path' ); assert(status==0, ['Could not obtain a path to the system ' ... 'header files using ''xcode-select -print-path''' '']); [status,sdkPaths] = system( 'xcrun -sdk macosx --show-sdk-path' ); assert(status==0, 'Could not find MacOSX sdk' ); % There might be multiple SDK's sdkPathCell = strsplit(sdkPaths,'\n'); for idx = 1:numel(sdkPathCell) if ~isempty(sdkPathCell{idx}) % Pick the first one that's not empty. sdkPath = sdkPathCell{idx}; fprintf('Choosing SDK in %s\n',sdkPath); break; end end assert(~isempty(sdkPath), ... sprintf('There is no sdk available in %s. Please check system environment.\n',sysHeaderPath)); ccCMD = [ 'xcrun clang -isysroot ' deblank( sdkPath ) ]; cppCMD = [ 'xcrun clang++ -isysroot ' deblank( sdkPath ) ]; else ccCMD = 'gcc'; cppCMD = 'g++'; end newfilecontent = regexprep(newfilecontent,'PASTE_CC',ccCMD); newfilecontent = regexprep(newfilecontent,'PASTE_CPP',cppCMD); end fid = fopen(mk_name,'w+'); fprintf(fid,'%s',newfilecontent); fclose(fid); end % Create platform specific commands needed to build the executable and % to run it. function [buildCommand, runCommand] = createBuildAndRunCommands( ... packageLocation,unzipFolderLocation,makefileName,fileName) % Create the build and run command. if ismac buildCommand = [' xcrun make -f ' makefileName]; runCommand = ['./' fileName ' "' fileName '"']; elseif isunix buildCommand = [' make -f ' makefileName]; runCommand = ['./' fileName ' "' fileName '"']; else % On PC we use the generated BAT files (there should be 2) to help % build the generated code. These files are copied to the % unzipFolderLocation where we can use them to build. batFilename = [fileName '_rtw.bat']; batFilelocation = fullfile(packageLocation,'codegen', ... filesep,'exe',filesep,fileName); batFileDestination = unzipFolderLocation; % For MSVC, also copy 'setup_msvc.bat' fid = fopen(fullfile(batFilelocation, batFilename)); batFileContent = fread(fid, '*char'); fclose(fid); if ~isempty(regexp(convertCharsToStrings(batFileContent), 'setup_msvc.bat', 'once')) setup_msvc_batFile = fullfile(batFilelocation, 'setup_msvc.bat'); copyfile(setup_msvc_batFile, batFileDestination); end % Copy it to packNGo output directory. copyfile(fullfile(batFilelocation,batFilename),batFileDestination); % The Makefile we created is named 'Makefile', whereas the Batch % file refers to <filename>_rtw.mk. Hence we rename the file. newMakefileName = [fileName '_rtw.mk']; oldMakefilename = makefileName; copyfile(fullfile(batFileDestination,oldMakefilename),... fullfile(batFileDestination,newMakefileName)); buildCommand = batFilename; runCommand = [fileName '.exe' ' "' fileName '"']; end end % Build the executable with the build command. function buildExecutable(unzipFolderLocation, buildCommand) % Call system command to build the executable. lastDir = cd(unzipFolderLocation); dirCleanup = onCleanup(@()cd(lastDir)); [hadError, sysResults] = system(buildCommand); if hadError error (sysResults); end end % Get the root directory that contains toolbox/vision sub-directories function matlabDirName = getRootDirName(unzipFolderName) dirLists = dir(unzipFolderName); dirLists = dirLists(~ismember({dirLists.name},{'.','..'})); matlabDirName=''; for ij=1:length(dirLists) thisDirName = dirLists(ij).name; if (isfolder(thisDirName)) % subdirectory will have toolbox/vision [subDir1, hasSubDir1] = hasSubdirectory(thisDirName, 'toolbox'); if hasSubDir1 [~, hasSubDir2] = hasSubdirectory(subDir1, 'vision'); if hasSubDir2 matlabDirName = thisDirName; break; end end end end end % Find the directory that contains the specified sub-directory function [subDir, hasSubDir] = hasSubdirectory(dirName, subDirName) dirLists = dir(dirName); dirLists = dirLists(~ismember({dirLists.name},{'.','..'})); subDir = ''; hasSubDir = false; for ij=1:length(dirLists) thisDirName = dirLists(ij).name; thisDir = fullfile(dirName,thisDirName); if (isfolder(thisDir) && strcmp(thisDirName, subDirName)) hasSubDir = true; subDir = thisDir; break; end end end
end