From a09800ac8fca0559429baf06fab7a6a69377fc7d Mon Sep 17 00:00:00 2001 From: Justin Pinkney Date: Tue, 31 Mar 2020 12:08:51 +0100 Subject: [PATCH] Perform image type conversion upfront To implement feature #5 we make the image type and range conversion more streamlined. Images are now converted to the expected type and range (single -1 to 1) as the first step in detection. This gives some changes in the numerics due to differences in types for pre-processing steps. --- code/mtcnn/+mtcnn/Detector.m | 36 +++++++++++++++++++++++------ code/mtcnn/+mtcnn/proposeRegions.m | 4 ++-- test/+tests/DetectorTest.m | 16 ++++++++++--- test/makeDetectionReference.m | 10 ++++++++ test/resources/ref.mat | Bin 744 -> 3176 bytes 5 files changed, 54 insertions(+), 12 deletions(-) create mode 100644 test/makeDetectionReference.m diff --git a/code/mtcnn/+mtcnn/Detector.m b/code/mtcnn/+mtcnn/Detector.m index 2cc7df9..89f36fa 100644 --- a/code/mtcnn/+mtcnn/Detector.m +++ b/code/mtcnn/+mtcnn/Detector.m @@ -70,9 +70,7 @@ % % See also: mtcnn.detectFaces - if obj.UseGPU() - im = gpuArray(single(im)); - end + im = obj.prepImage(im); bboxes = []; scores = []; @@ -103,7 +101,7 @@ end %% Stage 2 - Refinement - [cropped, bboxes] = obj.prepImages(im, bboxes, obj.RnetSize); + [cropped, bboxes] = obj.prepBbox(im, bboxes, obj.RnetSize); [probs, correction] = mtcnn.rnet(cropped, obj.RnetWeights); [scores, bboxes] = obj.processOutputs(probs, correction, bboxes, 2); @@ -112,7 +110,7 @@ end %% Stage 3 - Output - [cropped, bboxes] = obj.prepImages(im, bboxes, obj.OnetSize); + [cropped, bboxes] = obj.prepBbox(im, bboxes, obj.OnetSize); % Adjust bboxes for the behaviour of imcrop bboxes(:, 1:2) = bboxes(:, 1:2) - 0.5; @@ -144,12 +142,12 @@ function loadWeights(obj) obj.OnetWeights = load(fullfile(mtcnnRoot(), "weights", "onet.mat")); end - function [cropped, bboxes] = prepImages(obj, im, bboxes, outputSize) + function [cropped, bboxes] = prepBbox(obj, im, bboxes, outputSize) % prepImages Pre-process the images and bounding boxes. bboxes = mtcnn.util.makeSquare(bboxes); bboxes = round(bboxes); cropped = mtcnn.util.cropImage(im, bboxes, outputSize); - cropped = dlarray(single(cropped)./255*2 - 1, "SSCB"); + cropped = dlarray(cropped, "SSCB"); end @@ -167,5 +165,29 @@ function loadWeights(obj) "OverlapThreshold", obj.NmsThresholds(netIdx)); end end + + function outIm = prepImage(obj, im) + % convert the image to the correct scaling and type + % All images should be scaled to -1 to 1 and of single type + % also place on the GPU if required + + switch class(im) + case "uint8" + outIm = single(im)/255*2 - 1; + case "single" + % expect floats to be 0-1 scaled + outIm = im*2 - 1; + case "double" + outIm = single(im)*2 - 1; + otherwise + error("mtcnn:Detector:UnsupportedType", ... + "Input image is of unsupported type '%s'", class(im)); + end + + if obj.UseGPU() + outIm = gpuArray(outIm); + end + + end end end \ No newline at end of file diff --git a/code/mtcnn/+mtcnn/proposeRegions.m b/code/mtcnn/+mtcnn/proposeRegions.m index 0a6fe32..f72e55a 100644 --- a/code/mtcnn/+mtcnn/proposeRegions.m +++ b/code/mtcnn/+mtcnn/proposeRegions.m @@ -2,7 +2,7 @@ % proposeRegions Generate region proposals at a given scale. % % Args: -% im - Input image 0-255 range +% im - Input image -1 to 1 range, type single % scale - Scale to run proposal at % threshold - Confidence threshold to accept proposal % weights - P-Net weights struct @@ -19,7 +19,7 @@ pnetSize = 12; im = imresize(im, 1/scale); - im = dlarray(single(im)./255*2 - 1, "SSCB"); + im = dlarray(im, "SSCB"); [probability, correction] = mtcnn.pnet(im, weights); diff --git a/test/+tests/DetectorTest.m b/test/+tests/DetectorTest.m index 7dffdb0..462e59a 100644 --- a/test/+tests/DetectorTest.m +++ b/test/+tests/DetectorTest.m @@ -8,6 +8,12 @@ Reference end + properties (TestParameter) + imageTypeConversion = struct("uint8", @(x) x, ... + "single", @(x) single(x)/255, ... + "double", @(x) double(x)/255) + end + methods (TestClassSetup) function setupTestImage(test) test.Image = imread("visionteam.jpg"); @@ -26,10 +32,14 @@ function testCreate(test) detector = mtcnn.Detector(); end - function testDetectwithDefaults(test) + function testDetectwithDefaults(test, imageTypeConversion) + % Test expected inputs with images of type uint8, single, + % double (float images are scaled 0-1); detector = mtcnn.Detector(); - [bboxes, scores, landmarks] = detector.detect(test.Image); + inputImage = imageTypeConversion(test.Image); + + [bboxes, scores, landmarks] = detector.detect(inputImage); test.verifyEqual(size(bboxes), [6, 4]); test.verifyEqual(size(scores), [6, 1]); @@ -118,4 +128,4 @@ function testGpuDetect(test) test.verifyEqual(landmarks, test.Reference.landmarks, "RelTol", 1e-1); end end -end \ No newline at end of file + end diff --git a/test/makeDetectionReference.m b/test/makeDetectionReference.m new file mode 100644 index 0000000..0f96a2d --- /dev/null +++ b/test/makeDetectionReference.m @@ -0,0 +1,10 @@ +function makeDetectionReference() + % Run the detector in known good config to create reference boxes, + % scores and landmarks for regression tests. + im = imread("visionteam.jpg"); + [bboxes, scores, landmarks] = mtcnn.detectFaces(im); + + filename = fullfile(mtcnnTestRoot(), "resources", "ref.mat"); + save(filename, "bboxes", "scores", "landmarks"); + +end \ No newline at end of file diff --git a/test/resources/ref.mat b/test/resources/ref.mat index 585811a8de3c07139d74bc9ffca9d23456cc3bab..11c595b21deb9dc475cf824cda82ccfc067e4e99 100644 GIT binary patch literal 3176 zcmeH|T}V_x6vxkXH8<0+FEZ<4pdbsgbu*Ji@os8pwYus0^-%70UDH}y*mc3s;+Bv} zL{U%GLn~28i77n<6?vfuksk6T=}>L~Zw+nOh`DML|K%!v5|#^Pe+k&dig;jT}R&R})W=-8Z!x$s!kuk@{hO5!pklTuAlo@;yi` zx4(kyo=a||cf79<3HOIMOULuc9!QgsYX7b6NX`1fN!;H+HjK8=JX%3I+1|-v?ms$H zf;y%w-hqUrV%50DbL+bIB?GU#1Sv+36^xIL@%?G=1(Yyt(K17awM0F(reLHo;KwH! z|9-nwl~)u?U=djviHd!gE3kQQbtKNE;C}O7%ERRUgJ!-LHnCB&-wSj9%yP`OfBn74 zqpDiO`2m6>Q5u^C*)HKDH&z$1=$+{{U-&#-kKu;sjC;lSo z_3AOwvzL3hzUUg|qrIEp`E;-6`E0DBeAMuU;}{R#KP0`p*nhgl?jw)RKX|3oA;-6_UUywr zn-Z(5xj6enjdzPmPA=JL&MR~NZu$A|_okaUb;K(zxtJ(isJV|Vee1RJN%~h`%cYxI zYm45Gx!bXP^6js?RuuAY`%{(KYqjOfkv@h0JI`NQrs-lSKYI;rQe@AHSBXDnYgk|L?V( aP}Fd7zM{$Dd<|W;rSlmx85XsOJ^=vQ%`>F{