Main Content

Detect Face (Raspberry Pi2)

This example shows how to use the MATLAB® Coder™ to generate C code from a MATLAB file and deploy the application on an ARM target.

The example reads video frames from a webcam and detects faces in each of the frames using the Viola-Jones face detection algorithm. The detected faces are displayed with bounding boxes. The webcam function, from 'MATLAB Support Package for USB Webcams', and the VideoPlayer object, from the Computer Vision System toolbox™, are used for the simulation on the MATLAB host. The two functions do not support the ARM target, so OpenCV-based webcam reader and video viewer functions are used for deployment.

The target must have OpenCV version 3.4.0 libraries (built with GTK) and a standard C++ compiler. A Raspberry Pi 2 with Raspbian Stretch operating system was used for deployment. The example should work on any ARM target.

This example requires a MATLAB Coder license.

This example is a function with the main body at the top and helper routines in the form of Nested Functions below.

function FaceDetectionARMCodeGenerationExample()

Set Up Your C++ Compiler

To run this example, you must have access to a C++ compiler and you must configure it using 'mex -setup c++' command. For more information, see Choose a C++ Compiler.

Break Out the Computational Part of the Algorithm into a Separate MATLAB Function

MATLAB Coder requires MATLAB code to be in the form of a function in order to generate C code. The code for the main algorithm of this example resides in a function called faceDetectionARMKernel.m. The function takes an image from a webcam, as the input. The function outputs the image with a bounding box around the detected faces. The output image will be displayed on video viewer window. To learn how to modify the MATLAB code to make it compatible for code generation, you can look at example Introduction to Code Generation with Feature Matching and Registration.

fileName = 'faceDetectionARMKernel.m';

Create Main Function with I/O Functionality

For a standalone executable target, MATLAB Coder requires that you create a C file containing a function named "main". This example uses faceDetectionARMMain.c file. This main function in this file performs the following tasks:

  • Reads video frames from the webcam

  • Sends video frames to the face detection algorithm

  • Displays output frames containing bounding boxes around detected faces

For simulation on MATLAB host, the tasks performed in faceDetectionARMMain.c file is implemented in faceDetectionARMMain.m

Webcam Reader and Video Viewer

For deployment on ARM, this example implements webcam reader functionality using OpenCV functions. It also implements a video viewer using OpenCV functions. These OpenCV based utility functions are implemented in the following files:

  • helperOpenCVWebcam.hpp

  • helperOpenCVWebcam.cpp

  • helperOpenCVVideoViewer.cpp

  • helperOpenCVVideoViewer.hpp

For simulation on MATLAB host, the example uses the webcam function from the 'MATLAB Support Package for USB Webcams' and the VideoPlayer object from the Computer Vision System toolbox. Run the simulation on the MATLAB host by typing faceDetectionARMMain at the MATLAB® command line.

OpenCV for ARM Target

This example requires that you install OpenCV 3.4.0 libraries on your ARM target. The video viewer requires that you build the highqui library in OpenCV with GTK for the ARM target.

Follow the steps to download and build OpenCV 3.4.0 on Raspberry Pi 2 with preinstalled Raspbian Stretch. You must update your system firmware or install other developer tools and packages as needed for your system configuration before you start building OpenCV.

Turn off INSTALL_C_EXAMPLES due to: https://github.com/opencv/opencv/issues/5851

Turn off ENABLE_PRECOMPILED_HEADERS due to: https://github.com/opencv/opencv/issues/9942

  • $ wget -O opencv-3.4.0.zip https://github.com/opencv/opencv/archive/3.4.0.zip

  • $ unzip opencv-3.4.0.zip

  • $ cd opencv-3.4.0

  • $ mkdir build

  • $ cd build

  • $ cmake -D CMAKE_BUILD_TYPE=RELEASE -D CMAKE_INSTALL_PREFIX=/usr/local -D INSTALL_C_EXAMPLES=OFF -D BUILD_EXAMPLES=ON -D WITH_GTK=ON -D WITH_FFMPEG=OFF -D ENABLE_PRECOMPILED_HEADERS=OFF ..

These steps are followed to compile and install OpenCV:

  • $ make

  • $ sudo make install

For official deployment of the example, OpenCV libraries were installed in the following directory on Raspberry Pi 2:

/usr/local/lib

and the associated headers were placed in

/usr/local/include

Configure Code Generation Arguments

Create a code generation configuration object for EXE output.

codegenArgs = createCodegenArgs();

Generate Code

Invoke codegen command.

fprintf('-> Generating Code (it may take a few minutes) ....\n');
codegen(codegenArgs{:}, fileName);
% During code generation, all dependent file information is stored in a mat
% file named buildInfo.mat.
-> Generating Code (it may take a few minutes) ....
Code generation successful.

Create the Packaged Zip-file

Use build information stored in buildInfo.mat to create a zip folder using packNGo.

fprintf('-> Creating zip folder (it may take a few minutes) ....\n');
bInfo = load(fullfile('codegen','exe','faceDetectionARMKernel','buildInfo.mat'));
packNGo(bInfo.buildInfo, {'packType', 'hierarchical', ...
                          'fileName', 'faceDetectionARMKernel'});
% The generated zip folder is faceDetectionARMKernel.zip
-> Creating zip folder (it may take a few minutes) ....

Create Project Folder

Unzip faceDetectionARMKernel.zip into a folder named FaceDetectionARM. Unzip all files and remove the .zip files.

packngoDir = hUnzipPackageContents();
Warning: Directory already exists. 

