├── .gitignore ├── doc ├── logo.png ├── output1.jpg ├── output2.jpg ├── getting_started_01.png ├── getting_started_02.png ├── getting_started.md └── logo.svg ├── resources └── project │ ├── Root.type.Files │ ├── code.type.File.xml │ ├── doc.type.File.xml │ ├── test.type.File.xml │ ├── .circleci.type.File.xml │ ├── LICENSE.type.File.xml │ ├── README.md.type.File.xml │ ├── mtcnn.prj.type.File.xml │ ├── .gitignore.type.File.xml │ ├── code.type.File │ │ ├── mtcnn.type.File.xml │ │ ├── 1.type.DIR_SIGNIFIER.xml │ │ └── mtcnn.type.File │ │ │ ├── doc.type.File.xml │ │ │ ├── +mtcnn.type.File.xml │ │ │ ├── weights.type.File.xml │ │ │ ├── 1.type.DIR_SIGNIFIER.xml │ │ │ ├── +mtcnn.type.File │ │ │ ├── +util.type.File.xml │ │ │ ├── 1.type.DIR_SIGNIFIER.xml │ │ │ ├── +util.type.File │ │ │ │ ├── 1.type.DIR_SIGNIFIER.xml │ │ │ │ ├── prelu.m.type.File.xml │ │ │ │ ├── cropImage.m.type.File.xml │ │ │ │ ├── makeSquare.m.type.File.xml │ │ │ │ ├── preluLayer.m.type.File.xml │ │ │ │ ├── DagNetworkStrategy.m.type.File.xml │ │ │ │ ├── DlNetworkStrategy.m.type.File.xml │ │ │ │ ├── NetworkStrategy.m.type.File.xml │ │ │ │ ├── applyCorrection.m.type.File.xml │ │ │ │ ├── calculateScales.m.type.File.xml │ │ │ │ └── convertToDagNet.m.type.File.xml │ │ │ ├── onet.m.type.File.xml │ │ │ ├── pnet.m.type.File.xml │ │ │ ├── rnet.m.type.File.xml │ │ │ ├── Detector.m.type.File.xml │ │ │ ├── detectFaces.m.type.File.xml │ │ │ └── proposeRegions.m.type.File.xml │ │ │ ├── doc.type.File │ │ │ ├── 1.type.DIR_SIGNIFIER.xml │ │ │ ├── helptoc.xml.type.File.xml │ │ │ ├── GettingStarted.mlx.type.File.xml │ │ │ └── GettingStarted.html.type.File.xml │ │ │ ├── weights.type.File │ │ │ ├── 1.type.DIR_SIGNIFIER.xml │ │ │ ├── onet.mat.type.File.xml │ │ │ ├── pnet.mat.type.File.xml │ │ │ └── rnet.mat.type.File.xml │ │ │ ├── Contents.m.type.File.xml │ │ │ ├── info.xml.type.File.xml │ │ │ └── mtcnnRoot.m.type.File.xml │ ├── doc.type.File │ │ ├── 1.type.DIR_SIGNIFIER.xml │ │ ├── logo.png.type.File.xml │ │ ├── logo.svg.type.File.xml │ │ ├── output1.jpg.type.File.xml │ │ ├── output2.jpg.type.File.xml │ │ ├── getting_started.md.type.File.xml │ │ ├── getting_started_01.png.type.File.xml │ │ └── getting_started_02.png.type.File.xml │ ├── test.type.File │ │ ├── +tests.type.File.xml │ │ ├── resources.type.File.xml │ │ ├── 1.type.DIR_SIGNIFIER.xml │ │ ├── +tests.type.File │ │ │ ├── 1.type.DIR_SIGNIFIER.xml │ │ │ ├── DetectorTest.m.type.File.xml │ │ │ ├── UtilTest.m.type.File.xml │ │ │ ├── DetectFacesTest.m.type.File.xml │ │ │ ├── PerformanceTest.m.type.File.xml │ │ │ └── ProposeRegionsTest.m.type.File.xml │ │ ├── resources.type.File │ │ │ ├── 1.type.DIR_SIGNIFIER.xml │ │ │ └── ref.mat.type.File.xml │ │ ├── mtcnnTestRoot.m.type.File.xml │ │ └── makeDetectionReference.m.type.File.xml │ ├── .circleci.type.File │ │ ├── 1.type.DIR_SIGNIFIER.xml │ │ └── config.yml.type.File.xml │ ├── kpzhang93-MTCNN_face_detection_alignment.rights.txt.type.File.xml │ └── generateDAGNetVersions.m.type.File.xml │ ├── Project.xml │ ├── uuid-888b3c04-163a-4681-b133-4e6d0607d560.xml │ ├── ProjectData.type.Info.xml │ ├── Root.type.Categories │ ├── FileClassCategory.type.Category │ │ ├── design.type.Label.xml │ │ ├── none.type.Label.xml │ │ ├── other.type.Label.xml │ │ ├── test.type.Label.xml │ │ ├── artifact.type.Label.xml │ │ ├── derived.type.Label.xml │ │ └── convenience.type.Label.xml │ └── FileClassCategory.type.Category.xml │ ├── Root.type.ProjectPath │ └── cc5a25c3-9c36-4121-bf9f-975cb273e5c8.type.Reference.xml │ └── Root.type.EntryPoints │ └── 6e86f0ef-faba-4a6f-a9aa-491caaa23a05.type.EntryPoint.xml ├── test ├── resources │ └── ref.mat ├── mtcnnTestRoot.m ├── makeDetectionReference.m └── +tests │ ├── RegressionTest.m │ ├── DocTest │ ├── ProposeRegionsTest.m │ ├── PerformanceTest.m │ ├── DetectFacesTest.m │ ├── UtilTest.m │ └── DetectorTest.m ├── code └── mtcnn │ ├── weights │ ├── onet.mat │ ├── pnet.mat │ └── rnet.mat │ ├── doc │ ├── GettingStarted.mlx │ └── helptoc.xml │ ├── +mtcnn │ ├── +util │ │ ├── prelu.m │ │ ├── NetworkStrategy.m │ │ ├── makeSquare.m │ │ ├── cropImage.m │ │ ├── applyCorrection.m │ │ ├── preluLayer.m │ │ ├── calculateScales.m │ │ ├── DagNetworkStrategy.m │ │ ├── DlNetworkStrategy.m │ │ └── convertToDagNet.m │ ├── pnet.m │ ├── rnet.m │ ├── proposeRegions.m │ ├── onet.m │ ├── detectFaces.m │ └── Detector.m │ ├── mtcnnRoot.m │ ├── Contents.m │ └── info.xml ├── Mtcnnfacedetection.prj ├── generateDAGNetVersions.m ├── .circleci └── config.yml ├── kpzhang93-MTCNN_face_detection_alignment.rights.txt ├── LICENSE ├── README.md └── mtcnn.prj /.gitignore: -------------------------------------------------------------------------------- 1 | *.mltbx 2 | code/mtcnn/weights/dag*.mat -------------------------------------------------------------------------------- /doc/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matlab-deep-learning/mtcnn-face-detection/HEAD/doc/logo.png -------------------------------------------------------------------------------- /resources/project/Root.type.Files/code.type.File.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /resources/project/Root.type.Files/doc.type.File.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /resources/project/Root.type.Files/test.type.File.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /doc/output1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matlab-deep-learning/mtcnn-face-detection/HEAD/doc/output1.jpg -------------------------------------------------------------------------------- /doc/output2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matlab-deep-learning/mtcnn-face-detection/HEAD/doc/output2.jpg -------------------------------------------------------------------------------- /resources/project/Project.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /resources/project/Root.type.Files/.circleci.type.File.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /resources/project/Root.type.Files/LICENSE.type.File.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /resources/project/Root.type.Files/README.md.type.File.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /resources/project/Root.type.Files/mtcnn.prj.type.File.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /resources/project/Root.type.Files/.gitignore.type.File.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /resources/project/uuid-888b3c04-163a-4681-b133-4e6d0607d560.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /resources/project/ProjectData.type.Info.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /resources/project/Root.type.Files/code.type.File/mtcnn.type.File.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /resources/project/Root.type.Files/doc.type.File/1.type.DIR_SIGNIFIER.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /resources/project/Root.type.Files/doc.type.File/logo.png.type.File.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /resources/project/Root.type.Files/doc.type.File/logo.svg.type.File.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /resources/project/Root.type.Files/test.type.File/+tests.type.File.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /resources/project/Root.type.Files/test.type.File/resources.type.File.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /test/resources/ref.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matlab-deep-learning/mtcnn-face-detection/HEAD/test/resources/ref.mat -------------------------------------------------------------------------------- /resources/project/Root.type.Files/code.type.File/1.type.DIR_SIGNIFIER.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /resources/project/Root.type.Files/doc.type.File/output1.jpg.type.File.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /resources/project/Root.type.Files/doc.type.File/output2.jpg.type.File.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /resources/project/Root.type.Files/test.type.File/1.type.DIR_SIGNIFIER.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /code/mtcnn/weights/onet.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matlab-deep-learning/mtcnn-face-detection/HEAD/code/mtcnn/weights/onet.mat -------------------------------------------------------------------------------- /code/mtcnn/weights/pnet.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matlab-deep-learning/mtcnn-face-detection/HEAD/code/mtcnn/weights/pnet.mat -------------------------------------------------------------------------------- /code/mtcnn/weights/rnet.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matlab-deep-learning/mtcnn-face-detection/HEAD/code/mtcnn/weights/rnet.mat -------------------------------------------------------------------------------- /doc/getting_started_01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matlab-deep-learning/mtcnn-face-detection/HEAD/doc/getting_started_01.png -------------------------------------------------------------------------------- /doc/getting_started_02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matlab-deep-learning/mtcnn-face-detection/HEAD/doc/getting_started_02.png -------------------------------------------------------------------------------- /resources/project/Root.type.Files/.circleci.type.File/1.type.DIR_SIGNIFIER.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /resources/project/Root.type.Files/.circleci.type.File/config.yml.type.File.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /resources/project/Root.type.Files/code.type.File/mtcnn.type.File/doc.type.File.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /resources/project/Root.type.Files/doc.type.File/getting_started.md.type.File.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /resources/project/Root.type.Files/code.type.File/mtcnn.type.File/+mtcnn.type.File.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /resources/project/Root.type.Files/code.type.File/mtcnn.type.File/weights.type.File.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /resources/project/Root.type.Files/doc.type.File/getting_started_01.png.type.File.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /resources/project/Root.type.Files/doc.type.File/getting_started_02.png.type.File.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /code/mtcnn/doc/GettingStarted.mlx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matlab-deep-learning/mtcnn-face-detection/HEAD/code/mtcnn/doc/GettingStarted.mlx -------------------------------------------------------------------------------- /resources/project/Root.type.Files/code.type.File/mtcnn.type.File/1.type.DIR_SIGNIFIER.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /resources/project/Root.type.Files/test.type.File/+tests.type.File/1.type.DIR_SIGNIFIER.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /resources/project/Root.type.Files/test.type.File/resources.type.File/1.type.DIR_SIGNIFIER.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /resources/project/Root.type.Files/code.type.File/mtcnn.type.File/+mtcnn.type.File/+util.type.File.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /resources/project/Root.type.Files/kpzhang93-MTCNN_face_detection_alignment.rights.txt.type.File.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /resources/project/Root.type.Files/code.type.File/mtcnn.type.File/+mtcnn.type.File/1.type.DIR_SIGNIFIER.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /resources/project/Root.type.Files/code.type.File/mtcnn.type.File/doc.type.File/1.type.DIR_SIGNIFIER.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /resources/project/Root.type.Files/code.type.File/mtcnn.type.File/weights.type.File/1.type.DIR_SIGNIFIER.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /test/mtcnnTestRoot.m: -------------------------------------------------------------------------------- 1 | function folder = mtcnnTestRoot() 2 | % Copyright 2019 The MathWorks, Inc. 3 | folder = fileparts(mfilename('fullpath')); 4 | end -------------------------------------------------------------------------------- /resources/project/Root.type.Files/code.type.File/mtcnn.type.File/+mtcnn.type.File/+util.type.File/1.type.DIR_SIGNIFIER.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /resources/project/Root.type.Categories/FileClassCategory.type.Category/design.type.Label.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /resources/project/Root.type.Categories/FileClassCategory.type.Category/none.type.Label.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /resources/project/Root.type.Categories/FileClassCategory.type.Category/other.type.Label.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /resources/project/Root.type.Categories/FileClassCategory.type.Category/test.type.Label.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /resources/project/Root.type.Categories/FileClassCategory.type.Category/artifact.type.Label.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /resources/project/Root.type.Categories/FileClassCategory.type.Category/derived.type.Label.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /resources/project/Root.type.ProjectPath/cc5a25c3-9c36-4121-bf9f-975cb273e5c8.type.Reference.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /resources/project/Root.type.Categories/FileClassCategory.type.Category/convenience.type.Label.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /resources/project/Root.type.Categories/FileClassCategory.type.Category.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /Mtcnnfacedetection.prj: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /code/mtcnn/+mtcnn/+util/prelu.m: -------------------------------------------------------------------------------- 1 | function x = prelu(x, params) 2 | % prelu Parameterized relu activation. 3 | 4 | % Copyright 2019 The MathWorks, Inc. 5 | 6 | x = max(0, x) + params.*min(0, x); 7 | 8 | end -------------------------------------------------------------------------------- /code/mtcnn/mtcnnRoot.m: -------------------------------------------------------------------------------- 1 | function folder = mtcnnRoot() 2 | % mtcnnRoot Returns the root folder of MTCNN. 3 | 4 | % Copyright 2019 The MathWorks, Inc. 5 | 6 | folder = fileparts(mfilename('fullpath')); 7 | end -------------------------------------------------------------------------------- /resources/project/Root.type.Files/generateDAGNetVersions.m.type.File.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /resources/project/Root.type.Files/test.type.File/mtcnnTestRoot.m.type.File.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /resources/project/Root.type.EntryPoints/6e86f0ef-faba-4a6f-a9aa-491caaa23a05.type.EntryPoint.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /resources/project/Root.type.Files/test.type.File/makeDetectionReference.m.type.File.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /resources/project/Root.type.Files/code.type.File/mtcnn.type.File/Contents.m.type.File.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /resources/project/Root.type.Files/code.type.File/mtcnn.type.File/info.xml.type.File.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /resources/project/Root.type.Files/code.type.File/mtcnn.type.File/mtcnnRoot.m.type.File.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /resources/project/Root.type.Files/test.type.File/+tests.type.File/DetectorTest.m.type.File.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /resources/project/Root.type.Files/test.type.File/+tests.type.File/UtilTest.m.type.File.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /resources/project/Root.type.Files/test.type.File/resources.type.File/ref.mat.type.File.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /resources/project/Root.type.Files/test.type.File/+tests.type.File/DetectFacesTest.m.type.File.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /resources/project/Root.type.Files/test.type.File/+tests.type.File/PerformanceTest.m.type.File.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /resources/project/Root.type.Files/code.type.File/mtcnn.type.File/+mtcnn.type.File/onet.m.type.File.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /resources/project/Root.type.Files/code.type.File/mtcnn.type.File/+mtcnn.type.File/pnet.m.type.File.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /resources/project/Root.type.Files/code.type.File/mtcnn.type.File/+mtcnn.type.File/rnet.m.type.File.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /resources/project/Root.type.Files/test.type.File/+tests.type.File/ProposeRegionsTest.m.type.File.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /resources/project/Root.type.Files/code.type.File/mtcnn.type.File/+mtcnn.type.File/Detector.m.type.File.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /resources/project/Root.type.Files/code.type.File/mtcnn.type.File/doc.type.File/helptoc.xml.type.File.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /resources/project/Root.type.Files/code.type.File/mtcnn.type.File/weights.type.File/onet.mat.type.File.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /resources/project/Root.type.Files/code.type.File/mtcnn.type.File/weights.type.File/pnet.mat.type.File.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /resources/project/Root.type.Files/code.type.File/mtcnn.type.File/weights.type.File/rnet.mat.type.File.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /resources/project/Root.type.Files/code.type.File/mtcnn.type.File/+mtcnn.type.File/detectFaces.m.type.File.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /resources/project/Root.type.Files/code.type.File/mtcnn.type.File/+mtcnn.type.File/proposeRegions.m.type.File.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /resources/project/Root.type.Files/code.type.File/mtcnn.type.File/doc.type.File/GettingStarted.mlx.type.File.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /resources/project/Root.type.Files/code.type.File/mtcnn.type.File/doc.type.File/GettingStarted.html.type.File.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /resources/project/Root.type.Files/code.type.File/mtcnn.type.File/+mtcnn.type.File/+util.type.File/prelu.m.type.File.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /resources/project/Root.type.Files/code.type.File/mtcnn.type.File/+mtcnn.type.File/+util.type.File/cropImage.m.type.File.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /resources/project/Root.type.Files/code.type.File/mtcnn.type.File/+mtcnn.type.File/+util.type.File/makeSquare.m.type.File.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /resources/project/Root.type.Files/code.type.File/mtcnn.type.File/+mtcnn.type.File/+util.type.File/preluLayer.m.type.File.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /code/mtcnn/+mtcnn/+util/NetworkStrategy.m: -------------------------------------------------------------------------------- 1 | classdef (Abstract) NetworkStrategy < handle 2 | methods 3 | load(obj) 4 | pnet = getPNet(obj) 5 | [probs, correction] = applyRNet(obj, im) 6 | [probs, correction, landmarks] = applyONet(obj, im) 7 | end 8 | end -------------------------------------------------------------------------------- /resources/project/Root.type.Files/code.type.File/mtcnn.type.File/+mtcnn.type.File/+util.type.File/DagNetworkStrategy.m.type.File.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /resources/project/Root.type.Files/code.type.File/mtcnn.type.File/+mtcnn.type.File/+util.type.File/DlNetworkStrategy.m.type.File.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /resources/project/Root.type.Files/code.type.File/mtcnn.type.File/+mtcnn.type.File/+util.type.File/NetworkStrategy.m.type.File.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /resources/project/Root.type.Files/code.type.File/mtcnn.type.File/+mtcnn.type.File/+util.type.File/applyCorrection.m.type.File.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /resources/project/Root.type.Files/code.type.File/mtcnn.type.File/+mtcnn.type.File/+util.type.File/calculateScales.m.type.File.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /resources/project/Root.type.Files/code.type.File/mtcnn.type.File/+mtcnn.type.File/+util.type.File/convertToDagNet.m.type.File.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /code/mtcnn/Contents.m: -------------------------------------------------------------------------------- 1 | % MTCNN 2 | % Version 1.2.2 (R2020a) 14-September-2020 3 | % 4 | % Files 5 | % mtcnn.detectFaces - Use a pretrained model to detect faces in an image. 6 | % mtcnn.Detector - Create a detector object to detect faces in an image. 7 | 8 | % Copyright 2020 The MathWorks, Inc. 9 | -------------------------------------------------------------------------------- /code/mtcnn/doc/helptoc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | MTCNN Face Detection 6 | 7 | GitHub repository 8 | 9 | 10 | -------------------------------------------------------------------------------- /code/mtcnn/+mtcnn/+util/makeSquare.m: -------------------------------------------------------------------------------- 1 | function outBox = makeSquare(bboxes) 2 | % makeSquare Make the bounding box square. 3 | 4 | % Copyright 2019 The MathWorks, Inc. 5 | 6 | maxSide = max(bboxes(:, 3:4), [], 2); 7 | cx = bboxes(:, 1) + bboxes(:, 3)/2; 8 | cy = bboxes(:, 2) + bboxes(:, 4)/2; 9 | outBox = [cx - maxSide/2, cy - maxSide/2, maxSide, maxSide]; 10 | end -------------------------------------------------------------------------------- /test/makeDetectionReference.m: -------------------------------------------------------------------------------- 1 | function makeDetectionReference() 2 | % Run the detector in known good config to create reference boxes, 3 | % scores and landmarks for regression tests. 4 | im = imread("visionteam.jpg"); 5 | [bboxes, scores, landmarks] = mtcnn.detectFaces(im); 6 | 7 | filename = fullfile(mtcnnTestRoot(), "resources", "ref.mat"); 8 | save(filename, "bboxes", "scores", "landmarks"); 9 | 10 | end -------------------------------------------------------------------------------- /generateDAGNetVersions.m: -------------------------------------------------------------------------------- 1 | function generateDAGNetVersions 2 | % generate DAG net versions if not there already 3 | nets = ["p", "r", "o"]; 4 | for iNet = 1:numel(nets) 5 | thisNet = nets(iNet); 6 | dagFile = fullfile(mtcnnRoot, 'weights', ... 7 | strcat('dag', char(upper(thisNet)), 'Net.mat')); 8 | if ~isfile(dagFile) 9 | net = mtcnn.util.convertToDagNet(thisNet); 10 | save(dagFile, "net"); 11 | end 12 | end -------------------------------------------------------------------------------- /code/mtcnn/+mtcnn/+util/cropImage.m: -------------------------------------------------------------------------------- 1 | function cropped = cropImage(im, bboxes, outSize) 2 | % cropImage Crop and resize image. 3 | 4 | % Copyright 2019 The MathWorks, Inc. 5 | 6 | nBoxes = size(bboxes, 1); 7 | cropped = zeros(outSize, outSize, 3, nBoxes, 'like', im); 8 | for iBox = 1:nBoxes 9 | thisBox = bboxes(iBox, :); 10 | cropped(:,:,:,iBox) = imresize(imcrop(im, thisBox), [outSize, outSize]); 11 | end 12 | 13 | end -------------------------------------------------------------------------------- /code/mtcnn/+mtcnn/+util/applyCorrection.m: -------------------------------------------------------------------------------- 1 | function outBboxes = applyCorrection(bboxes, correction) 2 | % applyCorrection Apply bounding box regression correction. 3 | 4 | % Copyright 2019 The MathWorks, Inc. 5 | 6 | assert(all(bboxes(:, 3) == bboxes(:, 4)), ... 7 | "mtcnn:util:applyCorrection:badBox", ... 8 | "Correction assumes square bounding boxes."); 9 | 10 | scaledOffset = bboxes(:, 3).*correction; 11 | outBboxes = bboxes + scaledOffset; 12 | 13 | end -------------------------------------------------------------------------------- /code/mtcnn/+mtcnn/pnet.m: -------------------------------------------------------------------------------- 1 | function [probability, correction] = pnet(x, weights) 2 | % pnet Apply the pnet network to a batch of images. 3 | 4 | % Copyright 2019 The MathWorks, Inc. 5 | 6 | for layer = 1:3 7 | weightName = sprintf("features_conv%d_weight", layer); 8 | biasName = sprintf("features_conv%d_bias", layer); 9 | x = dlconv(x, ... 10 | weights.(weightName), ... 11 | weights.(biasName)); 12 | 13 | preluName = sprintf("features_prelu%d_weight", layer); 14 | x = mtcnn.util.prelu(x, weights.(preluName)); 15 | 16 | if layer == 1 17 | x = maxpool(x, 2, "Stride", 2); 18 | end 19 | end 20 | 21 | probability = dlconv(x, weights.conv4_1_weight, weights.conv4_1_bias); 22 | correction = dlconv(x, weights.conv4_2_weight, weights.conv4_2_bias); 23 | probability = softmax(probability); 24 | 25 | end -------------------------------------------------------------------------------- /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | version: 2.1 2 | orbs: 3 | matlab: mathworks/matlab@0.3.0 4 | codecov: codecov/codecov@1.0.2 5 | 6 | jobs: 7 | test: 8 | parameters: 9 | version: 10 | description: "MATLAB version" 11 | default: "R2020b" 12 | type: string 13 | machine: 14 | image: ubuntu-1604:201903-01 15 | steps: 16 | - checkout 17 | - matlab/install: 18 | release: <> 19 | - matlab/run-tests: 20 | test-results-junit: artifacts/junit/testResults.xml 21 | code-coverage-cobertura: artifacts/coverage/codeCoverage.xml 22 | source-folder: code 23 | - store_test_results: 24 | path: artifacts/junit 25 | - store_artifacts: 26 | path: artifacts 27 | - codecov/upload: 28 | file: artifacts/coverage/codeCoverage.xml 29 | 30 | workflows: 31 | matrix-tests: 32 | jobs: 33 | - test: 34 | matrix: 35 | parameters: 36 | version: 37 | - R2020a 38 | - R2020b 39 | - R2021a -------------------------------------------------------------------------------- /kpzhang93-MTCNN_face_detection_alignment.rights.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 Kaipeng Zhang 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /test/+tests/RegressionTest.m: -------------------------------------------------------------------------------- 1 | classdef RegressionTest < matlab.unittest.TestCase 2 | % Test cases for known bugs that have been fixed 3 | 4 | % Copyright 2020 The MathWorks, Inc. 5 | 6 | methods (Test) 7 | function testSelectStrongestBug(test) 8 | % GitHub issue #17 9 | im = imread("visionteam1.jpg"); 10 | 11 | [bboxes, scores, landmarks] = mtcnn.detectFaces(im, "ConfidenceThresholds", repmat(0.01, [3, 1])); 12 | for iBox = 1:size(bboxes, 1) 13 | test.assertInBox(landmarks(iBox, :, :), bboxes(iBox, :)); 14 | end 15 | end 16 | end 17 | 18 | methods 19 | function assertInBox(test, landmarks, box) 20 | % check that all landmarks are within the bounding box 21 | tf = all(inpolygon(landmarks(1, :, 1), ... 22 | landmarks(1, :, 2), ... 23 | [box(1), box(1) + box(3), box(1) + box(3), box(1)], ... 24 | [box(2), box(2), box(2) + box(4), box(2) + box(4)])); 25 | test.assertTrue(tf, "Landmarks should all be inside bounding box"); 26 | end 27 | end 28 | 29 | end -------------------------------------------------------------------------------- /test/+tests/DocTest: -------------------------------------------------------------------------------- 1 | classdef DocTest < matlab.unittest.TestCase 2 | 3 | methods(TestClassSetup) 4 | function addDocToPath(testCase) 5 | testCase.applyFixture(PathFixture(fullfile(mtcnnRoot,'doc'))); 6 | end 7 | end 8 | 9 | methods(TestMethodSetup) 10 | function captureAndCleanFigures(testCase) 11 | figs = findall(groot,'Type','figure'); 12 | testCase.addTeardown(@testCase.logAndClose, figs); 13 | end 14 | end 15 | methods(Access=private) 16 | function logAndClose(testCase, figs) 17 | openedFigs = setdiff(findall(groot,'Type','figure'),figs); 18 | for fig=openedFigs' 19 | testCase.log(1,FigureDiagnostic(fig)); 20 | end 21 | close(openedFigs); 22 | end 23 | end 24 | 25 | methods(Test) 26 | function executesWithoutError(~) 27 | GettingStarted; 28 | end 29 | end 30 | end 31 | 32 | function f = FigureDiagnostic(varargin) 33 | f = matlab.unittest.diagnostics.FigureDiagnostic(varargin{:}); 34 | end 35 | 36 | function f = PathFixture(varargin) 37 | f = matlab.unittest.fixtures.PathFixture(varargin{:}); 38 | end 39 | -------------------------------------------------------------------------------- /code/mtcnn/info.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 2019b 13 | 14 | MTCNN Face Detection 15 | 16 | 17 | toolbox 18 | 19 | 20 | 21 | 22 | doc 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /code/mtcnn/+mtcnn/+util/preluLayer.m: -------------------------------------------------------------------------------- 1 | classdef preluLayer < nnet.layer.Layer 2 | % Example custom PReLU layer. 3 | % Taken from "Define Custom Deep Learning Layer with Learnable 4 | % Parameters" 5 | 6 | % Copyright 2020 The MathWorks, Inc. 7 | 8 | properties (Learnable) 9 | % Scaling coefficient 10 | Alpha 11 | end 12 | 13 | methods 14 | function layer = preluLayer(weights, name) 15 | % layer = preluLayer(numChannels, name) creates a PReLU layer 16 | % for 2-D image input with numChannels channels and specifies 17 | % the layer name. 18 | 19 | layer.Name = name; 20 | layer.Alpha = weights; 21 | end 22 | 23 | function Z = predict(layer, X) 24 | % Z = predict(layer, X) forwards the input data X through the 25 | % layer and outputs the result Z. 26 | Z = max(X,0) + layer.Alpha .* min(0,X); 27 | end 28 | 29 | function [dLdX, dLdAlpha] = backward(layer, X, ~, dLdZ, ~) 30 | dLdX = layer.Alpha .* dLdZ; 31 | dLdX(X>0) = dLdZ(X>0); 32 | dLdAlpha = min(0,X) .* dLdZ; 33 | dLdAlpha = sum(sum(dLdAlpha,1),2); 34 | 35 | % Sum over all observations in mini-batch. 36 | dLdAlpha = sum(dLdAlpha,4); 37 | end 38 | end 39 | end -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2019, The MathWorks, Inc. 2 | All rights reserved. 3 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 4 | 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 5 | 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 6 | 3. In all cases, the software is, and all modifications and derivatives of the software shall be, licensed to you solely for use in conjunction with MathWorks products and service offerings. 7 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /code/mtcnn/+mtcnn/rnet.m: -------------------------------------------------------------------------------- 1 | function [probability, correction] = rnet(x, weights) 2 | % rnet Apply the rnet network to a batch of images. 3 | 4 | % Copyright 2019 The MathWorks, Inc. 5 | 6 | for layer = 1:3 7 | weightName = sprintf("features_conv%d_weight", layer); 8 | biasName = sprintf("features_conv%d_bias", layer); 9 | x = dlconv(x, ... 10 | weights.(weightName), ... 11 | weights.(biasName)); 12 | 13 | preluName = sprintf("features_prelu%d_weight", layer); 14 | x = mtcnn.util.prelu(x, weights.(preluName)); 15 | 16 | if layer == 1 || layer == 2 17 | if layer == 1 18 | % This padding is required to replicate the default padding 19 | % from the original Caffe implementation 20 | padding = [0, 0; 2, 2]; 21 | else 22 | padding = 0; 23 | end 24 | x = maxpool(x, 3, "Stride", 2, "Padding", padding); 25 | end 26 | end 27 | 28 | nBatch = size(x,4); 29 | % x = permute(stripdims(x), [4, 1, 2, 3]); 30 | % x = dlarray(reshape(x, nBatch, []), "BC"); 31 | 32 | x = fullyconnect(x, weights.features_conv4_weight, weights.features_conv4_bias); 33 | x = mtcnn.util.prelu(x, weights.features_prelu4_weight); 34 | probability = fullyconnect(x, weights.conv5_1_weight, weights.conv5_1_bias); 35 | correction = fullyconnect(x, weights.conv5_2_weight, weights.conv5_2_bias); 36 | probability = softmax(probability); 37 | end -------------------------------------------------------------------------------- /code/mtcnn/+mtcnn/+util/calculateScales.m: -------------------------------------------------------------------------------- 1 | function scales = calculateScales(im, minSize, maxSize, defaultScale, pyramidScale) 2 | % calculateScales Compute image scales for a given size range. 3 | % 4 | % Args: 5 | % im Input image 6 | % minSize Minimum pixel size for detection 7 | % maxSize Maximum pixel size for detection 8 | % defaultScale Pixel size of proposal network 9 | % pyramidScale Scale factor for image pyramid this must be 10 | % greater than 1 (optional, default = sqrt(2)) 11 | % 12 | % Returns: 13 | % scales 1xn row vector of calculated pyramid scales 14 | 15 | % Copyright 2019 The MathWorks, Inc. 16 | 17 | if nargin < 5 18 | pyramidScale = sqrt(2); 19 | else 20 | assert(pyramidScale > 1, ... 21 | "mtcnn:util:calculateScales:badScale", ... 22 | "PyramidScale must be >1 but it was %f", pyramidScale); 23 | end 24 | 25 | imSize = size(im); 26 | 27 | % if maxSize is empty assume the smallest image dimension 28 | if isempty(maxSize) 29 | maxSize = min(imSize(1:2)); 30 | end 31 | 32 | % Calculate min and max scaling factors required 33 | minScale = minSize/defaultScale; 34 | maxScale = maxSize/defaultScale; 35 | 36 | % Round to multiples or sqrt(2) 37 | alpha = floor(log(maxScale/minScale)/log(pyramidScale)); 38 | scales = minScale.*pyramidScale.^(0:alpha); 39 | end -------------------------------------------------------------------------------- /code/mtcnn/+mtcnn/proposeRegions.m: -------------------------------------------------------------------------------- 1 | function [bboxes, scores] = proposeRegions(im, scale, threshold, networkStrategy) 2 | % proposeRegions Generate region proposals at a given scale. 3 | % 4 | % Args: 5 | % im - Input image -1 to 1 range, type single 6 | % scale - Scale to run proposal at 7 | % threshold - Confidence threshold to accept proposal 8 | % weights - P-Net weights struct or trained network 9 | % 10 | % Returns: 11 | % bboxes - Nx4 array of bounding boxes 12 | % scores - Nx1 array of region scores 13 | 14 | % Copyright 2019 The MathWorks, Inc. 15 | 16 | 17 | % Stride of the proposal network 18 | stride = 2; 19 | % Field of view of the proposal network in pixels 20 | pnetSize = 12; 21 | 22 | im = imresize(im, 1/scale); 23 | 24 | [probability, correction] = networkStrategy.applyPNet(im); 25 | 26 | faces = probability(:,:,2) > threshold; 27 | if sum(faces, 'all') == 0 28 | bboxes = []; 29 | scores = []; 30 | return 31 | end 32 | 33 | linCoord = find(faces(:)); 34 | [iY, iX] = ind2sub(size(faces), linCoord); 35 | 36 | % Generate bounding boxes from positive locations 37 | bboxes = [scale*stride*(iX - 1) + 1, scale*stride*(iY - 1) + 1]; 38 | bboxes(:, 3:4) = scale*pnetSize; 39 | 40 | % Apply bounding box correction 41 | linCorrection = reshape(correction, [], 4); 42 | scaledOffset = scale*pnetSize*linCorrection(linCoord, :); 43 | bboxes = bboxes + scaledOffset; 44 | 45 | linProb = reshape(probability, [], 2); 46 | scores = linProb(linCoord, 2); 47 | 48 | end 49 | -------------------------------------------------------------------------------- /code/mtcnn/+mtcnn/onet.m: -------------------------------------------------------------------------------- 1 | function [probability, correction, landmarks] = onet(x, weights) 2 | % onet Apply the onet network to a batch of images. 3 | 4 | % Copyright 2019 The MathWorks, Inc. 5 | 6 | for layer = 1:4 7 | weightName = sprintf("features_conv%d_weight", layer); 8 | biasName = sprintf("features_conv%d_bias", layer); 9 | x = dlconv(x, ... 10 | weights.(weightName), ... 11 | weights.(biasName)); 12 | 13 | preluName = sprintf("features_prelu%d_weight", layer); 14 | x = mtcnn.util.prelu(x, weights.(preluName)); 15 | 16 | if layer < 4 17 | if layer == 3 18 | kernel = 2; 19 | else 20 | kernel = 3; 21 | end 22 | if layer == 1 23 | % This padding is required to replicate the default padding 24 | % from the original Caffe implementation 25 | padding = [0, 0; 2, 2]; 26 | else 27 | padding = 0; 28 | end 29 | x = maxpool(x, kernel, "Stride", 2, "Padding", padding); 30 | end 31 | end 32 | 33 | x = fullyconnect(x, weights.features_conv5_weight, weights.features_conv5_bias); 34 | % inference only so no dropout 35 | x = mtcnn.util.prelu(x, weights.features_prelu5_weight); 36 | 37 | probability = fullyconnect(x, weights.conv6_1_weight, weights.conv6_1_bias); 38 | correction = fullyconnect(x, weights.conv6_2_weight, weights.conv6_2_bias); 39 | landmarks = fullyconnect(x, weights.conv6_3_weight, weights.conv6_3_bias); 40 | probability = softmax(probability); 41 | end 42 | -------------------------------------------------------------------------------- /code/mtcnn/+mtcnn/+util/DagNetworkStrategy.m: -------------------------------------------------------------------------------- 1 | classdef DagNetworkStrategy < handle 2 | 3 | properties (SetAccess=private) 4 | % Trained Dag networks 5 | Pnet 6 | Rnet 7 | Onet 8 | ExecutionEnvironment 9 | end 10 | 11 | methods 12 | function obj = DagNetworkStrategy(useGpu) 13 | if useGpu 14 | obj.ExecutionEnvironment = "gpu"; 15 | else 16 | obj.ExecutionEnvironment = "cpu"; 17 | end 18 | end 19 | 20 | function load(obj) 21 | % loadWeights Load the network weights from file. 22 | obj.Pnet = importdata(fullfile(mtcnnRoot(), "weights", "dagPNet.mat")); 23 | obj.Rnet = importdata(fullfile(mtcnnRoot(), "weights", "dagRNet.mat")); 24 | obj.Onet = importdata(fullfile(mtcnnRoot(), "weights", "dagONet.mat")); 25 | end 26 | 27 | function [probability, correction] = applyPNet(obj, im) 28 | % need to use activations as we don't know what size it will be 29 | result = obj.Pnet.activations(im, "concat", ... 30 | "ExecutionEnvironment", obj.ExecutionEnvironment); 31 | 32 | probability = result(:,:,1:2,:); 33 | correction = result(:,:,3:end,:); 34 | end 35 | 36 | function [probs, correction] = applyRNet(obj, im) 37 | output = obj.Rnet.predict(im, "ExecutionEnvironment", obj.ExecutionEnvironment); 38 | 39 | probs = output(:,1:2); 40 | correction = output(:,3:end); 41 | end 42 | 43 | function [probs, correction, landmarks] = applyONet(obj, im) 44 | output = obj.Onet.predict(im, "ExecutionEnvironment", obj.ExecutionEnvironment); 45 | 46 | probs = output(:,1:2); 47 | correction = output(:,3:6); 48 | landmarks = output(:,7:end); 49 | end 50 | 51 | end 52 | end -------------------------------------------------------------------------------- /test/+tests/ProposeRegionsTest.m: -------------------------------------------------------------------------------- 1 | classdef ProposeRegionsTest < matlab.unittest.TestCase 2 | % Test propose regions function 3 | 4 | % Copyright 2020 The MathWorks, Inc. 5 | 6 | properties (Constant) 7 | Image = single(imread("visionteam.jpg"))/255*2 - 1 8 | end 9 | 10 | properties (TestParameter) 11 | getNet = struct("dl", @() mtcnn.util.DlNetworkStrategy(false) , ... 12 | "dag", @() mtcnn.util.DagNetworkStrategy(false)); 13 | end 14 | 15 | methods (Test) 16 | function testOutputs(test, getNet) 17 | scale = 2; 18 | conf = 0.5; 19 | strategy = getNet(); 20 | strategy.load(); 21 | 22 | [box, score] = mtcnn.proposeRegions(test.Image, scale, conf, strategy); 23 | 24 | test.verifyOutputs(box, score); 25 | end 26 | 27 | function test1DActivations(test, getNet) 28 | % Test for bug #6 (1xn activations causes proposeRegions to 29 | % fail) 30 | cropped = imcrop(test.Image, [300, 42, 65, 38]); 31 | scale = 3; 32 | conf = 0.5; 33 | strategy = getNet(); 34 | strategy.load(); 35 | 36 | [box, score] = mtcnn.proposeRegions(cropped, scale, conf, strategy); 37 | 38 | test.verifyOutputs(box, score); 39 | end 40 | end 41 | 42 | methods 43 | function verifyOutputs(test, box, score) 44 | % helper to check expected outputs of proposeRegions 45 | test.verifyEqual(size(box, 2), 4, ... 46 | "first output should be nx4 bounding box list"); 47 | test.verifyEqual(size(score, 2), 1, ... 48 | "second output should be nx1 face probabilities"); 49 | test.verifyEqual(size(box, 1), size(score, 1), ... 50 | "should be 1 box for each probability"); 51 | end 52 | end 53 | 54 | end 55 | -------------------------------------------------------------------------------- /test/+tests/PerformanceTest.m: -------------------------------------------------------------------------------- 1 | classdef PerformanceTest < matlab.perftest.TestCase 2 | % Test the run-time performance of face detection. 3 | 4 | % Copyright 2019 The MathWorks, Inc. 5 | 6 | properties 7 | Image 8 | Reference 9 | end 10 | 11 | properties (TestParameter) 12 | imSize = struct('small', 0.5, 'med', 1, 'large', 2); 13 | imFaces = {'few', 'many'}; 14 | end 15 | 16 | methods (TestClassSetup) 17 | function setupTestImage(test) 18 | test.Image = imread("visionteam.jpg"); 19 | end 20 | 21 | function loadReference(test) 22 | test.Reference = load(fullfile(mtcnnTestRoot(), ... 23 | "resources", ... 24 | "ref.mat")); 25 | end 26 | end 27 | 28 | methods (Test) 29 | function testDefaultDetect(test, imSize, imFaces) 30 | 31 | switch imFaces 32 | case 'few' 33 | im = test.Image; 34 | case 'many' 35 | im = repmat(test.Image(25:125, :, :), [4, 1]); 36 | end 37 | im = imresize(im, imSize); 38 | 39 | test.startMeasuring(); 40 | [bboxes, scores, landmarks] = mtcnn.detectFaces(im); 41 | test.stopMeasuring(); 42 | end 43 | 44 | function testLowLevelDetect(test, imSize, imFaces) 45 | 46 | switch imFaces 47 | case 'few' 48 | im = test.Image; 49 | case 'many' 50 | im = repmat(test.Image(25:125, :, :), [4, 1]); 51 | end 52 | im = imresize(im, imSize); 53 | 54 | detector = mtcnn.Detector(); 55 | 56 | test.startMeasuring(); 57 | [bboxes, scores, landmarks] = detector.detect(im); 58 | test.stopMeasuring(); 59 | end 60 | end 61 | 62 | end -------------------------------------------------------------------------------- /code/mtcnn/+mtcnn/detectFaces.m: -------------------------------------------------------------------------------- 1 | function [bboxes, scores, landmarks] = detectFaces(im, varargin) 2 | % detectFaces Use a pretrained model to detect faces in an image. 3 | % 4 | % Args: 5 | % im - RGB input image for detection 6 | % 7 | % Returns: 8 | % bbox - nx4 array of face bounding boxes in the 9 | % format [x, y, w, h] 10 | % scores - nx1 array of face probabilities 11 | % landmarks - nx5x2 array of facial landmarks 12 | % 13 | % Name-Value pairs: 14 | % detectFaces also takes the following optional Name-Value pairs: 15 | % - MinSize - Approx. min size in pixels 16 | % (default=24) 17 | % - MaxSize - Approx. max size in pixels 18 | % (default=[]) 19 | % - PyramidScale - Pyramid scales for region proposal 20 | % (default=sqrt(2)) 21 | % - ConfidenceThresholds - Confidence threshold at each stage of detection 22 | % (default=[0.6, 0.7, 0.8]) 23 | % - NmsThresholds - Non-max suppression overlap thresholds 24 | % (default=[0.5, 0.5, 0.5]) 25 | % - UseGPU - Use GPU for processing or not 26 | % (default=false) 27 | % - UseDagNet - Use DAGNetwork for prediction (for 28 | % compatibility with R2019a) 29 | % (default=false R2019b+, =true R2019a) 30 | % 31 | % Note: 32 | % The 5 landmarks detector are in the order: 33 | % - Left eye, right eye, nose, left mouth corner, right mouth corner 34 | % The final 2 dimensions correspond to x and y co-ords. 35 | % 36 | % See also: mtcnn.Detector.detect 37 | 38 | % Copyright 2019 The MathWorks, Inc. 39 | 40 | detector = mtcnn.Detector(varargin{:}); 41 | [bboxes, scores, landmarks] = detector.detect(im); 42 | end -------------------------------------------------------------------------------- /test/+tests/DetectFacesTest.m: -------------------------------------------------------------------------------- 1 | classdef DetectFacesTest < matlab.unittest.TestCase 2 | % Test the high-level api for detecting faces. 3 | 4 | % Copyright 2019 The MathWorks, Inc. 5 | 6 | properties 7 | Image 8 | Reference 9 | end 10 | 11 | methods (TestClassSetup) 12 | function setupTestImage(test) 13 | test.Image = imread("visionteam.jpg"); 14 | end 15 | 16 | function loadReference(test) 17 | test.Reference = load(fullfile(mtcnnTestRoot(), ... 18 | "resources", ... 19 | "ref.mat")); 20 | end 21 | end 22 | 23 | methods (Test) 24 | function testDefaultDetect(test) 25 | [bboxes, scores, landmarks] = mtcnn.detectFaces(test.Image); 26 | 27 | test.assertEqual(size(bboxes), [6, 4]); 28 | test.assertEqual(size(scores), [6, 1]); 29 | test.assertEqual(size(landmarks), [6, 5, 2]); 30 | 31 | test.assertEqual(bboxes, test.Reference.bboxes, "RelTol", 1e-6); 32 | test.assertEqual(scores, test.Reference.scores, "RelTol", 1e-6); 33 | test.assertEqual(landmarks, test.Reference.landmarks, "RelTol", 1e-6); 34 | end 35 | 36 | function testDetectWithOptions(test) 37 | % We should be able to pass name value paris to detect faces 38 | opts = {"MinSize", 20, ... 39 | "MaxSize", 100, ... 40 | "PyramidScale", 1.5, ... 41 | "ConfidenceThresholds", [0.6, 0.6, 0.6], ... 42 | "NmsThresholds", [0.6, 0.6, 0.6]}; 43 | 44 | [bboxes, scores, landmarks] = mtcnn.detectFaces(test.Image, opts{:}); 45 | 46 | test.assertEqual(size(bboxes), [6, 4]); 47 | test.assertEqual(size(scores), [6, 1]); 48 | test.assertEqual(size(landmarks), [6, 5, 2]); 49 | end 50 | end 51 | 52 | end -------------------------------------------------------------------------------- /code/mtcnn/+mtcnn/+util/DlNetworkStrategy.m: -------------------------------------------------------------------------------- 1 | classdef DlNetworkStrategy < handle 2 | 3 | properties (SetAccess=private) 4 | UseGPU 5 | % Weights for the networks 6 | PnetWeights 7 | RnetWeights 8 | OnetWeights 9 | end 10 | 11 | methods 12 | function obj = DlNetworkStrategy(useGpu) 13 | obj.UseGPU = useGpu; 14 | end 15 | 16 | function load(obj) 17 | % loadWeights Load the network weights from file. 18 | obj.PnetWeights = load(fullfile(mtcnnRoot(), "weights", "pnet.mat")); 19 | obj.RnetWeights = load(fullfile(mtcnnRoot(), "weights", "rnet.mat")); 20 | obj.OnetWeights = load(fullfile(mtcnnRoot(), "weights", "onet.mat")); 21 | 22 | if obj.UseGPU 23 | obj.PnetWeights = dlupdate(@gpuArray, obj.PnetWeights); 24 | obj.RnetWeights = dlupdate(@gpuArray, obj.RnetWeights); 25 | obj.OnetWeights = dlupdate(@gpuArray, obj.OnetWeights); 26 | end 27 | end 28 | 29 | function [probability, correction] = applyPNet(obj, im) 30 | im = dlarray(im, "SSCB"); 31 | 32 | [probability, correction] = mtcnn.pnet(im, obj.PnetWeights); 33 | 34 | probability = extractdata(gather(probability)); 35 | correction = extractdata(gather(correction)); 36 | end 37 | 38 | function [probs, correction] = applyRNet(obj, im) 39 | im = dlarray(im, "SSCB"); 40 | 41 | [probs, correction] = mtcnn.rnet(im, obj.RnetWeights); 42 | 43 | probs = extractdata(probs)'; 44 | correction = extractdata(correction)'; 45 | end 46 | 47 | function [probs, correction, landmarks] = applyONet(obj, im) 48 | im = dlarray(im, "SSCB"); 49 | 50 | [probs, correction, landmarks] = mtcnn.onet(im, obj.OnetWeights); 51 | 52 | probs = extractdata(probs)'; 53 | correction = extractdata(correction)'; 54 | landmarks = extractdata(landmarks)'; 55 | end 56 | 57 | end 58 | end -------------------------------------------------------------------------------- /doc/getting_started.md: -------------------------------------------------------------------------------- 1 | ### Getting started 2 | 3 | This example shows how to run face detection and alignment on an image. 4 | 5 | ### Face detection 6 | 7 | The simplest way to use the face detector is the function 8 | mtcnn.detectFaces this takes in an image and returns bounding boxes, 9 | probability scores, and facial landmarks for each face detected. 10 | 11 | ``` matlab 12 | im = imread("visionteam1.jpg"); 13 | [bboxes, scores, landmarks] = mtcnn.detectFaces(im); 14 | fprintf("Found %d faces.\n", numel(scores)); 15 | ``` 16 | 17 | ``` output 18 | Found 6 faces. 19 | ``` 20 | 21 | To visualise the results we can superimpose the bounding boxes and 22 | landmarks on the original 23 | image. 24 | 25 | ``` matlab 26 | displayIm = insertObjectAnnotation(im, "rectangle", bboxes, scores, "LineWidth", 2); 27 | imshow(displayIm) 28 | hold on 29 | for iFace = 1:numel(scores) 30 | scatter(landmarks(iFace, :, 1), landmarks(iFace, :, 2), 'filled'); 31 | end 32 | ``` 33 | 34 | ![](getting_started_01.png) 35 | 36 | ### Facial landmarks 37 | 38 | Landmarks detected are returned in the following order: 39 | 40 | 1. Left eye 41 | 2. Right eye 42 | 3. Nose 43 | 4. Left mouth corner 44 | 5. Right mouth corner 45 | 46 | Where left and right are as observed by camera. 47 | 48 | ``` matlab 49 | figure(); 50 | imshow(im); 51 | xlim([95, 150]) 52 | ylim([115, 185]) 53 | hold on 54 | scatter(landmarks(2, :, 1), landmarks(2, :, 2), 100, "r", "filled"); 55 | text(landmarks(2, :, 1)+3, landmarks(2, :, 2)-1, string(1:5), "FontSize", 25); 56 | ``` 57 | 58 | ![](getting_started_02.png) 59 | 60 | ### Detection speed 61 | 62 | For best detection speed you should manually create a detector object 63 | using mtcnn.Detector, then call its detect method. This will ensure that 64 | network parameters are loaded before the actual detection occurs. 65 | 66 | ``` matlab 67 | detector = mtcnn.Detector(); 68 | tic(); 69 | [~, scores, ~] = detector.detect(im); 70 | timeTaken = toc(); 71 | fprintf("Found %d faces in %.3f seconds.\n", numel(scores), timeTaken); 72 | ``` 73 | 74 | ``` output 75 | Found 6 faces in 0.544 seconds. 76 | ``` 77 | 78 | If you know the expected scale of your face there are also several 79 | Name-Value optional arguments which can be used to control the details 80 | of face 81 | detection 82 | 83 | ``` matlab 84 | detector = mtcnn.Detector("MinSize", 24, "MaxSize", 48, "PyramidScale", 2); 85 | tic(); 86 | [~, scores, ~] = detector.detect(im); 87 | timeTaken = toc(); 88 | fprintf("Found %d faces in %.3f seconds.\n", numel(scores), timeTaken); 89 | ``` 90 | 91 | ``` output 92 | Found 6 faces in 0.209 seconds. 93 | ``` 94 | 95 | If you have a GPU on your machine, you might see further perfromance 96 | improvments by setting "UseGPU" to true. For more details of these 97 | parameters type help mtcnn.detectFaces. 98 | 99 | _Copyright 2019 The MathWorks, Inc._ -------------------------------------------------------------------------------- /test/+tests/UtilTest.m: -------------------------------------------------------------------------------- 1 | classdef UtilTest < matlab.unittest.TestCase 2 | % Test helper functions in the util package. 3 | 4 | % Copyright 2019 The MathWorks, Inc. 5 | 6 | methods (Test) 7 | function testCalcScales(test) 8 | im = zeros(50, 50, 3); 9 | defaultScale = 12; 10 | minSize = 12; 11 | maxSize = []; 12 | expectedScales = [1, sqrt(2), 2, 2*sqrt(2), 4]; 13 | 14 | scales = mtcnn.util.calculateScales(im, minSize, maxSize, defaultScale); 15 | 16 | test.verifyEqual(scales, expectedScales, "RelTol", 1e-10); 17 | end 18 | 19 | function testCalcScalesSetMax(test) 20 | im = zeros(50, 50, 3); 21 | defaultScale = 12; 22 | minSize = 12; 23 | maxSize = 34; 24 | expectedScales = [1, sqrt(2), 2, 2*sqrt(2)]; 25 | 26 | scales = mtcnn.util.calculateScales(im, minSize, maxSize, defaultScale); 27 | 28 | test.verifyEqual(scales, expectedScales, "RelTol", 1e-10); 29 | end 30 | 31 | function testCalcScalesSetMin(test) 32 | im = zeros(50, 50, 3); 33 | defaultScale = 12; 34 | minSize = 24; 35 | maxSize = 34; 36 | expectedScales = [2, 2*sqrt(2)]; 37 | 38 | scales = mtcnn.util.calculateScales(im, minSize, maxSize, defaultScale); 39 | 40 | test.verifyEqual(scales, expectedScales, "RelTol", 1e-10); 41 | end 42 | 43 | function testCalcScalesPyramidScale(test) 44 | im = zeros(50, 50, 3); 45 | defaultScale = 12; 46 | minSize = 12; 47 | maxSize = []; 48 | pyramidScale = 2; 49 | expectedScales = [1, 2, 4]; 50 | 51 | scales = mtcnn.util.calculateScales(im, minSize, maxSize, ... 52 | defaultScale, pyramidScale); 53 | 54 | test.verifyEqual(scales, expectedScales, "RelTol", 1e-10); 55 | end 56 | 57 | function testCalcScalesPyramidScaleLessThanOne(test) 58 | im = zeros(50, 50, 3); 59 | defaultScale = 12; 60 | minSize = 12; 61 | maxSize = []; 62 | pyramidScale = 0.1; 63 | 64 | calcScales = @() mtcnn.util.calculateScales(im, minSize, maxSize, ... 65 | defaultScale, pyramidScale); 66 | 67 | test.verifyError(calcScales, "mtcnn:util:calculateScales:badScale") 68 | end 69 | 70 | function testMakeSquare(test) 71 | bbox = [1, 2, 3, 4]; 72 | expectedBox = [0.5, 2, 4, 4]; 73 | 74 | resizedBox = mtcnn.util.makeSquare(bbox); 75 | 76 | test.verifyEqual(resizedBox, expectedBox); 77 | end 78 | 79 | function testApplyCorrection(test) 80 | bbox = [1, 2, 3, 3]; 81 | correction = [0, -0.5, 0.5, 1]; 82 | expected = [1, 0.5, 4.5, 6]; 83 | 84 | corrected = mtcnn.util.applyCorrection(bbox, correction); 85 | 86 | test.verifyEqual(corrected, expected); 87 | end 88 | end 89 | end -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Face Detection and Alignment MTCNN 2 | 3 | ![circleci](https://circleci.com/gh/matlab-deep-learning/mtcnn-face-detection.svg?style=svg) 4 | [![codecov](https://codecov.io/gh/matlab-deep-learning/mtcnn-face-detection/branch/master/graph/badge.svg)](https://codecov.io/gh/matlab-deep-learning/mtcnn-face-detection) 5 | 6 | ## [__Download the toolbox here__](https://github.com/matlab-deep-learning/mtcnn-face-detection/releases/latest/download/MTCNN-Face-Detection.mltbx) 7 | 8 | This repository implements a deep-learning based face detection and facial landmark localization model using [multi-task cascaded convolutional neural networks (MTCNNs)](https://kpzhang93.github.io/MTCNN_face_detection_alignment/). 9 | 10 | - [📦 Installation](#installation) 11 | - [🏁 Getting Started](#getting-started) 12 | - [🔎😄 Usage](#usage) 13 | - [❓ About](#about) 14 | - [💬 Contribute](#contribute) 15 | 16 | _Note: This code supports inference using a pretrained model. Training from scratch is not supported. Weights are imported from the [original MTCNN model](https://kpzhang93.github.io/MTCNN_face_detection_alignment/) trained in Caffe._ 17 | 18 | ## Installation 19 | 20 | - Face Detection and Alignment MTCNN requires the following products: 21 | - MATLAB R2019a or later (_now works in R2019a and later!_) 22 | - Deep Learning Toolbox 23 | - Computer Vision Toolbox 24 | - Image Processing Toolbox 25 | - Download the [latest release](https://github.com/matlab-deep-learning/mtcnn-face-detection/releases/) of the Face Detection and Aligment MTCNN. To install, open the .mltbx file in MATLAB. 26 | 27 | ## Getting Started 28 | 29 | To get started using the pretrained face detector, import an image and use the `mtcnn.detectFaces` function: 30 | 31 | ```matlab 32 | im = imread("visionteam.jpg"); 33 | [bboxes, scores, landmarks] = mtcnn.detectFaces(im); 34 | ``` 35 | 36 | This returns the bounding boxes, probabilities, and five-point facial landmarks for each face detected in the image. 37 | 38 | ![](doc/output1.jpg) 39 | 40 | ## Usage 41 | 42 | The `detectFaces` function supports various optional arguments. For more details, refer to the help documentation for this function by typing `help mtcnn.detectFaces` at the command window. 43 | 44 | To get the best speed performance from the detector, first create a `mtcnn.Detector` object, then call its `detect` method on your image. Doing so ensures that the pretrained weights and options are loaded before calling detect: 45 | 46 | ```matlab 47 | detector = mtcnn.Detector(); 48 | [bboxes, scores, landmarks] = detector.detect(im); 49 | ``` 50 | 51 | The detector object accepts the same optional arguments as the `mtcnn.detectFaces` function. 52 | 53 | Refer to the MATLAB toolbox documentation or [click here](docs/getting_started.md) for a complete example. 54 | 55 | ## About 56 | 57 | The MTCNN face detector is fast and accurate. Evaluation on the [WIDER face benchmark](http://mmlab.ie.cuhk.edu.hk/projects/WIDERFace/WiderFace_Results.html) shows significant performance gains over non-deep learning face detection methods. Prediction speed depends on the image, dimensions, pyramid scales, and hardware (i.e. CPU or GPU). On a typical CPU, for VGA resolution images, a frame rates ~10 fps should be achievable. 58 | 59 | In comparisson to MATLAB's built in `vision.CascadeObjectDetector` the MTCNN detector is more robust to facial pose as demonstrated in the image below. 60 | 61 | ![](doc/output2.jpg) 62 | 63 | _Face detection from MTCNN in yellow, detections from the built in vision.CascadeObjectDetector in teal._ 64 | 65 | 66 | ## Contribute 67 | 68 | Please file any bug reports or feature requests as [GitHub issues](https://github.com/matlab-deep-learning/mtcnn-face-detection/issues). In particular if you'd be interested in training your own MTCNN network comment on the following issue: [Support training MTCNN](https://github.com/matlab-deep-learning/mtcnn-face-detection/issues/1) 69 | 70 | _Copyright 2019 The MathWorks, Inc._ 71 | -------------------------------------------------------------------------------- /code/mtcnn/+mtcnn/+util/convertToDagNet.m: -------------------------------------------------------------------------------- 1 | function net = convertToDagNet(stage) 2 | 3 | warnId = "deep:functionToLayerGraph:Placeholder"; 4 | warnState = warning('off', warnId); 5 | restoreWarn = onCleanup(@() warning(warnState)); 6 | 7 | switch stage 8 | case "p" 9 | inputSize = 12; 10 | nBlocks = 3; 11 | finalConnections = [sprintf("conv_%d", nBlocks), sprintf("prelu_%d", nBlocks)]; 12 | catConnections = ["sm_1", "conv_5"]; 13 | case "r" 14 | inputSize = 24; 15 | nBlocks = 4; 16 | finalConnections = ["fc_1", sprintf("prelu_%d", nBlocks)]; 17 | catConnections = ["sm_1", "fc_3"]; 18 | case "o" 19 | inputSize = 48; 20 | nBlocks = 5; 21 | finalConnections = ["fc_1", sprintf("prelu_%d", nBlocks)]; 22 | catConnections = ["sm_1", "fc_3", "fc_4"]; 23 | otherwise 24 | error("mtcnn:convertToDagNet:unknownStage", ... 25 | "Stage '%s' is not recognised", stage) 26 | end 27 | 28 | matFilename = strcat(stage, "net.mat"); 29 | weightsFile = load(fullfile(mtcnnRoot, "weights", matFilename)); 30 | input = dlarray(zeros(inputSize, inputSize, 3, "single"), "SSCB"); 31 | 32 | switch stage 33 | case "p" 34 | netFunc = @(x) mtcnn.pnet(x, weightsFile); 35 | [a, b] = netFunc(input); 36 | output = cat(3, a, b); 37 | case "r" 38 | netFunc = @(x) mtcnn.rnet(x, weightsFile); 39 | [a, b] = netFunc(input); 40 | output = cat(1, a, b); 41 | case "o" 42 | netFunc = @(x) mtcnn.onet(x, weightsFile); 43 | [a, b, c] = netFunc(input); 44 | output = cat(1, a, b, c); 45 | end 46 | 47 | if verLessThan('matlab', '9.9') 48 | lgraph = functionToLayerGraph(netFunc, input); 49 | else 50 | lgraph = functionToLayerGraph(netFunc, input, "GenerateLayer", "placeholder-layer"); 51 | end 52 | placeholders = findPlaceholderLayers(lgraph); 53 | lgraph = removeLayers(lgraph, {placeholders.Name}); 54 | 55 | for iPrelu = 1:nBlocks 56 | name = sprintf("prelu_%d", iPrelu); 57 | weightName = sprintf("features_prelu%d_weight", iPrelu); 58 | if iPrelu ~= nBlocks 59 | weights = weightsFile.(weightName); 60 | else 61 | weights = reshape(weightsFile.(weightName), 1, 1, []); 62 | end 63 | prelu = mtcnn.util.preluLayer(weights, name); 64 | lgraph = replaceLayer(lgraph, sprintf("plus_%d", iPrelu), prelu, "ReconnectBy", "order"); 65 | 66 | if iPrelu ~= nBlocks 67 | lgraph = connectLayers(lgraph, sprintf("conv_%d", iPrelu), sprintf("prelu_%d", iPrelu)); 68 | else 69 | % need to make different connections at the end of the 70 | % repeating blocks 71 | for iConnection = 1:size(finalConnections, 1) 72 | lgraph = connectLayers(lgraph, ... 73 | finalConnections(iConnection, 1), ... 74 | finalConnections(iConnection, 2)); 75 | end 76 | 77 | end 78 | end 79 | 80 | lgraph = addLayers(lgraph, imageInputLayer([inputSize, inputSize, 3], ... 81 | "Name", "input", ... 82 | "Normalization", "none")); 83 | lgraph = connectLayers(lgraph, "input", "conv_1"); 84 | 85 | lgraph = addLayers(lgraph, concatenationLayer(3, numel(catConnections), "Name", "concat")); 86 | for iConnection = 1:numel(catConnections) 87 | lgraph = connectLayers(lgraph, ... 88 | catConnections(iConnection), ... 89 | sprintf("concat/in%d", iConnection)); 90 | end 91 | lgraph = addLayers(lgraph, regressionLayer("Name", "output")); 92 | lgraph = connectLayers(lgraph, "concat", "output"); 93 | 94 | net = assembleNetwork(lgraph); 95 | result = net.predict(zeros(inputSize, inputSize, 3, "single")); 96 | 97 | difference = extractdata(sum(output - result', "all")); 98 | 99 | assert(difference < 1e-6, ... 100 | "mtcnn:convertToDagNet:outputMismatch", ... 101 | "Outputs of function and dag net do not match") 102 | end -------------------------------------------------------------------------------- /test/+tests/DetectorTest.m: -------------------------------------------------------------------------------- 1 | classdef DetectorTest < matlab.unittest.TestCase 2 | % Test cases for Detector class. 3 | 4 | % Copyright 2019 The MathWorks, Inc. 5 | 6 | properties 7 | Image 8 | Reference 9 | end 10 | 11 | properties (TestParameter) 12 | imageTypeConversion = struct("uint8", @(x) x, ... 13 | "single", @(x) single(x)/255, ... 14 | "double", @(x) double(x)/255) 15 | useDagNet = {false, true} 16 | end 17 | 18 | methods (TestClassSetup) 19 | function setupTestImage(test) 20 | test.Image = imread("visionteam.jpg"); 21 | end 22 | 23 | function loadReference(test) 24 | test.Reference = load(fullfile(mtcnnTestRoot(), ... 25 | "resources", ... 26 | "ref.mat")); 27 | end 28 | end 29 | 30 | methods (Test) 31 | 32 | function testCreate(test) 33 | detector = mtcnn.Detector(); 34 | end 35 | 36 | function testDetectwithDefaults(test, imageTypeConversion, useDagNet) 37 | % Test expected inputs with images of type uint8, single, 38 | % double (float images are scaled 0-1); 39 | detector = mtcnn.Detector("UseDagNet", useDagNet); 40 | 41 | inputImage = imageTypeConversion(test.Image); 42 | 43 | [bboxes, scores, landmarks] = detector.detect(inputImage); 44 | 45 | test.verifyEqual(size(bboxes), [6, 4]); 46 | test.verifyEqual(size(scores), [6, 1]); 47 | test.verifyEqual(size(landmarks), [6, 5, 2]); 48 | 49 | test.verifyEqual(bboxes, test.Reference.bboxes, "RelTol", 1e-6); 50 | test.verifyEqual(scores, test.Reference.scores, "RelTol", 1e-6); 51 | test.verifyEqual(landmarks, test.Reference.landmarks, "RelTol", 1e-6); 52 | end 53 | 54 | %% Pyramid parameters 55 | function testMinSize(test) 56 | detector = mtcnn.Detector("MinSize", 240); 57 | [bboxes, scores, landmarks] = detector.detect(test.Image); 58 | 59 | test.verifyEmpty(bboxes); 60 | test.verifyEmpty(scores); 61 | test.verifyEmpty(landmarks); 62 | end 63 | 64 | function testMinMinSize(test) 65 | createDetector = @() mtcnn.Detector("MinSize", 1); 66 | 67 | test.verifyError(createDetector, "MATLAB:validators:mustBeGreaterThan") 68 | end 69 | 70 | function testMaxSize(test) 71 | detector = mtcnn.Detector("MaxSize", 12); 72 | [bboxes, scores, landmarks] = detector.detect(test.Image); 73 | 74 | test.verifyEmpty(bboxes); 75 | test.verifyEmpty(scores); 76 | test.verifyEmpty(landmarks); 77 | end 78 | 79 | function testDetectwithMoreScales(test) 80 | detector = mtcnn.Detector("PyramidScale", sqrt(1.5)); 81 | 82 | [bboxes, scores, landmarks] = detector.detect(test.Image); 83 | 84 | test.verifyEqual(size(bboxes), [6, 4]); 85 | test.verifyEqual(size(scores), [6, 1]); 86 | test.verifyEqual(size(landmarks), [6, 5, 2]); 87 | 88 | boxOverlaps = bboxOverlapRatio(bboxes, test.Reference.bboxes); 89 | test.verifyEqual(max(boxOverlaps) > 0.8, true(1, 6)); 90 | test.verifyEqual(scores, test.Reference.scores, "RelTol", 1e-3); 91 | end 92 | 93 | %% Thresholds 94 | function testConfThresholds(test) 95 | detector = mtcnn.Detector("ConfidenceThresholds", [0.8, 0.8, 0.9]); 96 | 97 | [bboxes, scores, landmarks] = detector.detect(test.Image); 98 | 99 | test.verifyNotEmpty(bboxes); 100 | test.verifyNotEmpty(scores); 101 | test.verifyNotEmpty(landmarks); 102 | end 103 | 104 | function testNmsThresholds(test) 105 | detector = mtcnn.Detector("NmsThresholds", [0.4, 0.4, 0.4]); 106 | 107 | [bboxes, scores, landmarks] = detector.detect(test.Image); 108 | 109 | test.verifyNotEmpty(bboxes); 110 | test.verifyNotEmpty(scores); 111 | test.verifyNotEmpty(landmarks); 112 | end 113 | 114 | %% GPU 115 | function testGpuDetect(test, imageTypeConversion, useDagNet) 116 | 117 | % filter if no GPU present 118 | test.assumeGreaterThan(gpuDeviceCount, 0, "This test is only valid on machines with at least one GPU"); 119 | 120 | inputImage = imageTypeConversion(test.Image); 121 | detector = mtcnn.Detector("UseGPU", true, "UseDagNet", useDagNet); 122 | [bboxes, scores, landmarks] = detector.detect(inputImage); 123 | 124 | test.verifyEqual(size(bboxes), [6, 4]); 125 | test.verifyEqual(size(scores), [6, 1]); 126 | test.verifyEqual(size(landmarks), [6, 5, 2]); 127 | 128 | % Reference was taken on the CPU so increase relative tolerance 129 | test.verifyEqual(bboxes, test.Reference.bboxes, "RelTol", 1e-1); 130 | test.verifyEqual(scores, test.Reference.scores, "RelTol", 1e-1); 131 | test.verifyEqual(landmarks, test.Reference.landmarks, "RelTol", 1e-1); 132 | end 133 | end 134 | end 135 | -------------------------------------------------------------------------------- /mtcnn.prj: -------------------------------------------------------------------------------- 1 | 2 | 3 | MTCNN Face Detection 4 | Justin Pinkney 5 | jpinkney@mathworks.com 6 | MathWorks 7 | Face detection and landmark localisation using deep learning. 8 | This repository implements deep learning based face detection and facial landmark localisation using Multi-Task Cascaded CNNs. 9 | For more details see the GitHub repository: https://github.com/matlab-deep-learning/mtcnn-face-detection 10 | ${PROJECT_ROOT}\doc\logo.png 11 | 1.2.2 12 | ${PROJECT_ROOT}\MTCNN Face Detection.mltbx 13 | 14 | Computer Vision Toolbox 15 | Deep Learning Toolbox 16 | Image Processing Toolbox 17 | 18 | 19 | 96 20 | 12 21 | 17 22 | 23 | 24 | 9.2 25 | 14.0 26 | 11.1 27 | 28 | 29 | d2cc38da-9b3d-4307-8cfd-4a34d5a442bf 30 | 31 | true 32 | 33 | 34 | 35 | 36 | ${PROJECT_ROOT}\code\mtcnn\info.xml 37 | ${PROJECT_ROOT}\code\mtcnn\doc\GettingStarted.mlx 38 | 39 | 40 | false 41 | 42 | 43 | 44 | R2019a 45 | latest 46 | false 47 | true 48 | true 49 | true 50 | true 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | ${PROJECT_ROOT}\code\mtcnn 86 | 87 | 88 | ${PROJECT_ROOT}\code\mtcnn\+mtcnn 89 | ${PROJECT_ROOT}\code\mtcnn\Contents.m 90 | ${PROJECT_ROOT}\code\mtcnn\doc 91 | ${PROJECT_ROOT}\code\mtcnn\info.xml 92 | ${PROJECT_ROOT}\code\mtcnn\mtcnnRoot.m 93 | ${PROJECT_ROOT}\code\mtcnn\weights 94 | 95 | 96 | 97 | 98 | 99 | C:\code\internal\mtcnn-face-detection\MTCNN Face Detection.mltbx 100 | 101 | 102 | 103 | C:\Program Files\MATLAB\R2020a 104 | 105 | 106 | 107 | 108 | 109 | true 110 | 111 | 112 | 113 | 114 | false 115 | false 116 | true 117 | false 118 | false 119 | false 120 | false 121 | false 122 | 10.0 123 | false 124 | true 125 | win64 126 | true 127 | 128 | 129 | -------------------------------------------------------------------------------- /doc/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 21 | 29 | 33 | 34 | 35 | 53 | 58 | 59 | 61 | 62 | 64 | image/svg+xml 65 | 67 | 68 | 69 | 70 | 71 | 76 | 86 | 100 | 114 | 123 | 132 | 138 | 151 | 164 | 177 | 178 | 179 | 180 | -------------------------------------------------------------------------------- /code/mtcnn/+mtcnn/Detector.m: -------------------------------------------------------------------------------- 1 | classdef Detector < matlab.mixin.SetGet 2 | % MTCNN Detector class 3 | % 4 | % When creating an mtcnn.Detector object 5 | % pass in any of the public properties as Name-Value pairs to 6 | % configure the detector. For more details of the available 7 | % options see the help for mtcnn.detectFaces. 8 | % 9 | % See also: mtcnn.detectFaces 10 | 11 | % Copyright 2019 The MathWorks, Inc. 12 | 13 | properties 14 | % Approx. min size in pixels 15 | MinSize {mustBeGreaterThan(MinSize, 12)} = 24 16 | % Approx. max size in pixels 17 | MaxSize = [] 18 | % Pyramid scales for region proposal 19 | PyramidScale = sqrt(2) 20 | % Confidence threshold at each stage of detection 21 | ConfidenceThresholds = [0.6, 0.7, 0.8] 22 | % Non-max suppression overlap thresholds 23 | NmsThresholds = [0.5, 0.5, 0.5] 24 | % Use GPU for processing or not 25 | UseGPU = false 26 | % Use DAG Network for pre 19b compatibility 27 | UseDagNet = verLessThan('matlab', '9.7') 28 | end 29 | 30 | properties (Access=private) 31 | % An object providing an inteface to the networks 32 | Networks 33 | end 34 | 35 | properties (Constant) 36 | % Input sizes (pixels) of the networks 37 | PnetSize = 12 38 | RnetSize = 24 39 | OnetSize = 48 40 | end 41 | 42 | 43 | methods 44 | function obj = Detector(varargin) 45 | % Create an mtcnn.Detector object 46 | 47 | if nargin > 1 48 | obj.set(varargin{:}); 49 | end 50 | 51 | if obj.UseDagNet 52 | obj.Networks = mtcnn.util.DagNetworkStrategy(obj.UseGPU); 53 | else 54 | obj.Networks = mtcnn.util.DlNetworkStrategy(obj.UseGPU); 55 | end 56 | 57 | obj.Networks.load(); 58 | 59 | end 60 | 61 | function [bboxes, scores, landmarks] = detect(obj, im) 62 | % detect Run the detection algorithm on an image. 63 | % 64 | % Args: 65 | % im - RGB input image for detection 66 | % 67 | % Returns: 68 | % bbox - nx4 array of face bounding boxes in the 69 | % format [x, y, w, h] 70 | % scores - nx1 array of face probabilities 71 | % landmarks - nx5x2 array of facial landmarks 72 | % 73 | % See also: mtcnn.detectFaces 74 | 75 | im = obj.prepImage(im); 76 | 77 | bboxes = []; 78 | scores = []; 79 | landmarks = []; 80 | 81 | %% Stage 1 - Proposal 82 | scales = mtcnn.util.calculateScales(im, ... 83 | obj.MinSize, ... 84 | obj.MaxSize, ... 85 | obj.PnetSize, ... 86 | obj.PyramidScale); 87 | 88 | for scale = scales 89 | [thisBox, thisScore] = ... 90 | mtcnn.proposeRegions(im, scale, ... 91 | obj.ConfidenceThresholds(1), ... 92 | obj.Networks); 93 | bboxes = cat(1, bboxes, thisBox); 94 | scores = cat(1, scores, thisScore); 95 | end 96 | 97 | if ~isempty(scores) 98 | [bboxes, ~] = selectStrongestBbox(gather(bboxes), scores, ... 99 | "RatioType", "Min", ... 100 | "OverlapThreshold", obj.NmsThresholds(1)); 101 | else 102 | return % No proposals found 103 | end 104 | 105 | %% Stage 2 - Refinement 106 | [cropped, bboxes] = obj.prepBbox(im, bboxes, obj.RnetSize); 107 | [probs, correction] = obj.Networks.applyRNet(cropped); 108 | [scores, bboxes] = obj.processOutputs(probs, correction, bboxes, 2); 109 | 110 | if isempty(scores) 111 | return 112 | end 113 | 114 | %% Stage 3 - Output 115 | [cropped, bboxes] = obj.prepBbox(im, bboxes, obj.OnetSize); 116 | 117 | % Adjust bboxes for the behaviour of imcrop 118 | bboxes(:, 1:2) = bboxes(:, 1:2) - 0.5; 119 | bboxes(:, 3:4) = bboxes(:, 3:4) + 1; 120 | 121 | [probs, correction, landmarks] = obj.Networks.applyONet(cropped); 122 | 123 | % landmarks are relative to uncorrected bbox 124 | x = bboxes(:, 1) + landmarks(:, 1:5).*(bboxes(:, 3)); 125 | y = bboxes(:, 2) + landmarks(:, 6:10).*(bboxes(:, 4)); 126 | landmarks = cat(3, x, y); 127 | landmarks(probs(:, 2) < obj.ConfidenceThresholds(3), :, :) = []; 128 | 129 | [scores, bboxes, landmarks] = obj.processOutputs(probs, correction, bboxes, 3, landmarks); 130 | 131 | % Gather and cast the outputs 132 | bboxes= gather(double(bboxes)); 133 | scores = gather(double(scores)); 134 | landmarks = gather(double(landmarks)); 135 | end 136 | 137 | function set.UseDagNet(obj, val) 138 | if verLessThan('matlab', '9.7') && val 139 | warning("mtcnn:Detector:pre19b", ... 140 | "For use in R2019a UseDagNet must be set to true"); 141 | end 142 | obj.UseDagNet = val; 143 | end 144 | end 145 | 146 | methods (Access=private) 147 | function [cropped, bboxes] = prepBbox(obj, im, bboxes, outputSize) 148 | % prepImages Pre-process the images and bounding boxes. 149 | bboxes = mtcnn.util.makeSquare(bboxes); 150 | bboxes = round(bboxes); 151 | cropped = mtcnn.util.cropImage(im, bboxes, outputSize); 152 | end 153 | 154 | function [scores, bboxes, landmarks] = ... 155 | processOutputs(obj, probs, correction, bboxes, netIdx, landmarks) 156 | % processOutputs Post-process the output values. 157 | faceProbs = probs(:, 2); 158 | bboxes = mtcnn.util.applyCorrection(bboxes, correction); 159 | bboxes(faceProbs < obj.ConfidenceThresholds(netIdx), :) = []; 160 | scores = faceProbs(faceProbs >= obj.ConfidenceThresholds(netIdx)); 161 | if ~isempty(scores) 162 | if verLessThan("matlab", "9.9") 163 | % < R2020b no gpuArray support for selectStrongestBbox 164 | [bboxes, scores] = gather(bboxes, scores); 165 | end 166 | [bboxes, scores, index] = selectStrongestBbox(bboxes, scores, ... 167 | "RatioType", "Min", ... 168 | "OverlapThreshold", obj.NmsThresholds(netIdx)); 169 | if netIdx == 3 170 | landmarks = landmarks(index, :, :); 171 | end 172 | end 173 | end 174 | 175 | function outIm = prepImage(obj, im) 176 | % convert the image to the correct scaling and type 177 | % All images should be scaled to -1 to 1 and of single type 178 | % also place on the GPU if required 179 | 180 | switch class(im) 181 | case "uint8" 182 | outIm = single(im)/255*2 - 1; 183 | case "single" 184 | % expect floats to be 0-1 scaled 185 | outIm = im*2 - 1; 186 | case "double" 187 | outIm = single(im)*2 - 1; 188 | otherwise 189 | error("mtcnn:Detector:UnsupportedType", ... 190 | "Input image is of unsupported type '%s'", class(im)); 191 | end 192 | 193 | if obj.UseGPU && ~obj.UseDagNet 194 | outIm = gpuArray(outIm); 195 | end 196 | 197 | end 198 | end 199 | end --------------------------------------------------------------------------------