DNN: Face Detection

Face detector based on SSD framework (Single Shot MultiBox Detector), using a reduced ResNet-10 model.

Sources:

import network

[net, blobOpts] = ResNetSSD_FaceDetector();
assert(~net.empty());

minimum confidence threshold of detections to show

confThreshold = 0.5;

prepare video input

cap = createVideoCapture([], 'lena');
pause(1);
assert(cap.isOpened(), 'Could not initialize capturing');

prepare figure

frame = cap.read();
assert(~isempty(frame), 'Could not read frame');
hImg = imshow(frame);

main loop over video feed

while ishghandle(hImg)
    % read frame
    frame = cap.read();
    if isempty(frame), break; end

    % detect faces
    [rects, confs] = detectFaces(frame, net, blobOpts, confThreshold);
    for i=1:size(rects,1)
        frame = cv.rectangle(frame, rects(i,:), ...
            'Color',[0 255 0], 'Thickness',2);
        frame = cv.putText(frame, sprintf('conf = %3.0f%%', confs(i)*100), ...
            rects(i,1:2) - [0 4], 'Color',[255 0 0], 'FontScale',0.5);
    end

    % show inference timing
    [~,t] = net.getPerfProfile();
    t = double(t) / cv.TickMeter.getTickFrequency();
    frame = cv.putText(frame, sprintf('time = %g sec', t), [10 20], ...
        'Color',[255 255 0], 'FontScale',0.5);

    % update plot
    set(hImg, 'CData',frame);
    drawnow;
end
cap.release();

Helper functions

function [rects, confs] = detectFaces(img, net, blobOpts, thresh)
    %DETECTFACES  Run face detection network to detect faces on input image
    %
    % You may play with input blob sizes to balance detection quality and
    % efficiency. The bigger input blob the smaller faces may be detected.
    %

    % detect faces
    net.setInput(cv.Net.blobFromImages(flip(img,3), blobOpts{:}));
    dets = net.forward();

    % SSD output is 1-by-1-by-ndetections-by-7
    % d = [img_id, class_id, confidence, left, bottom, right, top]
    dets = permute(dets, [3 4 2 1]);

    % filter out weak detections
    if nargin < 4, thresh = 0.5; end
    idx = (dets(:,2) == 1 & dets(:,3) > thresh);  % 0: background, 1: face
    dets = dets(idx,:);

    % adjust relative coordinates to image size
    sz = [size(img,2) size(img,1)];
    dets(:,4:7) = bsxfun(@times, dets(:,4:7), [sz sz]);

    % output detections (clamp coords and remove small and out-of-bound rects)
    rects = cv.Rect.from2points(dets(:,4:5), dets(:,6:7));
    rects = cv.Rect.intersect(rects, [0 0 sz]);
    idx = (cv.Rect.area(rects) >= 10);
    rects = rects(idx,:);
    confs = dets(idx,3);
end

Pretrained models

function dname = get_dnn_dir(dname)
    %GET_DNN_DIR  Path to model files, and show where to get them if missing

    dname = fullfile(mexopencv.root(), 'test', 'dnn', dname);
    b = isdir(dname);
    if ~b
        % display help of calling function
        % (assumed to be a local function in current file)
        st = dbstack(1);
        help([mfilename() filemarker() st(1).name])
    end
    assert(b, 'Missing model: %s', dname);
end

function [net, blobOpts] = ResNetSSD_FaceDetector()
    %RESNETSSD_FACEDETECTOR  face detector based on SSD framework with reduced ResNet-10 backbone
    %
    % homepage = https://github.com/opencv/opencv/blob/3.4.0/samples/dnn/face_detector/how_to_train_face_detector.txt
    %
    % ## Model
    %
    % file = test/dnn/ResNetSSD_FaceDetector/deploy.prototxt
    % url  = https://github.com/opencv/opencv/raw/3.4.0/samples/dnn/face_detector/deploy.prototxt
    % hash = 006BAF926232DF6F6332DEFB9C24F94BB9F3764E
    %
    % ## Weights
    %
    % file = test/dnn/ResNetSSD_FaceDetector/res10_300x300_ssd_iter_140000.caffemodel
    % url  = https://github.com/opencv/opencv_3rdparty/raw/dnn_samples_face_detector_20170830/res10_300x300_ssd_iter_140000.caffemodel
    % hash = 15aa726b4d46d9f023526d85537db81cbc8dd566
    % size = 10.1 MB
    %

    dname = get_dnn_dir('ResNetSSD_FaceDetector');
    net = cv.Net('Caffe', ...
        fullfile(dname, 'deploy.prototxt'), ...
        fullfile(dname, 'res10_300x300_ssd_iter_140000.caffemodel'));
    blobOpts = {'SwapRB',false, 'Crop',false, 'Size',[300 300], 'Mean',[104 117 123]};
end