📄 startshape.cpp
字号:
// $masm\startshape.cpp 1.5 milbo$ routines for finding the start shape//-----------------------------------------------------------------------------// This program is free software; you can redistribute it and/or modify// it under the terms of the GNU General Public License as published by// the Free Software Foundation; either version 2 of the License, or// (at your option) any later version.//// This program is distributed in the hope that it will be useful,// but WITHOUT ANY WARRANTY; without even the implied warranty of// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the// GNU General Public License for more details.//// A copy of the GNU General Public License is available at// http://www.r-project.org/Licenses///-----------------------------------------------------------------------------#include "all.hpp"// following are for aligning base shape to Rowley detector shapestatic const double CONF_RowleyHeightRatio = 0.60;static const double CONF_RowleyWidthRatio = 0.07;static const double CONF_RowleyEyeWeight = 1.0;static const double CONF_RowleyWidthWeight = 0.5;static const double CONF_RowleyHeightWeight = 0.5;static const double CONF_MaxEyeToFaceWidth = 0.52;static const double CONF_EyeFaceBlend = 0.60;// following are for aligning base shape to Viola Jones detector shapestatic const double CONF_VjHeightShift = 0.15; // shift height down by 15%static const double CONF_VjShrink = 0.8; // shrink size of VJ box by 20%//-----------------------------------------------------------------------------// Align MeanShape to the Rowley detector eyes (ignore the overall face box).static voidAlignToRowleyEyes (SHAPE &StartShape, // out const SHAPE &MeanShape, const SHAPE &DetShape) // in{SHAPE Base(2, 2), Rowley(2, 2); // points are MLEye MrEyeDASSERT(iTranslatedPoint(MLEye) < MeanShape.nrows() && iTranslatedPoint(MREye) < MeanShape.nrows());Base.row(0) = MeanShape.row(iTranslatedPoint(MLEye));Base.row(1) = MeanShape.row(iTranslatedPoint(MREye));ASSERT(!Base.fARowIsZero());// In practice the Rowley detector sometimes misaligns the eyes so here// it's better to just use the average height of the eyes.double yMean = (DetShape(DETECTOR_LEye, VY) + DetShape(DETECTOR_REye, VY)) / 2;Rowley(0, VX) = DetShape(DETECTOR_LEye, VX);Rowley(0, VY) = yMean;Rowley(1, VX) = DetShape(DETECTOR_REye, VX);Rowley(1, VY) = yMean;ASSERT(!Rowley.fARowIsZero());Mat Pose(AlignShape(Base, Rowley, NULL));StartShape = TransformShape(MeanShape, Pose);}//-----------------------------------------------------------------------------// Align MeanShape to the Rowley detector overall face box and the eyes.static voidAlignToRowleyFaceAndEyes (SHAPE &StartShape, // out const SHAPE &MeanShape, const SHAPE &DetShape) // in{SHAPE Base(4, 2), Rowley(4, 2); // points are MLEye MrEye MlJaw2 MRJaw2DASSERT(iTranslatedPoint(MLEye) < MeanShape.nrows() && iTranslatedPoint(MREye) < MeanShape.nrows());Base.row(0) = MeanShape.row(iTranslatedPoint(MLEye));Base.row(1) = MeanShape.row(iTranslatedPoint(MREye));Base.row(2) = MeanShape.row(iTranslatedPoint(MLJaw2));Base.row(3) = MeanShape.row(iTranslatedPoint(MRJaw2));ASSERT(!Base.fARowIsZero());// In practice the Rowley detector sometimes misaligns the eyes so here// it's better to just use the average height of the eyes.double yMean = (DetShape(DETECTOR_LEye, VY) + DetShape(DETECTOR_REye, VY)) / 2;Rowley(0, VX) = DetShape(DETECTOR_LEye, VX);Rowley(0, VY) = yMean;Rowley(1, VX) = DetShape(DETECTOR_REye, VX);Rowley(1, VY) = yMean;double xMin = DetShape(DETECTOR_TopLeft, VX);double xMax = DetShape(DETECTOR_BotRight, VX);// recenter the face on the midpoint of eyes (found to give better results)double EyeMidpoint = (DetShape(DETECTOR_LEye, VX)+DetShape(DETECTOR_REye, VX))/2;xMin = EyeMidpoint - (xMax - xMin)/2;xMax = EyeMidpoint + (xMax - xMin)/2;double yMin = DetShape(DETECTOR_TopLeft, VY);double yMax = DetShape(DETECTOR_BotRight, VY);// y position of Jaw2 in Rowley boxdouble HeightCorrect = CONF_RowleyHeightRatio * (yMax - yMin);// difference between MLJaw2-to-MRJaw2 and Rowley boxdouble WidthCorrect = CONF_RowleyWidthRatio * (xMax - xMin);Rowley(2, VX) = xMin + WidthCorrect; Rowley(2, VY) = yMin + HeightCorrect;Rowley(3, VX) = xMax - WidthCorrect; Rowley(3, VY) = yMin + HeightCorrect;ASSERT(!Rowley.fARowIsZero());Vec Weights(Base.nrows());Weights(0) = CONF_RowleyEyeWeight;Weights(1) = CONF_RowleyEyeWeight;Weights(2) = CONF_RowleyWidthWeight;Weights(3) = CONF_RowleyHeightWeight;Mat Pose(AlignShape(Base, Rowley, &Weights));StartShape = TransformShape(MeanShape, Pose);}//-----------------------------------------------------------------------------// Possbily blend the StartShape with a shape generated from the eye positions.// In detail:// Align the MeanShape using only the Rowley eye positions, to give// the shape StartEyes.// Measure the eye positions in StartEyes against those in the StartShape// we got above (which was aligned to both the detector face and detector eyes).// If the eye positions are very different, then form a new StartShape by blending// StartShape and StartEyes.//// This has proven effective in improving the fit in faces with large// alignment errors, without affecting other faces.static void Blend (SHAPE &StartShape, // io const SHAPE MeanShape, const SHAPE DetShape) // in{SHAPE StartEyes;AlignToRowleyEyes(StartEyes, MeanShape, DetShape);double EyeToFaceWidth = (StartEyes(MREye, VX) - StartEyes(MLEye, VX)) / (StartShape(MRJaw2, VX) - StartShape(MLJaw2, VX));double Blend = 0; // assume no blendingif (EyeToFaceWidth > CONF_MaxEyeToFaceWidth) Blend = CONF_EyeFaceBlend;int i;for (i = 0; i <= MNoseTip; i++) StartShape.row(i) = (1-Blend) * StartShape.row(i) + Blend * StartEyes.row(i);for (i = MLEye0; i < StartShape.nrows(); i++) // extra eye points StartShape.row(i) = (1-Blend) * StartShape.row(i) + Blend * StartEyes.row(i);}//-----------------------------------------------------------------------------static voidAlignToRowley (SHAPE &StartShape, // out const SHAPE &MeanShape, const SHAPE &DetShape) // in{AlignToRowleyFaceAndEyes(StartShape, MeanShape, DetShape);Blend(StartShape, MeanShape, DetShape);}//-----------------------------------------------------------------------------// Make the Rowley Jones face box smaller and move it down a bit.// This gives a better position for the start shape and also// is needed so we can find the eyes using the Rowley detector.void AdjustViolaJonesShape (SHAPE &Shape) // io{double xMin = Shape(DETECTOR_TopLeft, VX);double yMin = Shape(DETECTOR_TopLeft, VY);double xMax = Shape(DETECTOR_BotRight, VX);double yMax = Shape(DETECTOR_BotRight, VY);double NewHeight = CONF_VjShrink * (yMax - yMin);double yMean = (Shape(DETECTOR_TopLeft, VY) + Shape(DETECTOR_BotRight, VY)) / 2;yMean += CONF_VjHeightShift * (yMax - yMin); // move face downdouble NewWidth = CONF_VjShrink * (xMax - xMin);double xMean = (Shape(DETECTOR_TopLeft, VX) + Shape(DETECTOR_BotRight, VX)) / 2;Shape(DETECTOR_TopLeft, VX) = xMean - .5 * NewWidth;Shape(DETECTOR_TopLeft, VY) = yMean - .5 * NewHeight;Shape(DETECTOR_BotRight, VX) = xMean + .5 * NewWidth;Shape(DETECTOR_BotRight, VY) = yMean + .5 * NewHeight;}//-----------------------------------------------------------------------------// Align MeanShape to the Viola Jones detector face box.static voidAlignToViolaJonesFace (SHAPE &StartShape, // out const SHAPE &MeanShape, const SHAPE &DetShape) // in{SHAPE Base(2, 2), VjAlign(2, 2); // MlJaw2 MRJaw2Base.row(0) = MeanShape.row(iTranslatedPoint(MLJaw2));Base.row(1) = MeanShape.row(iTranslatedPoint(MRJaw2));ASSERT(!Base.fARowIsZero());double yMean = (DetShape(DETECTOR_TopLeft, VY) + DetShape(DETECTOR_BotRight, VY)) / 2;VjAlign(0, VX) = DetShape(DETECTOR_TopLeft, VX);VjAlign(0, VY) = yMean;VjAlign(1, VX) = DetShape(DETECTOR_BotRight, VX);VjAlign(1, VY) = yMean;ASSERT(!VjAlign.fARowIsZero());Mat Pose(AlignShape(Base, VjAlign, NULL));StartShape = TransformShape(MeanShape, Pose);}//-----------------------------------------------------------------------------static voidAlignToViolaJones (SHAPE &StartShape, // out SHAPE &DetShape, // io: two rows added if fViolaJonesEyes bool fViolaJonesEyes, Image &Img, // in const SHAPE &MeanShape, // in const char sDataDir[]) // in{AdjustViolaJonesShape(DetShape);AlignToViolaJonesFace(StartShape, MeanShape, DetShape);if (fViolaJonesEyes) { int xRightEye, yRightEye, xLeftEye, yLeftEye; FindEyesGivenFace(&xRightEye, &yRightEye, &xLeftEye, &yLeftEye, DetShape, Img, sDataDir); if (xRightEye != -1 && yRightEye != -1 && xLeftEye != -1 && yLeftEye != -1) { // found both the left and right eye positions, so use them DetShape.dimKeep(4, 2); // add two extra rows for the eyes DetShape(DETECTOR_LEye, VX) = xRightEye - Img.width / 2; DetShape(DETECTOR_LEye, VY) = Img.height / 2 - yRightEye; DetShape(DETECTOR_REye, VX) = xLeftEye - Img.width / 2; DetShape(DETECTOR_REye, VY) = Img.height / 2 - yLeftEye; Blend(StartShape, MeanShape, DetShape); } }}//-----------------------------------------------------------------------------// Generate StartShape by aligning the MeanShape to a noised// reference shape (instead of the global detector shape).//// This is invoked during testing if the global detector failed, but we// have a reference shape.//// A test of how good an estimate this is compared to using real Rowley// detector data is as follows: use this instead of the Rowley detector// data on images for which the detector data is available. Then compare// fit results against using the actual Rowley detector data. On large// sample sizes you will get the same results to within less than 1// percent.static void GenerateStartShapeFromRefShape (SHAPE &StartShape, // out const SHAPE &MeanShape, // in const SHAPE &RefShape, // in const char sImageBase[]) // in{lprintf("Using noised ref shape instead of face detector shape\n");StartShape = MeanShape;SHAPE AnchorShape(StartShape.nrows(), 2);// We align to three points of RefShape.// These three points are defined in all three atasets (XM2VTS, BioId and AR sets).double StdDev = 0;switch (sImageBase[0]) { case 'a': StdDev = 1.2; break; case 'B': StdDev = 1.06; break; case 'm': StdDev = 3.20; break; default: SysErr("GenerateStartShapeFromRefShape %s", sImageBase); break; }// multiplying MTipOfChin noise by 5 keeps fit error roughly constant across the entire faceAnchorShape.row(MTipOfChin) = RefShape.row(MTipOfChin) + RandGauss(5 * StdDev);AnchorShape.row(MLEye) = RefShape.row(MLEye) + RandGauss(StdDev);AnchorShape.row(MREye) = RefShape.row(MREye) + RandGauss(StdDev);Mat Pose(AlignShape(StartShape, AnchorShape, NULL));StartShape = TransformShape(MeanShape, Pose);}//-----------------------------------------------------------------------------// Align MeanShape to a global detector shape to provide a// good starting point for the search.// Results are returned in StartShape.boolfGetStartShape (SHAPE &StartShape, // out SHAPE &DetShape, // out: see DETECTOR_ defs in landmarks.hpp for format bool fBrief, // in bool fViolaJones, // in bool fViolaJonesEyes, // in bool fUseRefShapeIfNoDetFace, // in Image &Img, // in const char sImageFile[], // in const char sImageBase[], // in const SHAPE &MeanShape, // in const char sDataDir[], // in const char *sRefShapeFile, // in const SHAPE *pRefShape) // in{bool fSuccess = false;if (sRefShapeFile && sRefShapeFile[0]) { // Use the pre-calculated global detector shape in sShapeFile. The global // detector shapes in sShapeFile were determined by a prior off-line search. // This is equivalent to doing a global detector search using a global detector // but is much faster -- nice for testing new models. bprintf(fBrief, "Precalculated detector shape "); char sTagString[SLEN]; sprintf(sTagString, "%4.4x %s", (fViolaJones? FA_ViolaJones: FA_Rowley), sImageBase); fSuccess = fGetNamedShape(DetShape, sTagString, sRefShapeFile, MAT_PLAIN_STRING, fBrief); if (fSuccess) if (fViolaJones) AlignToViolaJones(StartShape, DetShape, fViolaJonesEyes, Img, MeanShape, sDataDir); else AlignToRowley(StartShape, MeanShape, DetShape); bprintf(fBrief, "\n"); }else if (fViolaJones) { fSuccess = fViolaJonesFindFace(DetShape, Img, sImageFile, sDataDir); if (fSuccess) AlignToViolaJones(StartShape, DetShape, fViolaJonesEyes, Img, MeanShape, sDataDir); }else // Rowley detector { fSuccess = fRowleyFindFace(DetShape, Img, sImageFile, sDataDir); if (fSuccess) AlignToRowley(StartShape, MeanShape, DetShape); }if (!fSuccess && fUseRefShapeIfNoDetFace && pRefShape) { GenerateStartShapeFromRefShape(StartShape, MeanShape, *pRefShape, sImageBase); fSuccess = true; }// An all zero row means both the x and y coords are zero.// But x and y both equal to 0 means the point is "unused".// So assert that that is not the case.// TODO should do this properly by setting all 0 row to a small nbr.ASSERT(!fSuccess || !StartShape.fARowIsZero());return fSuccess;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -