shapemod.cpp
4.54 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
// shapemod.cpp: the ASM shape model
//
// Copyright (C) 2005-2013, Stephen Milborrow
#include "shapemod.h"
#include "landmarks.h"
#include "asm.h"
namespace stasm
{
static const int SHAPEHACK_MINPYRLEV = 2; // allow hacks only at coarse pyr levs
// Limit the values of b to make sure the generated shape is plausible.
// That is, clip each b[i] to bmax * sqrt(lambda_i).
// "b" is the name used for the eigenvector weights in Cootes' papers.
static void LimitB(
VEC& b, // io: eigvec weights
const VEC& eigvals, // in
double bmax) // in
{
for (int i = 0; i < NSIZE(eigvals); i++)
{
const double limit = bmax * sqrt(eigvals(i));
b(i) = Clamp(b(i), -limit, limit);
}
}
// This implements Section 4.8 of CootesTaylor 2004
// www.isbe.man.ac.uk/~bim/Mods/app_models.pdf.
// Except that we don't implement tangent spaces. And we don't iterate the
// shape model until convergence. Instead we use the b from the previous
// iteration of the ASM, which gives as good landmark fit results, empirically.
static Shape ConformShapeToMod( // Return a copy of inshape conformed to the model
VEC& b, // io: eigvec weights
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 = AlignShape(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 AlignShape(meanshape + conformedshape, pose);
}
static VEC PointWeights(void) // return point weights from LANDMARK_INFO_TAB
{
CV_DbgAssert(NELEMS(LANDMARK_INFO_TAB) == stasm_NLANDMARKS);
VEC pointweights(stasm_NLANDMARKS, 1);
for (int i = 0; i < stasm_NLANDMARKS; i++)
pointweights(i) = LANDMARK_INFO_TAB[i].weight;
return pointweights;
}
// wrapper around ConformShapeToMod above
const Shape ShapeMod::ConformShapeToMod_( // return shape conformed to shape model
VEC& b, // io: eigvec weights from previous iters of ASM
const Shape& shape, // in: shape suggested by the descriptor models
int ilev) // in: pyramid level (0 is full size)
const
{
// static for efficiency (init once)
static const VEC pointweights(PointWeights());
Shape newshape = ConformShapeToMod(b,
shape, meanshape_ * GetPyrScale(ilev),
eigvals_ / pow(SQ(PYR_RATIO), ilev), eigvecs_, eigvecsi_,
bmax_, pointweights);
newshape = JitterPointsAt00(newshape); // jitter points at 0,0 if any
if (ilev >= SHAPEHACK_MINPYRLEV) // allow shape hacks only at coarse pyr levs
ApplyShapeModelHacks(newshape, hackbits_);
return newshape;
}
// Like ConformShapeToMod_ but with pinned landmarks. Conform the given shape to
// the ASM model, but keeping points in pinnedshape at their original position.
const Shape ShapeMod::ConformShapeToMod_Pinned_(
VEC& b, // io: eigvec weights from previous iters of ASM
const Shape& shape, // in: shape suggested by the descriptor models
int ilev, // in: pyramid level (0 is full size)
const Shape& pinnedshape) // in: pinned landmarks
const
{
static const double MAX_DIST = 0.5;
static const int MAX_ITERS = 50;
Shape outshape(shape.clone());
double dist = FLT_MAX;
for (int iter = 0; dist > MAX_DIST && iter < MAX_ITERS; iter++)
{
outshape = ConformShapeToMod_(b, outshape, ilev);
dist = ForcePinnedPoints(outshape, pinnedshape);
}
return outshape;
}
} // namespace stasm