static Shape StartShapeFromDetPar( const DetPar& detpar_roi, // in: detpar wrt the ROI const Image& face_roi, // in const Shape& meanshape, // in ESTART estart) // in: use mouth etc. to posn start shape? { CV_Assert(estart == ESTART_RECT_ONLY || estart == ESTART_EYES || estart == ESTART_EYE_AND_MOUTH); Shape startshape; Shape meanshape1(meanshape); if (estart == ESTART_EYE_AND_MOUTH && // use both eyes and mouth? Valid(detpar_roi.mouthx) && Valid(detpar_roi.lex) && Valid(detpar_roi.rex)) { FlipIfLeftFacing(meanshape1, detpar_roi.eyaw, face_roi.cols); startshape = AlignMeanShapeToBothEyesAndMouth(detpar_roi, meanshape1); FlipIfLeftFacing(startshape, detpar_roi.eyaw, face_roi.cols); } else if (Valid(detpar_roi.lex) && // use both eyes? Valid(detpar_roi.rex)) { FlipIfLeftFacing(meanshape1, detpar_roi.eyaw, face_roi.cols); // TODO Tune the following code, what approach is best? if (detpar_roi.eyaw == EYAW00) startshape = AlignMeanShapeToBothEyesEstMouth(detpar_roi, meanshape1); else startshape = AlignMeanShapeToBothEyesNoMouth(detpar_roi, meanshape1); FlipIfLeftFacing(startshape, detpar_roi.eyaw, face_roi.cols); } else if (estart == ESTART_EYE_AND_MOUTH && // use left eye and mouth? Valid(detpar_roi.mouthx) && Valid(detpar_roi.lex)) { FlipIfLeftFacing(meanshape1, detpar_roi.eyaw, face_roi.cols); startshape = AlignMeanShapeToLeftEyeAndMouth(detpar_roi, meanshape1); FlipIfLeftFacing(startshape, detpar_roi.eyaw, face_roi.cols); } else if (estart == ESTART_EYE_AND_MOUTH && // use right eye and mouth? Valid(detpar_roi.mouthx) && Valid(detpar_roi.rex)) { FlipIfLeftFacing(meanshape1, detpar_roi.eyaw, face_roi.cols); startshape = AlignMeanShapeToRightEyeAndMouth(detpar_roi, meanshape1); FlipIfLeftFacing(startshape, detpar_roi.eyaw, face_roi.cols); } else // last resort: use the face det rectangle (can't use facial features) { startshape = AlignMeanShapeToFaceDetRect(detpar_roi, meanshape1, FACERECT_SCALE_WHEN_NO_EYES, face_roi); } return JitterPointsAt00(startshape); }
static Shape PinMeanShape( // align mean shape to the pinned points const Shape& pinned, // in: at least two of these points must be set const Shape& meanshape) // in { CV_Assert(pinned.rows == meanshape.rows); int ipoint, nused = 0; // number of points used in pinned for (ipoint = 0; ipoint < meanshape.rows; ipoint++) if (PointUsed(pinned, ipoint)) nused++; if (nused < 2) Err("Need at least two pinned landmarks"); // Create an anchor shape (the pinned landmarks) and an alignment shape (the // points in meanshape that correspond to those pinned landmarks). Do that by // copying the used points in pinned to pinned_used, and the corresponding // points in meanshape to meanused. Shape pinned_used(nused, 2), mean_used(nused, 2); int i = 0; for (ipoint = 0; ipoint < meanshape.rows; ipoint++) if (PointUsed(pinned, ipoint)) { pinned_used(i, IX) = pinned(ipoint, IX); pinned_used(i, IY) = pinned(ipoint, IY); mean_used(i, IX) = meanshape(ipoint, IX); mean_used(i, IY) = meanshape(ipoint, IY); i++; } CV_Assert(i == nused); // transform meanshape to pose generated by aligning mean_used to pinned_used Shape TransformedShape( AlignShape(meanshape, AlignmentMat(mean_used, pinned_used))); return JitterPointsAt00(TransformedShape); }
static Shape StartShapeFromDetPar( const DetPar& detpar_roi, // in: detpar wrt the ROI const Image& face_roi, // in const Shape& meanshape, // in ESTART estart) // in: use mouth etc. to posn start shape? { Shape startshape; switch (estart) { case ESTART_RECT_ONLY: startshape = EstartRectOnly(detpar_roi, face_roi, meanshape); break; case ESTART_EYES: startshape = EstartEyes(detpar_roi, face_roi, meanshape); break; case ESTART_EYE_AND_MOUTH: startshape = EstartEyeMouth(detpar_roi, face_roi, meanshape); break; default: Err("Invalid estart %d", estart); } return JitterPointsAt00(startshape); }
Shape ConformShapeToMod( // Return a copy of inshape conformed to the model VEC& b, // io: eigvec weights, 2n x 1 const Shape& inshape, // in: the current position of the landmarks const Shape& meanshape, // in: n x 2 const VEC& eigvals, // in: neigs x 1 const MAT& eigvecs, // in: 2n x neigs const MAT& eigvecsi, // in: neigs x 2n, inverse of eigvecs const double bmax, // in: for LimitB const VEC& pointweights) // in: contribution of each point to the pose { Shape shape(inshape.clone()); // estimate the pose which transforms the shape into the model space // (use the b from previous iterations of the ASM) MAT modelshape(AsColVec(meanshape) + eigvecs * b); modelshape = DimKeep(modelshape, shape.rows, 2); // redim back to 2 columns const MAT pose(AlignmentMat(modelshape, shape, Buf(pointweights))); // transform the shape into the model space modelshape = TransformShape(shape, pose.inv(cv::DECOMP_LU)); // update shape model params b to match modelshape, then limit b b = eigvecsi * AsColVec(modelshape - meanshape); LimitB(b, eigvals, bmax); // generate conformedshape from the model using the limited b // (we generate as a column vec, then redim back to 2 columns) const Shape conformedshape(DimKeep(eigvecs * b, shape.rows, 2)); // back to image space return JitterPointsAt00(TransformShape(meanshape + conformedshape, pose)); }
static void StartShapeAndRoi( // we have the facerect, now get the rest Shape& startshape, // out: the start shape we are looking for Image& face_roi, // out: ROI around face, possibly rotated upright DetPar& detpar_roi, // out: detpar wrt to face_roi DetPar& detpar, // io: detpar wrt to img (has face rect on entry) const Image& img, // in: the image (grayscale) const vec_Mod& mods, // in: a vector of models, one for each yaw range // (use only estart, and meanshape) StasmCascadeClassifier cascade) { PossiblySetRotToZero(detpar.rot); // treat small rots as zero rots FaceRoiAndDetPar(face_roi, detpar_roi, // get ROI around face img, detpar, false); DetectEyesAndMouth(detpar_roi, // use OpenCV eye and mouth detectors face_roi, cascade); // Some face detectors return the face rotation, some don't (in // the call to NextFace_ just made via NextStartShapeAndRoi). // If we don't have the rotation, then estimate it from the eye // angle, if the eyes are available. if (!Valid(detpar.rot)) // don't have the face rotation? { detpar_roi.rot = EstRotFromEyeAngle(detpar_roi); PossiblySetRotToZero(detpar_roi.rot); detpar.rot = detpar_roi.rot; if (detpar.rot != 0) { // face is rotated: rotate ROI and re-get the eyes and mouth // TODO: Prevent bogus OpenCV assert fail face_roi.data == img.data. face_roi = Image(0,0); FaceRoiAndDetPar(face_roi, detpar_roi, img, detpar, false); DetectEyesAndMouth(detpar_roi, // use OpenCV eye and mouth detectors face_roi, cascade); } } if (trace_g) lprintf("%-6.6s yaw %3.0f rot %3.0f ", EyawAsString(detpar_roi.eyaw), detpar_roi.yaw, detpar_roi.rot); else logprintf("%-6.6s yaw %3.0f rot %3.0f ", EyawAsString(detpar_roi.eyaw), detpar_roi.yaw, detpar_roi.rot); // select an ASM model based on the face's yaw const Mod* mod = mods[ABS(EyawAsModIndex(detpar_roi.eyaw, mods))]; const ESTART estart = mod->Estart_(); CV_Assert(estart == ESTART_EYES || estart == ESTART_EYE_AND_MOUTH); startshape = StartShapeFromDetPar(detpar_roi, face_roi, mod->MeanShape_(), estart); if (IsLeftFacing(detpar_roi.eyaw)) FlipImgInPlace(face_roi); JitterPointsAt00(startshape); }