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