-
-
Notifications
You must be signed in to change notification settings - Fork 309
/
flploc.go
178 lines (151 loc) · 5.45 KB
/
flploc.go
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
package main
import "C"
import (
"io/ioutil"
"log"
"runtime"
"unsafe"
pigo "github.com/esimov/pigo/core"
)
var (
cascade []byte
puplocCascade []byte
faceClassifier *pigo.Pigo
puplocClassifier *pigo.PuplocCascade
flpcs map[string][]*pigo.FlpCascade
imgParams *pigo.ImageParams
err error
)
var (
eyeCascades = []string{"lp46", "lp44", "lp42", "lp38", "lp312"}
mouthCascade = []string{"lp93", "lp84", "lp82", "lp81"}
)
func main() {}
//export FindFaces
func FindFaces(pixels []uint8) uintptr {
pointCh := make(chan uintptr)
results := clusterDetection(pixels, 480, 640)
dets := make([][]int, len(results))
for i := 0; i < len(results); i++ {
dets[i] = append(dets[i], results[i].Row, results[i].Col, results[i].Scale, int(results[i].Q), 0)
// left eye
puploc := &pigo.Puploc{
Row: results[i].Row - int(0.085*float32(results[i].Scale)),
Col: results[i].Col - int(0.185*float32(results[i].Scale)),
Scale: float32(results[i].Scale) * 0.4,
Perturbs: 63,
}
leftEye := puplocClassifier.RunDetector(*puploc, *imgParams, 0.0, false)
if leftEye.Row > 0 && leftEye.Col > 0 {
dets[i] = append(dets[i], leftEye.Row, leftEye.Col, int(leftEye.Scale), int(results[i].Q), 1)
}
// right eye
puploc = &pigo.Puploc{
Row: results[i].Row - int(0.085*float32(results[i].Scale)),
Col: results[i].Col + int(0.185*float32(results[i].Scale)),
Scale: float32(results[i].Scale) * 0.4,
Perturbs: 63,
}
rightEye := puplocClassifier.RunDetector(*puploc, *imgParams, 0.0, false)
if rightEye.Row > 0 && rightEye.Col > 0 {
dets[i] = append(dets[i], rightEye.Row, rightEye.Col, int(rightEye.Scale), int(results[i].Q), 1)
}
// Traverse all the eye cascades and run the detector on each of them.
for _, eye := range eyeCascades {
for _, flpc := range flpcs[eye] {
flp := flpc.GetLandmarkPoint(leftEye, rightEye, *imgParams, puploc.Perturbs, false)
if flp.Row > 0 && flp.Col > 0 {
dets[i] = append(dets[i], flp.Row, flp.Col, int(flp.Scale), int(results[i].Q), 2)
}
flp = flpc.GetLandmarkPoint(leftEye, rightEye, *imgParams, puploc.Perturbs, true)
if flp.Row > 0 && flp.Col > 0 {
dets[i] = append(dets[i], flp.Row, flp.Col, int(flp.Scale), int(results[i].Q), 2)
}
}
}
// Traverse all the mouth cascades and run the detector on each of them.
for _, mouth := range mouthCascade {
for _, flpc := range flpcs[mouth] {
flp := flpc.GetLandmarkPoint(leftEye, rightEye, *imgParams, puploc.Perturbs, false)
if flp.Row > 0 && flp.Col > 0 {
dets[i] = append(dets[i], flp.Row, flp.Col, int(flp.Scale), int(results[i].Q), 2)
}
}
}
flp := flpcs["lp84"][0].GetLandmarkPoint(leftEye, rightEye, *imgParams, puploc.Perturbs, true)
if flp.Row > 0 && flp.Col > 0 {
dets[i] = append(dets[i], flp.Row, flp.Col, int(flp.Scale), int(results[i].Q), 2)
}
}
coords := make([]int, 0, len(dets))
go func() {
// Since in Go we cannot transfer a 2d array through an array pointer
// we have to transform it into 1d array.
for _, v := range dets {
coords = append(coords, v...)
}
// Include as a first slice element the number of detected faces.
// We need to transfer this value in order to define the Python array buffer length.
coords = append([]int{len(dets), 0, 0, 0, 0}, coords...)
// Convert the slice into an array pointer.
s := *(*[]uint8)(unsafe.Pointer(&coords))
p := uintptr(unsafe.Pointer(&s[0]))
// Ensure `det` is not freed up by GC prematurely.
runtime.KeepAlive(coords)
// return the pointer address
pointCh <- p
}()
return <-pointCh
}
// clusterDetection runs Pigo face detector core methods
// and returns a cluster with the detected faces coordinates.
func clusterDetection(pixels []uint8, rows, cols int) []pigo.Detection {
imgParams = &pigo.ImageParams{
Pixels: pixels,
Rows: rows,
Cols: cols,
Dim: cols,
}
cParams := pigo.CascadeParams{
MinSize: 60,
MaxSize: 600,
ShiftFactor: 0.1,
ScaleFactor: 1.1,
ImageParams: *imgParams,
}
// Ensure that the face detection classifier is loaded only once.
if len(cascade) == 0 {
cascade, err = ioutil.ReadFile("../../cascade/facefinder")
if err != nil {
log.Fatalf("Error reading the cascade file: %v", err)
}
p := pigo.NewPigo()
// Unpack the binary file. This will return the number of cascade trees,
// the tree depth, the threshold and the prediction from tree's leaf nodes.
faceClassifier, err = p.Unpack(cascade)
if err != nil {
log.Fatalf("Error unpacking the cascade file: %s", err)
}
}
// Ensure that we load the pupil localization cascade only once
if len(puplocCascade) == 0 {
puplocCascade, err := ioutil.ReadFile("../../cascade/puploc")
if err != nil {
log.Fatalf("Error reading the puploc cascade file: %s", err)
}
puplocClassifier, err = puplocClassifier.UnpackCascade(puplocCascade)
if err != nil {
log.Fatalf("Error unpacking the puploc cascade file: %s", err)
}
flpcs, err = puplocClassifier.ReadCascadeDir("../../cascade/lps")
if err != nil {
log.Fatalf("Error unpacking the facial landmark detection cascades: %s", err)
}
}
// Run the classifier over the obtained leaf nodes and return the detection results.
// The result contains quadruplets representing the row, column, scale and detection score.
dets := faceClassifier.RunCascade(cParams, 0.0)
// Calculate the intersection over union (IoU) of two clusters.
dets = faceClassifier.ClusterDetections(dets, 0.0)
return dets
}