Update Makefile and Copy to Project Folder

The Makefile, faceDetectionARMMakefile.mk, provided in this example is written for Raspberry PI 2 with specific optimization flags. The Makefile was written to work with GCC in a Linux environment and with your OpenCV libraries located in /usr/local/lib. You can update the Makefile based on your target configuration. Copy the Makefile to the project folder.

copyfile('faceDetectionARMMakefile.mk', packngoDir);
% Also move the file containing the main function in the top level folder.
copyfile('faceDetectionARMMain.c', packngoDir);
% For simplicity, make sure the root directory name is matlab.
setRootDirectory(packngoDir);

Deployment on ARM

Deploy your project on ARM:

disp('Follow these steps to deploy your project on ARM');
Follow these steps to deploy your project on ARM

Transfer Code to ARM Target

Transfer your project folder named FaceDetectionARM to your ARM target using your preferred file transfer tool. Since the Raspberry Pi 2 (with Raspbian Stretch) already has an SSH server, you can use SFTP to transfer files from host to target.

For official deployment of this example, the FileZilla SFTP Client was installed on the host machine and the project folder was transferred from the host to the /home/pi/FaceDetectionARM folder on Raspberry Pi.

disp('Step-1: Transfer the folder ''FaceDetectionARM'' to your ARM target');
Step-1: Transfer the folder 'FaceDetectionARM' to your ARM target

Build the Executable on ARM

Run the makefile to build the executable on ARM. For Raspberry Pi 2, (with Raspbian Stretch), open a linux shell and cd to /home/pi/FaceDetectionARM. Build the executable using the following command:

make -f faceDetectionARMMakefile

The command creates an executable, faceDetectionARMKernel.

disp('Step-2: Build the executable on ARM using the shell command: make -f faceDetectionARMMakefile.mk');
Step-2: Build the executable on ARM using the shell command: make -f faceDetectionARMMakefile.mk

Run the Executable on ARM

Run the executable generated in the above step. For Raspberry Pi 2, (with Raspbian Stretch), use the following command in the shell window:

./faceDetectionARMKernel

Make sure that you are connected to the Raspberry Pi with a window manager, and not just through a command line terminal to avoid errors related to GTK. This is necessary for the tracking window to show up.

To close the video viewer while the executable is running on Raspberry Pi2, click on the video viewer and press the escape key.

disp('Step-3: Run the executable on ARM using the shell command: ./faceDetectionARMKernel');
Step-3: Run the executable on ARM using the shell command: ./faceDetectionARMKernel

Appendix - Helper Functions

    % Configure coder to create executable. Use packNGo at post code
    % generation stage.
    function codegenArgs = createCodegenArgs()
        % Create arguments required for code generation.

        % First - create configuration object
        %
        % For standalone executable a main C function is required. The
        % faceDetectionARMMain.c created for this example is compatible
        % with the content of the file faceDetectionARMKernel.m
        mainCFile = 'faceDetectionARMMain.c';

        % Include helper functions
        camCPPFile = 'helperOpenCVWebcam.cpp';
        viewerCPPFile = 'helperOpenCVVideoViewer.cpp';

        % Handle path with space
        if contains(mainCFile, ' ')
            mainCFile     = ['"' mainCFile '"'];
            camCPPFile    = ['"' camCPPFile '"'];
            viewerCPPFile = ['"' viewerCPPFile '"'];
        end

        % Create configuration object
        cfg = coder.config('exe');
        cfg.CustomSource       = sprintf('%s\n%s\n%s',mainCFile,camCPPFile,viewerCPPFile);
        cfg.CustomInclude      = pwd;
        % Set production hardware to ARM to generate ARM compatible portable code
        cfg.HardwareImplementation.ProdHWDeviceType = 'ARM Compatible->ARM Cortex';
        cfg.EnableOpenMP       = false;

        % Create input arguments
        inRGB_type = coder.typeof(uint8(0),[480 640 3]);
        % Use '-c' option to generate C code without calling C++ compiler.
        codegenArgs = {'-config', cfg, '-c', '-args', {inRGB_type}};

    end

    % Unzip the packaged zip file
    function packngoDir   = hUnzipPackageContents()

        packngoDirName = 'FaceDetectionARM';

        % create packngo directory
        mkdir(packngoDirName);

        % get the name of the single zip file generated by packngo
        zipFile = dir('*.zip');
        assert(numel(zipFile)==1);

        unzip(zipFile.name,packngoDirName);

        % unzip internal zip files created in hierarchical packNGo
        zipFileInternal = dir(fullfile(packngoDirName,'*.zip'));

        for i=1:numel(zipFileInternal)
            unzip(fullfile(packngoDirName,zipFileInternal(i).name), ...
                packngoDirName);
        end
        % delete internal zip files
        delete(fullfile(packngoDirName,'*.zip'));
        packngoDir = packngoDirName;
    end

    % Set root directory as matlab
    function setRootDirectory(packngoDir)
        dirList = dir(packngoDir);
        if isempty(find(ismember({dirList.name},'matlab'), 1))
            % root directory is not matlab. Change it to matlab
            for i=1:length(dirList)
                thisDir = fullfile(packngoDir,dirList(i).name, 'toolbox', 'vision');
                if isfolder(thisDir)
                    % rename the dir
                    movefile(fullfile(packngoDir,dirList(i).name), ...
                             fullfile(packngoDir,'matlab'));
                    break;
                end
            end
        end
    end
end