static void DrawEyes( CImage& img, // io const vec_Rect& leyes, // in const vec_Rect& reyes, // in int ileft_best, // in int iright_best, // in const Rect& facerect, // in EYAW eyaw) // in { // rectangles showing search regions const int linewidth = facerect.width > 700? 3: facerect.width > 300? 2: 1; const Rect left_rect(EyeSearchRect(eyaw, facerect, false)); const Rect right_rect(EyeSearchRect(eyaw, facerect, true)); const Rect eye_inner_rect(EyeInnerRect(eyaw, facerect)); DrawRect(img, facerect, Dark(C_YELLOW), linewidth); DrawRect(img, left_rect, VeryDark(C_RED), linewidth); DrawRect(img, right_rect, VeryDark(C_GREEN), linewidth); DrawRect(img, eye_inner_rect, Dark(C_YELLOW), linewidth); int i; for (i = 0; i < NSIZE(leyes); i++) DrawFeat(img, i, leyes, C_RED, linewidth, i==ileft_best); for (i = 0; i < NSIZE(reyes); i++) DrawFeat(img, i, reyes, C_RED /*C_GREEN*/, linewidth, i==iright_best); }
// Show a complex array --------------------------------------------------- std::ostream& operator<<(std::ostream& ostrm, const MultidimArray< Complex >& v) { if (v.xdim == 0) ostrm << "NULL MultidimArray\n"; else ostrm << std::endl; for (int l = 0; l < NSIZE(v); l++) { if (NSIZE(v)>1) ostrm << "Image No. " << l << std::endl; for (int k = STARTINGZ(v); k <= FINISHINGZ(v); k++) { if (ZSIZE(v) > 1) ostrm << "Slice No. " << k << std::endl; for (int i = STARTINGY(v); i <= FINISHINGY(v); i++) { for (int j = STARTINGX(v); j <= FINISHINGX(v); j++) ostrm << "(" << A3D_ELEM(v, k, i, j).real << "," << A3D_ELEM(v, k, i, j).imag << ")" << ' '; ostrm << std::endl; } } } return ostrm; }
static void ioctl_simplestruct_init_from_bin(ioctl_tree * node, const void *data) { DBG(DBG_IOCTL_TREE, "ioctl_simplestruct_init_from_bin: %s(%lX): size is %lu bytes\n", node->type->name, node->id, NSIZE(node)); node->data = malloc(NSIZE(node)); memcpy(node->data, data, NSIZE(node)); }
static void DiscardMissizedFaces( vec_DetPar& detpars) // io { // constants (TODO These have not yet been rigorously empirically adjusted.) static const double MIN_WIDTH = 1.33; // as fraction of median width static const double MAX_WIDTH = 1.33; // as fraction of median width if (NSIZE(detpars) >= 3) // need at least 3 faces { // sort the faces on their width (smallest first) so can get median width sort(detpars.begin(), detpars.end(), DecreasingWidth); const int median = cvRound(detpars[NSIZE(detpars) / 2].width); const int minallowed = cvRound(median / MIN_WIDTH); const int maxallowed = cvRound(MAX_WIDTH * median); // keep only faces that are not too big or small vec_DetPar all_detpars(detpars); detpars.resize(0); for (int iface = 0; iface < NSIZE(all_detpars); iface++) { DetPar* face = &all_detpars[iface]; if (face->width >= minallowed && face->width <= maxallowed) detpars.push_back(*face); else if (trace_g || TRACE_IMAGES) lprintf("[discard face%d of %d]", iface, NSIZE(all_detpars)); } } }
void ShapeFile::Subset_( // select a subset of the shapes int nshapes, // in: number of shapes to select int seed, // in: if 0 use first nshapes, // else rand subset of nshapes const char* sregex) // in: regex string, NULL or "" to match any { SubsetRegex_(sregex); // select shapes matching the regex if (nshapes) SubsetN_(nshapes, seed, sregex); // select nshapes nchar_ = 0; // nbr of chars in longest string in bases_ for (int i = 0; i < NSIZE(bases_); i++) if (int(bases_[i].length()) > nchar_) nchar_ = int(bases_[i].length()); if (nshapes == 1 || nshapes_ == 1) lprintf("Using 1 shape"); else if (nshapes == 0) lprintf("Using all %d shapes", nshapes_); else if (seed == 0) lprintf("Using the first %d shape%s", nshapes, plural(nshapes)); else lprintf("Using a random subset of %d shape%s", nshapes, plural(nshapes)); if (sregex && sregex[0]) lprintf(" matching %s", sregex); if (NSIZE(bases_) > 1) lprintf(" (%s ... %s)", bases_[0].c_str(), bases_[NSIZE(bases_)-1].c_str()); lprintf("\n"); }
static void NbrUsedPointsVec( // nbr of used points in each shape vec_int& nused_vec, // out: vec [nshapes] of int const vec_Shape& shapes) // in { nused_vec.resize(NSIZE(shapes)); for (int ishape = 0; ishape < NSIZE(shapes); ishape++) nused_vec[ishape] = NbrUsedPoints(shapes[ishape]); }
void CopyShapeVec( vec_Shape& newshapes, // out const vec_Shape& oldshapes) // in { newshapes.resize(NSIZE(oldshapes)); for (int i = 0; i < NSIZE(oldshapes); i++) oldshapes[i].copyTo(newshapes[i]); }
static void SelectMouth( int& imouth_best, // out: index into mouths, -1 if none int ileft_best, // in: index of best left eye, -1 if none int iright_best, // in: index of best right eye, -1 if none const vec_Rect& leyes, // in: left eyes found by eye detector const vec_Rect& reyes, // in: right eyes found by eye detector const vec_Rect& mouths, // in: left eyes found by eye detector const Rect& mouth_inner_rect) // in: center of mouth must be in this region { CV_Assert(!mouths.empty()); imouth_best = -1; // if only one mouth, use it if (NSIZE(mouths) == 1 && InRect(mouths[0], mouth_inner_rect)) imouth_best = 0; else { // More than one mouth: selected the lowest mouth to avoid // "nostril mouths". But to avoid "chin mouths", the mouth // must also meet the following criteria: // i) it must be wider than the .7 * smallest eye width // ii) it must be not much narrower than widest mouth. int minwidth = 0; if (ileft_best != -1) minwidth = leyes[ileft_best].width; if (iright_best != -1) minwidth = MIN(minwidth, reyes[iright_best].width); minwidth = cvRound(.7 * minwidth); // find widest mouth int maxwidth = minwidth; for (int imouth = 0; imouth < NSIZE(mouths); imouth++) { const Rect mouth = mouths[imouth]; if (InRect(mouth, mouth_inner_rect) && mouth.width > maxwidth) { maxwidth = mouth.width; imouth_best = imouth; } } // choose lowest mouth that is at least .84 the width of widest minwidth = MAX(minwidth, cvRound(.84 * maxwidth)); int ymin = int(-1e5); for (int imouth = 0; imouth < NSIZE(mouths); imouth++) { const Rect mouth = mouths[imouth]; if (InRect(mouth, mouth_inner_rect) && mouth.y + mouth.height / 2 > ymin && mouth.width > minwidth) { ymin = mouth.y + mouth.height / 2; imouth_best = imouth; } } } }
void DetectFaces( // all face rects into detpars vec_DetPar& detpars, // out const Image& img, // in int minwidth) // in: as percent of img width { CV_Assert(!facedet_g.empty()); // check that OpenFaceDetector_ was called int leftborder = 0, topborder = 0; // border size in pixels Image bordered_img(BORDER_FRAC == 0? img: EnborderImg(leftborder, topborder, img)); // Detection results are very slightly better with equalization // (tested on the MUCT images, which are not pre-equalized), and // it's quick enough to equalize (roughly 10ms on a 1.6 GHz laptop). Image equalized_img; cv::equalizeHist(bordered_img, equalized_img); CV_Assert(minwidth >= 1 && minwidth <= 100); // TODO smallest bioid faces are about 80 pixels width, hence 70 below const int minpix = MAX(minwidth <= 5? 70: 100, cvRound(img.cols * minwidth / 100.)); // the params below are accurate but slow static const double SCALE_FACTOR = 1.1; static const int MIN_NEIGHBORS = 3; static const int DETECTOR_FLAGS = 0; vec_Rect facerects = // all face rects in image Detect(equalized_img, facedet_g, NULL, SCALE_FACTOR, MIN_NEIGHBORS, DETECTOR_FLAGS, minpix); // copy face rects into the detpars vector detpars.resize(NSIZE(facerects)); for (int i = 0; i < NSIZE(facerects); i++) { Rect* facerect = &facerects[i]; DetPar detpar; // detpar constructor sets all fields INVALID // detpar.x and detpar.y is the center of the face rectangle detpar.x = facerect->x + facerect->width / 2.; detpar.y = facerect->y + facerect->height / 2.; detpar.x -= leftborder; // discount the border we added earlier detpar.y -= topborder; detpar.width = double(facerect->width); detpar.height = double(facerect->height); detpar.yaw = 0; // assume face has no yaw in this version of Stasm detpar.eyaw = EYAW00; detpars[i] = detpar; } }
static void NormalizeDesc( // take sqrt of elems and divide by L2 norm VEC& desc) // io { double* const data = Buf(desc); for (int i = 0; i < NSIZE(desc); i++) data[i] = sqrt(data[i]); // sqrt reduces effect of outliers const double norm = cv::norm(desc); // L2 norm if (!IsZero(norm)) { const double scale = FINAL_SCALE / norm; for (int i = 0; i < NSIZE(desc); i++) data[i] *= scale; } }
static void WriteTrainDescs( const TRAIN_DISTS& train_dists, // in: [ipoint] [idesc] distances from true landmark const TRAIN_DESCS& train_descs, // in: [ipoint] [idesc] training descriptors int ilev, // in: const char* outdir) // in: output directory (-d flag) { clock_t start_time = clock(); const int npoints = NSIZE(train_descs); Pacifier pacifier(npoints); for (int ipoint = 0; ipoint < npoints; ipoint++) { // filename const int ndigits = NumDigits(npoints); char format[SLEN]; // e.g. tasmout/log/lev1_p23_classic.desc sprintf(format, "%%s/log/lev%%d_p%%%d.%dd_%%s.desc", ndigits, ndigits); char path[SLEN]; sprintf(path, format, outdir, ilev, ipoint, IsTasmHatDesc(ilev, ipoint)? "hat": "classic"); static int printed; PrintOnce(printed, " generating %s (tasm -w flag)...\n", path); FILE* file = fopen(path, "wb"); if (!file) Err("Cannot open %s", path); // header const vec_VEC descs = train_descs[ipoint]; // descriptors for this point const vec_double dists = train_dists[ipoint]; Fprintf(file, "ilev ipoint dist "); for (int i = 0; i < NSIZE(descs[0]); i++) { char s[SLEN]; sprintf(s, "x%d ", i); Fprintf(file, " %10.10s", s); } Fprintf(file, "\n"); // contents for (int idesc = 0; idesc < NSIZE(descs); idesc++) { Fprintf(file, "%4d %6d %10g", ilev, ipoint, dists[idesc]); for (int i = 0; i < NSIZE(descs[idesc]); i++) Fprintf(file, " %10g", descs[idesc](i)); Fprintf(file, "\n"); } fclose(file); if (print_g) pacifier.Print_(ipoint); } if (print_g) pacifier.End_(); lprintf(" [%.1f secs]\n", double(clock() - start_time) / CLOCKS_PER_SEC); }
static VEC FullProf( // return full profile const Image& img, // in const MAT& shape, // in int ipoint, // in: index of the current point int fullproflen) // in { CV_Assert(fullproflen > 1 && fullproflen < 100); // 100 is arb CV_Assert(fullproflen % 2 == 1); // fullprof length must be odd VEC fullprof(1, fullproflen); double xstep; // x axis dist corresponding to one pixel along whisker double ystep; WhiskerStep(xstep, ystep, shape, ipoint); const double x = shape(ipoint, IX); // center point of the whisker const double y = shape(ipoint, IY); // number of pixs to sample in each direction along the whisker const int n = (NSIZE(fullprof) - 1) / 2; int prevpix = Pix(img, Step(x, xstep, -n-1), Step(y, ystep, -n-1)); for (int i = -n; i <= n; i++) { const int pix = Pix(img, Step(x, xstep, i), Step(y, ystep, i)); fullprof(i + n) = double(pix - prevpix); // signed gradient prevpix = pix; } return fullprof; }
static void RandInts( vec_int& ints, // out: scrambled integers in range 0 ... NSIZE(ints)-1 int seed) // in: random seed { const int n = NSIZE(ints); CV_Assert(n > 0); if (n > RAND_MAX) Err("vector size %d is too big (max allowed is %d)", n, RAND_MAX); CV_Assert(seed != 0); if (seed == 1) // 1 has a special meaning which we don't want seed = int(1e6); // arb int i; for (i = 0; i < n; i++) ints[i] = i; srand(seed); // We use our own random shuffle here because different compilers // give different results which messes up regression testing. // (I think only Visual C 6.0 is incompatible with everyone else?) // // Following code is equivalent to // random_shuffle(ints.begin(), ints.end(), // pointer_to_unary_function<int,int>(RandInt)); vec_int::iterator it = ints.begin(); for (i = 2; ++it != ints.end(); i++) iter_swap(it, ints.begin() + rand() % n); }
static inline void GetMagsAndOrients_AllInImg( vec_double& mags, // out vec_double& orients, // out const int ix, // in: x coord of center of patch const int iy, // in: y coord of center of patch const int patchwidth, // in const MAT& magmat, // in const MAT& orientmat, // in const vec_double& pixelweights) // in { const int halfpatchwidth = (patchwidth-1) / 2; int ipix = 0; for (int x = iy - halfpatchwidth; x <= iy + halfpatchwidth; x++) { const double* const magbuf = Buf(magmat) + x * magmat.cols; const double* const orientbuf = Buf(orientmat) + x * orientmat.cols; for (int y = ix - halfpatchwidth; y <= ix + halfpatchwidth; y++) { mags[ipix] = pixelweights[ipix] * magbuf[y]; orients[ipix] = orientbuf[y]; ipix++; } } CV_DbgAssert(ipix == NSIZE(mags)); }
void Mod::LevSearch_( // do an ASM search at one level in the image pyr Shape& shape, // io: the face shape for this pyramid level int ilev, // in: pyramid level (0 is full size) const Image& img, // in: image scaled to this pyramid level const Shape& pinnedshape) // in: if no rows then no pinned landmarks, else // points except those equal to 0,0 are pinned const { TraceShape(shape, img, ilev, 0, "enterlevsearch"); InitHatLevData(img, ilev); // init internal HAT mats for this lev VEC b(NSIZE(shapemod_.eigvals_), 1, 0.); // eigvec weights, init to 0 for (int iter = 0; iter < SHAPEMODEL_ITERS; iter++) { // suggest shape by descriptor matching at each landmark SuggestShape_(shape, ilev, img, pinnedshape); TraceShape(shape, img, ilev, iter, "suggested"); // adjust suggested shape to conform to the shape model if (pinnedshape.rows) shape = shapemod_.ConformShapeToMod_Pinned_(b, shape, ilev, pinnedshape); else shape = shapemod_.ConformShapeToMod_(b, shape, ilev); TraceShape(shape, img, ilev, iter, "conformed"); } }
void GenAndWriteDescMods( // generate the descriptor models and write them to .mh files const ShapeFile& sh, // in: the shapefile contents const char* outdir, // in: output directory (-d flag) const char* modname, // in: e.g. "yaw00" const char* cmdline, // in: command line used to invoke tasm bool write_traindescs) // in: tasm -w flag { lprintf("--- Generating the descriptor models from %d shapes ---\n", NSIZE(sh.shapes_)); srand((unsigned int)TASM_NEGTRAIN_SEED); // we work a level at a time to save memory (although it takes longer than // doing all levels simultaneously because we have to reread the images for // each level) for (int ilev = 0; ilev < N_PYR_LEVS; ilev++) { TRAIN_DISTS train_dists; // [ipoint] [idesc] distances from true landmark TRAIN_DESCS train_descs; // [ipoint] [idesc] training descriptors // get the training descriptors for all points and images for this pyr lev GetTrainingDescs(train_dists, train_descs, ilev, sh); if (write_traindescs) WriteTrainDescs(train_dists, train_descs, ilev, outdir); // generate the descriptor models from the training descriptors GenDescMods(train_dists, train_descs, ilev, sh, outdir, modname, cmdline); } }
static void GenDescMods( // generate descriptor models for all points and images for one pyr lev TRAIN_DISTS& train_dists, // out: [ipoint] [idesc] distances from true landmark TRAIN_DESCS& train_descs, // out: [ipoint] [idesc] training descriptors int ilev, // in: pyramid level (0 is full scale) const ShapeFile& sh, // in const char* outdir, // in: output directory (-d flag) const char* modname, // in: e.g. "yaw00" const char* cmdline) // in: command line used to invoke tasm { clock_t start_time = clock(); lprintf("Generating pyramid level %d models ", ilev); const int npoints = sh.shapes_[0].rows; Pacifier pacifier(npoints); for (int ipoint = 0; ipoint < npoints; ipoint++) { const vec_VEC descs = train_descs[ipoint]; // descriptors for this point CV_Assert(NSIZE(descs)); char msg[SLEN]; sprintf(msg, "(level %d point %d)", ilev, ipoint); if (IsTasmHatDesc(ilev, ipoint)) // HAT descriptor? GenHatMod(ilev, ipoint, npoints, train_dists[ipoint], descs, msg, outdir, modname, cmdline); else // classical 1D profile GenClassicMod(ilev, ipoint, npoints, descs, msg, outdir, modname, cmdline); if (print_g) pacifier.Print_(ipoint); } if (print_g) pacifier.End_(); lprintf(" [%.1f secs]\n", double(clock() - start_time) / CLOCKS_PER_SEC); }
void FaceDet::DetectFaces_( // call once per image to find all the faces const Image& img, // in: the image (grayscale) const char*, // in: unused (match virt func signature) bool multiface, // in: if false, want only the best face int minwidth, // in: min face width as percentage of img width void* user) // in: unused (match virt func signature) { CV_Assert(user == NULL); CV_Assert(!facedet_g.empty()); // check that OpenFaceDetector_ was called DetectFaces(detpars_, img, minwidth); TraceFaces(detpars_, img, "facedet_BeforeDiscardMissizedFaces.bmp"); DiscardMissizedFaces(detpars_); TraceFaces(detpars_, img, "facedet_AfterDiscardMissizedFaces.bmp"); if (multiface) // order faces on increasing distance from left margin { sort(detpars_.begin(), detpars_.end(), IncreasingLeftMargin); TraceFaces(detpars_, img, "facedet.bmp"); } else { // order faces on decreasing width, keep only the first (the largest face) sort(detpars_.begin(), detpars_.end(), DecreasingWidth); TraceFaces(detpars_, img, "facedet.bmp"); if (NSIZE(detpars_)) detpars_.resize(1); } iface_ = 0; // next invocation of NextFace_ must get first face }
void FaceDet::DetectFaces_( // call once per image to find all the faces const Image& img, // in: the image (grayscale) const char* imgpath, // in: used only for debugging bool multiface, // in: if false, want only the best face int minwidth, // in: min face width as percentage of img width void* user, // in: unused (match virt func signature) cv::CascadeClassifier cascade) { (void) imgpath; (void) user; DetectFaces(detpars_, img, minwidth, cascade); DiscardMissizedFaces(detpars_); if (multiface) // order faces on increasing distance from left margin { sort(detpars_.begin(), detpars_.end(), IncreasingLeftMargin); } else { // order faces on decreasing width, keep only the first (the largest face) sort(detpars_.begin(), detpars_.end(), DecreasingWidth); if (NSIZE(detpars_)) detpars_.resize(1); } iface_ = 0; // next invocation of NextFace_ must get first face }
static void GetMagsAndOrients_GeneralCase( vec_double& mags, // out vec_double& orients, // out const int ix, // in: x coord of center of patch const int iy, // in: y coord of center of patch const int patchwidth, // in const MAT& magmat, // in const MAT& orientmat, // in const vec_double& pixelweights) // in { const int halfpatchwidth = (patchwidth-1) / 2; int ipix = 0; for (int x = iy - halfpatchwidth; x <= iy + halfpatchwidth; x++) { const double* const magbuf = Buf(magmat) + x * magmat.cols; const double* const orientbuf = Buf(orientmat) + x * orientmat.cols; for (int y = ix - halfpatchwidth; y <= ix + halfpatchwidth; y++) { if (x < 0 || x >= magmat.rows || y < 0 || y >= magmat.cols) { mags[ipix] = 0; // off image orients[ipix] = 0; } else // in image { mags[ipix] = pixelweights[ipix] * magbuf[y]; orients[ipix] = orientbuf[y]; } ipix++; } } CV_DbgAssert(ipix == NSIZE(mags)); }
void FaceDet::DetectFaces_( // call once per image to find all the faces const Image& img, // in: the image (grayscale) const char* imgpath, // in: used only for debugging bool multiface, // in: if false, want only the best face int minwidth, // in: min face width as percentage of img width void* user) // in: unused (match virt func signature) { CV_Assert(user == NULL); DetectFaces(detpars_, img, minwidth); char tracepath[SLEN]; sprintf(tracepath, "%s_00_unsortedfacedet.bmp", Base(imgpath)); TraceFaces(detpars_, img, tracepath); DiscardMissizedFaces(detpars_); if (multiface) // order faces on increasing distance from left margin { sort(detpars_.begin(), detpars_.end(), IncreasingLeftMargin); sprintf(tracepath, "%s_05_facedet.bmp", Base(imgpath)); TraceFaces(detpars_, img, tracepath); } else { // order faces on decreasing width, keep only the first (the largest face) sort(detpars_.begin(), detpars_.end(), DecreasingWidth); sprintf(tracepath, "%s_05_sortedfaces.bmp", Base(imgpath)); TraceFaces(detpars_, img, tracepath); if (NSIZE(detpars_)) detpars_.resize(1); } iface_ = 0; // next invocation of NextFace_ must get first face }
static Shape AlignMeanShapeToRightEyeAndMouth( const DetPar& detpar, // in const Shape& meanshape) // in { if (trace_g) lprintf("AlignToRightEyeAndMouth "); CV_Assert(NSIZE(meanshape) > 0 && PointUsed(meanshape, 0)); CV_Assert(!Valid(detpar.lex)); // left eye invalid? (else why are we here?) CV_Assert(Valid(detpar.rex)); // right eye valid? CV_Assert(Valid(detpar.mouthx)); // mouth valid? const double x_meanmouth = (meanshape(L_CTopOfTopLip, IX) + meanshape(L_CBotOfBotLip, IX)) / 2; const double y_meanmouth = (meanshape(L_CTopOfTopLip, IY) + meanshape(L_CBotOfBotLip, IY)) / 2; Shape meanline(2, 2), detline(2, 2); // line from eye to mouth meanline(0, IX) = meanshape(L_RPupil, IX); // right eye meanline(0, IY) = meanshape(L_RPupil, IY); meanline(1, IX) = x_meanmouth; // mouth meanline(1, IY) = y_meanmouth; detline(0, IX) = detpar.rex; // right eye detline(0, IY) = detpar.rey; detline(1, IX) = detpar.mouthx; // mouth detline(1, IY) = detpar.mouthy; return AlignShape(meanshape, AlignmentMat(meanline, detline)); }
static Shape AlignMeanShapeToBothEyesNoMouth( const DetPar& detpar, // in const Shape& meanshape) // in { if (trace_g) lprintf("AlignToBothEyesNoMouth "); CV_Assert(NSIZE(meanshape) > 0 && PointUsed(meanshape, 0)); CV_Assert(Valid(detpar.lex)); CV_Assert(Valid(detpar.rex)); Shape meanline(2, 2), detline(2, 2); // line from eye to eye meanline(0, IX) = meanshape(L_LPupil, IX); // left eye meanline(0, IY) = meanshape(L_LPupil, IY); meanline(1, IX) = meanshape(L_RPupil, IX); // right eye meanline(1, IY) = meanshape(L_RPupil, IY); detline(0, IX) = detpar.lex; // left eye detline(0, IY) = detpar.ley; detline(1, IX) = detpar.rex; // right eye detline(1, IY) = detpar.rey; return AlignShape(meanshape, AlignmentMat(meanline, detline)); }
static void TraceFaces( // write image showing detected face rects const vec_DetPar& detpars, // in const Image& img, // in const char* path) // in { #if TRACE_IMAGES // will be 0 unless debugging (defined in stasm.h) CImage cimg; cvtColor(img, cimg, CV_GRAY2BGR); // color image for (int iface = 0; iface < NSIZE(detpars); iface++) { const DetPar &detpar = detpars[iface]; rectangle(cimg, cv::Point(cvRound(detpar.x - detpar.width/2), cvRound(detpar.y - detpar.height/2)), cv::Point(cvRound(detpar.x + detpar.width/2), cvRound(detpar.y + detpar.height/2)), CV_RGB(255,255,0), 2); ImgPrintf(cimg, // 10 * iface to minimize overplotting detpar.x + 10 * iface, detpar.y, C_YELLOW, 1, ssprintf("%d", iface)); } lprintf("%s\n", path); if (!cv::imwrite(path, cimg)) Err("Cannot write %s", path); #endif }
static Shape AlignMeanShapeToBothEyesEstMouth( const DetPar& detpar, // in const Shape& meanshape) // in { // .48 was tested to give slightly better worse case results than .50 static double EYEMOUTH_TO_FACERECT_RATIO = .48; if (trace_g) lprintf("AlignToBothEyesNoMouth(EstMouth) "); CV_Assert(NSIZE(meanshape) > 0 && PointUsed(meanshape, 0)); CV_Assert(Valid(detpar.lex)); CV_Assert(Valid(detpar.rex)); // estimate the mouth's position double x_eyemid = 0; switch (detpar.eyaw) { case EYAW00: // mid point x_eyemid = .50 * detpar.lex + .50 * detpar.rex; break; // TODO The constants below have not been empirically optimized. case EYAW_45: // closer to left eye x_eyemid = .30 * detpar.lex + .70 * detpar.rex; break; case EYAW_22: // closer to left eye x_eyemid = .30 * detpar.lex + .70 * detpar.rex; break; case EYAW22: // closer to right eye x_eyemid = .30 * detpar.lex + .70 * detpar.rex; break; case EYAW45: // closer to right eye x_eyemid = .30 * detpar.lex + .70 * detpar.rex; break; default: Err("AlignMeanShapeToBothEyesEstMouth: Invalid eyaw %d", detpar.eyaw); break; } const double y_eyemid = (detpar.ley + detpar.rey) / 2; Shape mean_tri(3, 2), det_tri(3, 2); // triangle of eyes and mouth mean_tri(0, IX) = meanshape(L_LPupil, IX); // left eye mean_tri(0, IY) = meanshape(L_LPupil, IY); mean_tri(1, IX) = meanshape(L_RPupil, IX); // right eye mean_tri(1, IY) = meanshape(L_RPupil, IY); mean_tri(2, IX) = meanshape(L_CBotOfBotLip, IX); // mouth mean_tri(2, IY) = meanshape(L_CBotOfBotLip, IY); det_tri(0, IX) = detpar.lex; // left eye det_tri(0, IY) = detpar.ley; det_tri(1, IX) = detpar.rex; // right eye det_tri(1, IY) = detpar.rey; det_tri(2, IX) = x_eyemid; // mouth det_tri(2, IY) = y_eyemid + EYEMOUTH_TO_FACERECT_RATIO * detpar.width; return AlignShape(meanshape, AlignmentMat(mean_tri, det_tri)); }
bool NeedMouth( const vec_Mod& mods) // in: the ASM model(s) { for (int imod = 0; imod < NSIZE(mods); imod++) if (mods[imod]->Estart_() == ESTART_EYE_AND_MOUTH) return true; return false; }
void ShapeFile::Open_( // read shape file from disk const char* shapepath) // in { lprintf("Reading %s: ", shapepath); STRCPY(shapepath_, shapepath); FILE* file = fopen(shapepath, "rb"); if (!file) Err("Cannot open %s", shapepath); bool oldformat = false; Header(oldformat, shapepath, file); ImgDirs(dirs_, shapepath, file); shapes_.clear(); bases_.clear(); bits_.clear(); poses_.clear(); char base[SLEN]; unsigned bits; Shape shape; nchar_ = 0; int nrows = -1; while (ReadMat(base, bits, shape, file, shapepath)) { bool normalshape = true; if (oldformat) { if (bits & FA_Meta) // metashape? normalshape = false; } else if (bits & AT_Meta) // metashape? { normalshape = false; if (bits == AT_Pose) { CV_Assert(shape.rows == 1 && shape.cols == 4); poses_[base] = shape.clone(); } } if (normalshape) { // check that all shapes have same number of points if (nrows == -1) // first shape? nrows = shape.rows; else if (shape.rows != nrows) Err("%s has %d row%s but %s has %d row%s", base, shape.rows, plural(shape.rows), bases_[0].c_str(), nrows, plural(nrows)); shapes_.push_back(shape.clone()); bases_.push_back(base); bits_.push_back(bits); int len = STRNLEN(base, 100); if (len > nchar_) nchar_ = len; } } fclose(file); nshapes_ = NSIZE(shapes_); lprintf("%d shape%s\n", nshapes_, plural(nshapes_)); if (nshapes_ == 0) Err("No shapes in %s", shapepath); }
const DetPar FaceDet::NextFace_(void) { DetPar detpar; // detpar constructor sets all fields INVALID if (iface_ < NSIZE(detpars_)) detpar = detpars_[iface_++]; return detpar; }
static void CheckUnusedPoints( const TRAIN_DISTS& train_dists, // in: [ipoint] [idesc] distances from true landmark int nshapes) // in { for (int ipoint = 0; ipoint < NSIZE(train_dists); ipoint++) { if (NSIZE(train_dists[ipoint]) == 0) Err("no descriptors were generated for point %d", ipoint); if (NSIZE(train_dists[ipoint]) < nshapes) // see PointUsableForTraining { static int printed; PrintOnce(printed, "\n in point %d, only %d descriptors used from " "%d shapes because some points were skipped... ", ipoint, NSIZE(train_dists[ipoint]), nshapes); } } }
static MAT CalcShapeCov( const vec_Shape& shapes, // in const Shape& meanshape) // in { int npoints = meanshape.rows; int i, j; MAT cov(2 * npoints, 2 * npoints, 0.); // covariance of every x and y coord MAT nused(npoints, npoints, 0.); // Because of possible unused points, we have to iterate by // hand (we can't use standard matrix multiplies). for (int ishape = 0; ishape < NSIZE(shapes); ishape++) { Shape shape(shapes[ishape]); for (i = 0; i < npoints; i++) if (PointUsed(shape, i)) for (j = 0; j < npoints; j++) if (PointUsed(shape, j)) { cov(2*i, 2*j) += // x * x (shape(i, IX) - meanshape(i, IX)) * (shape(j, IX) - meanshape(j, IX)); cov(2*i+1, 2*j) += // y * x (shape(i, IY) - meanshape(i, IY)) * (shape(j, IX) - meanshape(j, IX)); cov(2*i, 2*j+1) += // x * y (shape(i, IX) - meanshape(i, IX)) * (shape(j, IY) - meanshape(j, IY)); cov(2*i+1, 2*j+1) += // y * y (shape(i, IY) - meanshape(i, IY)) * (shape(j, IY) - meanshape(j, IY)); nused(i, j)++; } } for (i = 0; i < npoints; i++) for (j = 0; j < npoints; j++) { const double n = nused(i, j); if (n < 3) // 3 is somewhat arb Err("Cannot calculate covariance of %g shape%s (need more shapes)", n, plural(int(n))); cov(2*i, 2*j) /= n; cov(2*i+1, 2*j) /= n; cov(2*i, 2*j+1) /= n; cov(2*i+1, 2*j+1) /= n; } return cov; }