📄 prof.cpp
字号:
// $masm\prof.cpp 1.5 milbo$ functions for profiles around landmarks// Warning: this is raw research code -- expect it to be quite messy.// milbo durban jan06//-----------------------------------------------------------------------------// 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"bool fgExplicitPrevNext; // train:set from CONF_fGenExplicitPrevNext // search:read from ASM filedouble gNormalizedProfLen; // train:set from CONF_GenNormalizedProfLen // search:read from ASM file//-----------------------------------------------------------------------------// See header for PROF_ defines in prof.hppunsigned GetSubProfSpec (unsigned Prof, unsigned iSub){DASSERT(iSub < CONF_nMaxSubProfs);return (((Prof >> (iSub * 8)) & 0xff) | (Prof & 0xff000000));}//-----------------------------------------------------------------------------unsigned nSubProfsForProfSpec (unsigned ProfSpec){DASSERT(CONF_nMaxSubProfs <= 3); // this routine can count up to 3DASSERT((ProfSpec & PROF_2d) || (ProfSpec & 0x00ffff00) == 0); // 1d profiles can have only one subprofileunsigned nSubProfs = 1;if (ProfSpec & 0xff00) { nSubProfs++; if (ProfSpec & 0xff0000) nSubProfs++; }return nSubProfs;}//-----------------------------------------------------------------------------// This is only needed when GENERATING a model.// When USING the model (i.e. during search), use nGetProfWidth() instead.int nGenelemsPerSubProf (unsigned ProfSpec){if ((ProfSpec & PROF_2d) == 0) // one dimensional profile? return CONF_nProfWidth1d;return CONF_nProfWidth2d * CONF_nProfWidth2d;}//-----------------------------------------------------------------------------static void WriteMatAsBmp (const Mat &m, const char sPath[], bool fNormalizeBmp, bool fVerbose){Mat Norm;if (fNormalizeBmp) // normalize elems to 0..255? { double Max = m.maxElem(); double Min = m.minElem(); Norm = (m - Min) * 255.0 / (Max - Min); }else Norm = m;Image Img(m.ncols(), m.nrows());for (int iRow = 0; iRow < m.nrows(); iRow++) for (int iCol = 0; iCol < m.ncols(); iCol++) Img(iCol, iRow) = Norm(iRow, iCol);WriteBmp(Img, sPath, fVerbose);}//-----------------------------------------------------------------------------// Print the matrix, laid out in the same way as an image// i.e. top left corner of print is top left corner of imagevoid PrintBmpAsMat (const Image &Img, const char sMsg[], const char sFormat[]){Mat m(Img.height, Img.width);for (int iRow = 0; iRow < m.nrows(); iRow++) for (int iCol = 0; iCol < m.ncols(); iCol++) m(iRow, iCol) = Img(iCol, iRow);if (sFormat == NULL) sFormat = "%3.0f ";m.print(sMsg, sFormat);}//-----------------------------------------------------------------------------void Write2dProfAsBmp (const Vec &Prof, char sPath[], bool fNormalizeBmp, bool fVerbose){MatView Square(Prof.viewAsSquare());Mat Norm;if (fNormalizeBmp) // normalize elems to 0..255? { double Max = Square.maxElem(); double Min = Square.minElem(); if (Max - Min == 0) Norm.dimClear(Square.nrows(), Square.ncols()); else Norm = (Square - Min) * 255.0 / (Max - Min); }else Norm = Square;Image Img(Square.ncols(), Square.nrows());for (int iRow = 0; iRow < Square.nrows(); iRow++) for (int iCol = 0; iCol < Square.ncols(); iCol++) { int iPix = Norm(iRow, iCol); DASSERT(iPix >= 0 && iPix < 256); Img(iCol, iRow) = iPix; }WriteBmp(Img, sPath, fVerbose);}//-----------------------------------------------------------------------------// Debugging stuffconst char *pgCurImageName; // for debugging it is useful to know which is the current imageint igCurShape;bool fgStepSizeErr;// just a little hack for finding out which shapes are causing pixel out-of-range errors#if CONF_fFindOffRangePixelsstatic bool fgPixelOffRange[CONF_nMaxImages];static void GetPixelFail (int ix, int iy){DASSERT(igCurShape < CONF_nMaxImages);if (pgCurImageName) { if (!fgPixelOffRange[igCurShape]) { fgPixelOffRange[igCurShape] = true; Warn("Offrange %s %dx%d", pgCurImageName, ix, iy); } }}#endif//-----------------------------------------------------------------------------// Get pixel in Img, ix and iy in SHAPE coords, with range checking.//// SHAPE coords: 0,0 is center of image,// image spans -width/2 to width/2, -height/2 to height/2//// Returns 255 if pixel is white; 0 if pixel is black or out-of-rangestatic int iGetPixel (const Image &Img, int ix, int iy){int width = Img.width, height = Img.height;ix += width/2; // convert SHAPE to Image coordsiy += height/2;#if CONF_fFindOffRangePixelsif (ix < 0 || ix > width-1 || iy < 0 || iy > height-1) { GetPixelFail(ix, iy);#if CONF_fPrintOffRangePixels Warn("GetPix %dx%d", ix, iy);#endif }#endifif (ix < 0) ix = 0;if (ix > width-1) ix = width-1;if (iy < 0) iy = 0;if (iy > height-1) iy = height-1;int iPix = Img(ix, (height - 1 - iy)); // height-1-iy min is 0 when iy at max=height-1#if CONF_fFindOffRangePixels// This code is used to help decide which images are unsuitable for training because// the face is too close to the border. There has to be a margin around the face// so the whiskers don't go off the image.//// If the lprintf below occurs during training, we know that the training face is too// near the border.//// LoadAndTranslateImage can leave black borders around the image (if fTranslate is true).// Here we check to see if we are on a black border. This will also (incorrectly) pick// up correct black pixels in the image, so we filter (most) of those out by checking for// three black pixels in a row, and by making sure the pixel is near the edge of the image.static int iLastPix1 = 255;static int iLastPix2 = 255;if (iPix == 0 && iLastPix1 == 0 && iLastPix2 == 0 && (ix < width/4 || ix > 3 * width/4) && (iy < height/4 || ix > 3 * height/4)) { Warn("GetPix zero %s %dx%d", pgCurImageName, ix, iy); }iLastPix2 = iLastPix1;iLastPix1 = iPix;#endifreturn iPix;}//-----------------------------------------------------------------------------// Same as iGetPixel for Image, but here the image is represented by a Mat// We have to swap ix,iy at the right place.static double GetPixel (const Mat &m, int ix, int iy){int width = m.ncols(), height = m.nrows();ix += width/2; // convert SHAPE to Image coordsiy += height/2;#if CONF_fFindOffRangePixelsif (ix < 0 || ix > width-1 || iy < 0 || iy > height-1) { GetPixelFail(ix, iy);#if CONF_fPrintOffRangePixels Warn("GetPix_%dx%d", ix, iy);#endif }#endifif (ix < 0) ix = 0;if (ix > width-1) ix = width-1;if (iy < 0) iy = 0;if (iy > height-1) iy = height-1;double Pix = m(height - 1 - iy, ix); // x,y back to front to iGetPixel(Image)#if CONF_fFindOffRangePixels// This code is used to help decide which images are unsuitable for training because// the face is too close to the border. There has to be a margin around the face// so the whiskers don't go off the image.//// If the lprintf below occurs during training, we know that the training face is too// near the border.//// LoadAndTranslateImage can leave black borders around the image (if fTranslate is true).// Here we check to see if we are on a black border. This will also (incorrectly) pick// up correct black pixels in the image, so we filter (most) of those out by checking for// three black pixels in a row, and by making sure the pixel is near the edge of the image.static double LastPix1 = 255;static double LastPix2 = 255;if (Pix == 0 && LastPix1 == 0 && LastPix2 == 0 && (ix < width/4 || ix > 3 * width/4) && (iy < height/4 || ix > 3 * height/4)) { Warn("GetPix zero %s %dx%d", pgCurImageName, ix, iy); }LastPix2 = LastPix1;LastPix1 = Pix;#endifreturn Pix;}//-----------------------------------------------------------------------------// AlignedAvShape is used only if there are missing iPrev and iNext landmarks in Shape// and fgExplicitPrevNext is true.// In such a case, we approximate the position of iPrev and iNext from AlignedAvShape//// ProfAngle is the angle of the whisker relative to the orthogonal. Make 0 to get// a conventional orthogonal whisker.//// *pDeltaX is along whisker, *pDeltaY is orthogonal to whiskervoid GetProfStepSize (double *pDeltaX, double *pDeltaY, // out const SHAPE &Shape, int iPoint, double ProfAngle, const tLand Lands[], const SHAPE &AlignedAvShape) // in{DASSERT(fPointUsed(Shape, iPoint));int nPoints = Shape.nrows();// Use Lands prev and next points if Lands explictly specifies them.//// If these are unused on Shape, get the best approximation for them from AlignedAvShape.// This can only happen if not all landmarks are used in Shape (e.g. a BioId or AR shape).//// If Lands doesn't explicitly specify prev and next landmarks, use the// one-less and one-more index in Shape to select prev and next landmarks.// This is the classic Cootes method.int iPrev, iNext;static Vec PrevRow; PrevRow.dim(Shape.ncols(), ROWVEC);static Vec NextRow; NextRow.dim(Shape.ncols(), ROWVEC);if (fgExplicitPrevNext) { iPrev = Lands[iPoint].iPrev; if (iPrev < 0) iPrev = (iPoint + nPoints - 1) % nPoints; PrevRow = Shape.row(iPrev); if (!fPointUsed(Shape, iPrev)) { DASSERT(fPointUsed(AlignedAvShape, iPrev)); PrevRow = AlignedAvShape.row(iPrev); } iNext = Lands[iPoint].iNext; if (iNext < 0) iNext = (iPoint + 1) % nPoints; NextRow = Shape.row(iNext); if (!fPointUsed(Shape, iNext)) { DASSERT(fPointUsed(AlignedAvShape, iNext)); NextRow = AlignedAvShape.row(iNext); } }else // classic Cootes method, but skip over unused points in Shape { iPrev = iPoint; do { if (--iPrev < 0) iPrev = nPoints-1; } while (!fPointUsed(Shape, iPrev)); PrevRow = Shape.row(iPrev); iNext = iPoint; do { if (++iNext >= nPoints) iNext = 0; } while (!fPointUsed(Shape, iNext)); NextRow = Shape.row(iNext); }static Vec Whisker;Whisker.assign(GetBisector(PrevRow, Shape.row(iPoint), NextRow, ProfAngle));*pDeltaX = Whisker(VX);*pDeltaY = Whisker(VY);// Normalise so we take single pixel steps.// After this either *pDeltaX or *pDeltaY will be +-1, and the other will be less than 1.double AbsDeltaX = fabs(*pDeltaX);double AbsDeltaY = fabs(*pDeltaY);if (AbsDeltaX < 1e-10 && AbsDeltaY < 1e-10) // I fixed this in GetBisector, but left check in { static bool fWarned = false; if (!fWarned) { Warn("StepSizeErr %s %d %d", pgCurImageName, igCurShape, iPoint); fWarned = true; } return; }if (AbsDeltaX >= AbsDeltaY) { *pDeltaY /= AbsDeltaX; *pDeltaX /= AbsDeltaX; }else { *pDeltaX /= AbsDeltaY; *pDeltaY /= AbsDeltaY; }}//-----------------------------------------------------------------------------// Variables for PrepareProf and GetProf routines// Max number of elems in a profile including nPixSearch on each end// If this is too small you'll get a SysErr, it won't silently failstatic const CONF_nMaxProfWidth1D = 50;static Vec gProf(CONF_nMaxProfWidth1D, CONF_nMaxProfWidth1D);static int ngProfWidth;static int igProfPoint; // for sanity checkingstatic const byte *pgProfImage; // dittostatic unsigned gSubProfSpec; // ditto#if CONF_fRawProfsstatic Vec gRawProf(CONF_nMaxProfWidth1D, CONF_nMaxProfWidth1D);static FILE *pgRawProfFile;#endif//-----------------------------------------------------------------------------// If you want multiple profiles along one whisker, then
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -