forked from IntelRealSense/librealsense
-
Notifications
You must be signed in to change notification settings - Fork 0
/
validate_face.h
122 lines (101 loc) · 4.39 KB
/
validate_face.h
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
// License: Apache 2.0. See LICENSE file in root directory.
// Copyright(c) 2019 Intel Corporation. All Rights Reserved.
#pragma once
#include <librealsense2/rs.hpp> // Include RealSense Cross Platform API
#include <dlib/image_processing/full_object_detection.h>
#include "markup_68.h"
/*
Calculates the average depth for a range of two-dimentional points in face, such that:
point(n) = face.part(n)
and puts the result in *p_average_depth.
Points for which no depth is available (is 0) are ignored and not factored into the average.
Returns true if an average is available (at least one point has depth); false otherwise.
*/
bool find_depth_from(
rs2::depth_frame const & frame,
float const depth_scale,
dlib::full_object_detection const & face,
markup_68 markup_from, markup_68 markup_to,
float * p_average_depth
)
{
uint16_t const * data = reinterpret_cast<uint16_t const *>(frame.get_data());
float average_depth = 0;
size_t n_points = 0;
for( int i = markup_from; i <= markup_to; ++i )
{
auto pt = face.part( i );
auto depth_in_pixels = *(data + pt.y() * frame.get_width() + pt.x());
if( !depth_in_pixels )
continue;
average_depth += depth_in_pixels * depth_scale;
++n_points;
}
if( !n_points )
return false;
if( p_average_depth )
*p_average_depth = average_depth / n_points;
return true;
}
/*
Returns whether the given 68-point facial landmarks denote the face of a real
person (and not a picture of one), using the depth data in depth_frame.
See markup_68 for an explanation of the point topology.
NOTE: requires the coordinates in face align with those of the depth frame.
*/
bool validate_face(
rs2::depth_frame const & frame,
float const depth_scale,
dlib::full_object_detection const & face
)
{
// Collect all the depth information for the different facial parts
// For the ears, only one may be visible -- we take the closer one!
float left_ear_depth = 100, right_ear_depth = 100;
if( !find_depth_from( frame, depth_scale, face, markup_68::RIGHT_EAR, markup_68::RIGHT_1, &right_ear_depth )
&& !find_depth_from( frame, depth_scale, face, markup_68::LEFT_1, markup_68::LEFT_EAR, &left_ear_depth ) )
return false;
float ear_depth = std::min( right_ear_depth, left_ear_depth );
float chin_depth;
if( !find_depth_from( frame, depth_scale, face, markup_68::CHIN_FROM, markup_68::CHIN_TO, &chin_depth ) )
return false;
float nose_depth;
if( !find_depth_from( frame, depth_scale, face, markup_68::NOSE_RIDGE_2, markup_68::NOSE_TIP, &nose_depth ) )
return false;
float right_eye_depth;
if( !find_depth_from( frame, depth_scale, face, markup_68::RIGHT_EYE_FROM, markup_68::RIGHT_EYE_TO, &right_eye_depth ) )
return false;
float left_eye_depth;
if( !find_depth_from( frame, depth_scale, face, markup_68::LEFT_EYE_FROM, markup_68::LEFT_EYE_TO, &left_eye_depth ) )
return false;
float eye_depth = std::min( left_eye_depth, right_eye_depth );
float mouth_depth;
if( !find_depth_from( frame, depth_scale, face, markup_68::MOUTH_OUTER_FROM, markup_68::MOUTH_INNER_TO, &mouth_depth ) )
return false;
// We just use simple heuristics to determine whether the depth information agrees with
// what's expected: that the nose tip, for example, should be closer to the camera than
// the eyes.
// These heuristics are fairly basic but nonetheless serve to illustrate the point that
// depth data can effectively be used to distinguish between a person and a picture of a
// person...
if( nose_depth >= eye_depth )
return false;
if( eye_depth - nose_depth > 0.07f )
return false;
if( ear_depth <= eye_depth )
return false;
if( mouth_depth <= nose_depth )
return false;
if( mouth_depth > chin_depth )
return false;
// All the distances, collectively, should not span a range that makes no sense. I.e.,
// if the face accounts for more than 20cm of depth, or less than 2cm, then something's
// not kosher!
float x = std::max( { nose_depth, eye_depth, ear_depth, mouth_depth, chin_depth } );
float n = std::min( { nose_depth, eye_depth, ear_depth, mouth_depth, chin_depth } );
if( x - n > 0.20f )
return false;
if( x - n < 0.02f )
return false;
return true;
}