faceroi.cpp
7 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
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
// faceroi.cpp: face ROI, and translation from image frame to the ROI
//
// Copyright (C) 2005-2013, Stephen Milborrow
#include "stasm.h"
namespace stasm
{
// Rotations less than 5 are treated as zero to minimize image preprocessing.
static const double ROT_TREAT_AS_ZERO = 5;
//-----------------------------------------------------------------------------
// Return a rect which covers the face with enough space around it for an
// ASM search, but also ensuring that the rect is in the image boundaries.
static Rect RoiRect(
const DetPar& detpar, // in
int nimgcols, // in
int nimgrows, // in
bool flip, // in: mirror the ROI
double botfrac, // in: distance from center to bottom marg
double leftfrac, // in: dist from center to left marg
double topfrac, // in
double rightfrac) // in
{
int ixmin, ixmax;
if (flip)
{
ixmin = MAX(0, cvRound(detpar.x - rightfrac * detpar.width));
ixmax = MIN(nimgcols, cvRound(detpar.x + leftfrac * detpar.width));
}
else
{
ixmin = MAX(0, cvRound(detpar.x - leftfrac * detpar.width));
ixmax = MIN(nimgcols, cvRound(detpar.x + rightfrac * detpar.width));
}
const int iymin = MAX(0, cvRound(detpar.y - botfrac * detpar.height));
const int iymax = MIN(nimgrows, cvRound(detpar.y + topfrac * detpar.height));
Rect roi;
roi.x = ixmin;
roi.y = iymin;
roi.width = ixmax - ixmin;
roi.height = iymax - iymin;
CV_Assert(roi.width > 0);
CV_Assert(roi.height > 0);
return roi;
}
static bool IsRoiEntireImg(
const Rect& roi, // in
int imgcols, // in
int imgrows) // in
{
return roi.x == 0 &&
roi.y == 0 &&
roi.width == imgcols &&
roi.height == imgrows;
}
static DetPar ImgDetParToRoiFrame(
const DetPar& detpar, // in
const Rect& rect_roi) // in
{
DetPar detpar_roi(detpar);
detpar_roi.x -= rect_roi.x;
detpar_roi.y -= rect_roi.y;
Shape eyemouth_shape(5, 2, 0.);
if (Valid(detpar_roi.lex))
{
eyemouth_shape(0, IX) -= rect_roi.x;
eyemouth_shape(0, IY) -= rect_roi.y;
}
if (Valid(detpar_roi.rex))
{
eyemouth_shape(1, IX) -= rect_roi.x;
eyemouth_shape(1, IY) -= rect_roi.y;
}
if (Valid(detpar_roi.mouthx))
{
eyemouth_shape(2, IX) -= rect_roi.x;
eyemouth_shape(2, IY) -= rect_roi.y;
}
if (Valid(detpar.rot) && detpar.rot)
{
// rotate eyes and mouth
const MAT rotmat = getRotationMatrix2D(cv::Point2f(float(detpar_roi.x),
float(detpar_roi.y)),
-detpar.rot, 1.);
AlignShapeInPlace(eyemouth_shape, rotmat);
}
if (Valid(detpar.lex))
{
detpar_roi.lex = eyemouth_shape(0, IX);
detpar_roi.ley = eyemouth_shape(0, IY);
}
if (Valid(detpar.rex))
{
detpar_roi.rex = eyemouth_shape(1, IX);
detpar_roi.rey = eyemouth_shape(1, IY);
}
if (Valid(detpar.mouthx))
{
detpar_roi.mouthx = eyemouth_shape(2, IX);
detpar_roi.mouthy = eyemouth_shape(2, IY);
}
return detpar_roi;
}
Shape ImgShapeToRoiFrame( // return shape in ROI frame
const Shape& shape, // in: shape in image frame
const DetPar& detpar_roi, // in: detpar wrt the ROI
const DetPar& detpar) // in
{
Shape outshape(shape.clone());
for (int i = 0; i < outshape.rows; i++)
if (PointUsed(outshape, i))
{
outshape(i, IX) -= detpar.x - detpar_roi.x;
outshape(i, IY) -= detpar.y - detpar_roi.y;
}
if (Valid(detpar.rot) && detpar.rot)
{
const MAT rotmat = getRotationMatrix2D(cv::Point2f(float(detpar_roi.x),
float(detpar_roi.y)),
-detpar.rot,
1.);
outshape = AlignShape(outshape, rotmat);
}
return outshape;
}
// In StartShapeAndRoi we selected a ROI and possibly rotated that ROI.
// The search was done on that ROI. Now de-adjust the search results
// to undo the effects of searching on the ROI, not on the actual image.
Shape RoiShapeToImgFrame( // return shape in image frame
const Shape& shape, // in: shape in roi frame
const Image& face_roi, // in
const DetPar& detpar_roi, // in: detpar wrt the ROI
const DetPar& detpar) // in: detpar wrt the image
{
Shape outshape(shape.clone());
if (IsLeftFacing(detpar.eyaw))
outshape = FlipShape(outshape, face_roi.cols);
if (Valid(detpar.rot) && detpar.rot)
{
const MAT rotmat = getRotationMatrix2D(cv::Point2f(float(detpar_roi.x),
float(detpar_roi.y)),
detpar.rot, 1.);
outshape = AlignShape(outshape, rotmat);
}
for (int i = 0; i < outshape.rows; i++)
if (PointUsed(outshape, i))
{
outshape(i, IX) += detpar.x - detpar_roi.x;
outshape(i, IY) += detpar.y - detpar_roi.y;
}
return outshape;
}
void PossiblySetRotToZero( // this is to avoid rotating the image unnecessarily
double& rot) // io
{
if (rot >= -ROT_TREAT_AS_ZERO && rot <= ROT_TREAT_AS_ZERO)
rot = 0;
}
void FaceRoiAndDetPar( // get ROI around the face, rotate if necessary
Image& face_roi, // out
DetPar& detpar_roi, // out: detpar wrt the ROI
const Image& img, // in: original image
const DetPar& detpar, // in: wrt img frame, only x,y,w,h,rot used
bool flip, // in: mirror the ROI?
double botfrac, // in: default ROI_FRAC
double leftfrac, // in: dist from center to left margin
double topfrac, // in
double rightfrac) // in
{
Rect rect_roi = RoiRect(detpar, img.cols, img.rows, flip,
botfrac, leftfrac, topfrac, rightfrac);
detpar_roi = ImgDetParToRoiFrame(detpar, rect_roi);
// following "if"s are for efficiency (avoid rotation etc. when possible).
if (detpar.rot == 0 && IsRoiEntireImg(rect_roi, img.cols, img.rows))
face_roi = img;
else if (!Valid(detpar.rot) || detpar.rot == 0)
face_roi = Image(img, rect_roi);
else // rotate image so face is upright, results go into face_roi
warpAffine(Image(img, rect_roi), face_roi,
getRotationMatrix2D(cv::Point2f(float(detpar_roi.x),
float(detpar_roi.y)),
-detpar.rot, 1.),
cv::Size(face_roi.cols, face_roi.rows),
cv::INTER_AREA, cv::BORDER_REPLICATE);
// TODO For efficiency could combine this flip with above rot img when possible?
if (flip)
FlipImgInPlace(face_roi);
}
} // namespace stasm