ドキュメンテーション

最新のリリースでは、このページがまだ翻訳されていません。 このページの最新版は英語でご覧になれます。

packNGo を使用した顔追跡のコード生成

この例では、関数 packNGo を用いてKLT アルゴリズムを使用した顔の検出と追跡の例からコードを生成する方法を説明します。関数 packNGo は、MATLAB がインストールされていない他の開発環境でプロジェクトの移動、解凍およびリビルドを行えるよう、すべての関連ファイルを zip 圧縮ファイルにパッケージ化します。この例ではまた、packNGo コンテンツ用に makefile を作成し、ソース ファイルをリビルドして、最後にスタンドアロン実行可能ファイルを MATLAB 環境の外で実行する方法についても説明します。

この例には MATLAB® Coder™ のライセンスが必要です。

この例は、本体部分が上部にあり、ヘルパー ルーチンが入れ子関数の形式で下部に置かれた関数です。

function FaceTrackingKLTpackNGoExample()

C++ コンパイラの設定

この例を実行するには C++ コンパイラへのアクセスが必要です。コンパイラは 'mex -setup c++' コマンドを使用して構成しなければなりません。詳細については、C++ コンパイラの選択を参照してください。MATLAB ホストにアプリケーションを配布する場合、OpenCV ライブラリのビルドに使用されるコンパイラと互換性のある C++ コンパイラを使用します。詳細については、OpenCV ライブラリのビルドに使用されるコンパイラに関するページを参照してください。

アルゴリズムにおける計算部分を独立した 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) ....

codegen コマンドを使用する代わりに、coder を使用してダイアログを開き、コード生成プロジェクトを起動することもできます。関数 packNGo と共にコード生成後のコマンドを使用して、zip ファイルを作成します。

スタンドアロン実行可能ファイルのビルド

zip ファイルを新しいフォルダーに解凍します。zip ファイルにはソース ファイル、ヘッダー ファイル、ライブラリ、ビルド情報オブジェクトを含む MAT ファイル、データ ファイルが含まれています。unzipPackageContents および他の補助関数は付録に含まれています。

zipFileLocation  = codegenOutDir;
fprintf('-> Unzipping files ....\n');
unzipFolderLocation       = unzipPackageContents(zipFileLocation);
-> Unzipping files ....

テンプレート makefile から、プラットフォームに依存する makefile を作成します。

fprintf('-> Creating makefile ....\n');
makefileName = createMakeFile(visiondemo_dir, unzipFolderLocation);
-> Creating makefile ....

プロジェクトのビルドと実行に必要なコマンドを作成します。

[~, fname, ~] = fileparts(fileName);
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)
        % Create Makefile from buildInfo.

        binfo = load('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( [ 'find ' deblank( sysHeaderPath ) ...
                                             ' -name ''MacOSX*.sdk''' ] );
                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 VS 2017, also copy 'setup_msvc150.bat'
            fid = fopen(fullfile(batFilelocation, batFilename));
            batFileContent = fread(fid, '*char');
            fclose(fid);
            if ~isempty(regexp(convertCharsToStrings(batFileContent), 'setup_msvc150.bat', 'once'))
                setup_msvc150_batFile = fullfile(batFilelocation, 'setup_msvc150.bat');
                copyfile(setup_msvc150_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