-
Notifications
You must be signed in to change notification settings - Fork 0
/
detector.go
146 lines (120 loc) · 3.19 KB
/
detector.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
package mtcnn
import (
"bytes"
_ "embed"
"encoding/binary"
"image"
"io"
"math"
)
type FaceData struct {
Box image.Rectangle
Confidence float32
LeftEye, RightEye, Nose, MouthLeft, MouthRight image.Point
}
type DetectorOptions struct {
MinFaceSize int
StageThresholds [3]float32
ScaleFactor float32
}
var defaultDetectorOptions = DetectorOptions{
MinFaceSize: 20,
StageThresholds: [3]float32{0.6, 0.6, 0.7},
ScaleFactor: 0.709,
}
type Detector struct {
pnet, rnet, onet *Model
options *DetectorOptions
}
func NewDetector(pnetWeights, rnetWeights, onetWeights [][]float32, options *DetectorOptions) *Detector {
if options == nil {
options = &defaultDetectorOptions
}
return &Detector{
pnet: getPNet(pnetWeights),
rnet: getRNet(rnetWeights),
onet: getONet(onetWeights),
options: options,
}
}
func getScalePyramid(m, minLayer, scaleFactor float32) []float32 {
var scales []float32
var currentScale float32 = 1
for minLayer >= 12 {
scales = append(scales, m*currentScale)
currentScale *= scaleFactor
minLayer *= scaleFactor
}
return scales
}
func (d *Detector) DetectFaces(img image.Image) []FaceData {
width, height := img.Bounds().Dx(), img.Bounds().Dy()
m := 12 / float32(d.options.MinFaceSize)
minLayer := float32(width) * m
if height < width {
minLayer = float32(height) * m
}
scales := getScalePyramid(m, minLayer, d.options.ScaleFactor)
_ = scales
boundingBoxes := stage1(d.pnet, img, scales, d.options.StageThresholds[0])
boundingBoxes = stage2(d.rnet, img, boundingBoxes, d.options.StageThresholds[1])
return stage3(d.onet, img, boundingBoxes, d.options.StageThresholds[2])
}
func ReadWeightsFromBin(r io.Reader) (weights [][]float32, err error) {
buf := make([]byte, 0, 1024*32)
bo := binary.LittleEndian
buf = buf[:4]
_, err = r.Read(buf)
if err != nil {
return
}
weights = make([][]float32, bo.Uint32(buf))
if len(weights) == 0 {
return
}
for i := 0; i < len(weights); i++ {
buf = buf[:4]
_, err = r.Read(buf)
if err != nil {
return
}
weights[i] = make([]float32, bo.Uint32(buf))
toRead, read := len(weights[i]), 0
for read < toRead {
leftToRead := toRead - read
if 4*leftToRead < cap(buf) {
buf = buf[:4*leftToRead]
} else {
buf = buf[:cap(buf)]
}
_, err = r.Read(buf)
if err != nil {
return
}
for j := 0; j < len(buf)/4; j++ {
weights[i][read+j] = math.Float32frombits(bo.Uint32(buf[4*j : 4*(j+1)]))
}
read += len(buf) / 4
}
}
return
}
var (
//go:embed weights/pnet_weights.bin
pnetWeightsFile []byte
//go:embed weights/rnet_weights.bin
rnetWeightsFile []byte
//go:embed weights/onet_weights.bin
onetWeightsFile []byte
)
var defaultDetector *Detector
var DefaultPnetWeights, DefaultRnetWeights, DefaultOnetWeights [][]float32
func init() {
DefaultPnetWeights, _ = ReadWeightsFromBin(bytes.NewReader(pnetWeightsFile))
DefaultRnetWeights, _ = ReadWeightsFromBin(bytes.NewReader(rnetWeightsFile))
DefaultOnetWeights, _ = ReadWeightsFromBin(bytes.NewReader(onetWeightsFile))
defaultDetector = NewDetector(DefaultPnetWeights, DefaultRnetWeights, DefaultOnetWeights, nil)
}
func DetectFaces(img image.Image) []FaceData {
return defaultDetector.DetectFaces(img)
}