Генерация кода для отслеживания лица с PackNGo

В этом примере показано, как сгенерировать код из Face Detection and Tracking Используя пример алгоритма KLT с функцией packNGo. The packNGo Функция (MATLAB Coder) упаковывает все релевантные файлы в сжатый zip-файл, чтобы можно было перемещать, распаковывать и перестраивать проект в другой среде разработки без присутствия MATLAB. В этом примере также показано, как создать make-файл для содержимого packNGo, перестроить исходные файлы и, наконец, запустить независимый исполняемый файл вне окружения MATLAB.

Для этого примера требуется лицензия Coder™ MATLAB ®.

Этот пример является функцией с основным телом в верхней части и стандартными программами в форме Вложенных функций ниже.

function FaceTrackingKLTpackNGoExample()

Настройте компилятор C++

Чтобы запустить этот пример, вы должны иметь доступ к компилятору C++, и вы должны сконфигурировать его с помощью команды 'mex -setup c++'. Для получения дополнительной информации смотрите Выбор компилятора C++. Если вы развертываете приложение на узле MATLAB, используйте компилятор C++, совместимый с компилятором, используемым для создания библиотек OpenCV. Для получения дополнительной информации смотрите Portable C Code Generation для функций, которые используют библиотеку OpenCV.

Выключите вычислительную часть алгоритма в отдельную функцию MATLAB

MATLAB Coder требует, чтобы код 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

Создайте объект строения генерации кода для выхода EXE с вызовом функции packNGo на этапе генерации посткода.

codegenArgs = createCodegenArgs(visiondemo_dir);

Setup окружения генерации кода

Измените имя выходной директории.

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). Используйте команду post генерации кода с функцией packNGo, чтобы создать zip- файла.

Создание независимого исполняемого файла

Разархивируйте zip- файла в новую папку. Обратите внимание, что zip-файл содержит исходные файлы, заголовочные файлы, библиотеки, MAT-файл, содержащий информационный объект сборки, файлы данных. unzipPackageContents и другие вспомогательные функции включены в приложение.

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

Создайте зависящий от платформы make-файл из шаблона make-файла.

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( [ '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 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