-
-
Notifications
You must be signed in to change notification settings - Fork 0
/
main.go
182 lines (152 loc) · 4.33 KB
/
main.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
179
180
181
182
// waybar-eyes based on face detection
package main
import (
"flag"
"fmt"
"image"
"image/color"
"os"
"strconv"
"time"
"github.com/cyrinux/waybar-eyes/eyes"
"gocv.io/x/gocv"
)
// SleepTimeOnPresence is the sleep time
// when a face is detected
const SleepTimeOnPresence = 60 * time.Second
// SleepTimeOnAbsence is the sleep time
// when no face is detected
const SleepTimeOnAbsence = 30 * time.Second
// NewEyeTimeRate is the time rate required
// to add a new eye in the output
const NewEyeTimeRate = 15 * time.Minute
// Version give the software version
var Version string
// XMLFile is the detection model use
var XMLFile = "haarcascade_frontalface_default.xml"
// Config is config struct
type Config struct {
Debug bool
Display bool
Device int
Model string
}
func main() {
// parse params
config := Config{Debug: false, Display: false, Device: 0, Model: XMLFile}
flag.BoolVar(&config.Display, "display", config.Display, "Display mode")
flag.BoolVar(&config.Debug, "debug", config.Debug, "Debug mode")
flag.IntVar(&config.Device, "d", config.Device, "Video device id, default: 0.")
flag.StringVar(&config.Model, "m", config.Model, "Detection model path")
flag.Parse()
// get debug mode
envDebug, _ := strconv.ParseBool(os.Getenv("DEBUG"))
if envDebug {
config.Debug = envDebug
}
// If the app boot, we will skip the first loop
// but if in debug, we will probe directly
boot := !config.Debug
// init waybar output
var previousEyes eyes.Eyes
// main loop here
e := eyes.New(config.Debug)
// handle SIGUSR1 to reset count
go e.SignalHandler()
lastEyeTS := time.Now()
for {
// prevent poll on boot
if boot {
boot = false
time.Sleep(SleepTimeOnPresence)
continue
}
// increase based on face detected or not
faces, detected := detectFace(config.Device, config.Model, 2, config.Debug, config.Display)
e.Faces = faces
if detected && e.Count < eyes.MaxEyes && time.Since(lastEyeTS) > NewEyeTimeRate {
e.Count++
// keep timestamp of the last eye added
lastEyeTS = time.Now()
} else if !detected && e.Count > 0 {
// decrease if no face detected
// and count > 0
e.Count--
}
// get formatted output
e.PrepareWaybarOutput()
jsonOutput := e.GetJSONOutput()
// write the output in JSON cache file
if e != previousEyes {
e.WriteJSONOutput(jsonOutput)
}
previousEyes = e
// sleep based on the eyes number
// we want to quickly decrease the eyes
// number if absence detected
if !detected && e.Count > 0 {
time.Sleep(SleepTimeOnAbsence)
} else {
// and the default sleep time
time.Sleep(SleepTimeOnPresence)
}
}
}
// detectFace try to detect a face
func detectFace(deviceID int, xmlFile string, retryTime int, debug bool, display bool) (int, bool) {
// open webcam
webcam, err := gocv.VideoCaptureDevice(int(deviceID))
if err != nil {
fmt.Println(err)
webcam.Close()
return 0, false
}
defer webcam.Close()
// prepare image matrix
img := gocv.NewMat()
defer img.Close()
// load classifier to recognize faces
classifier := gocv.NewCascadeClassifier()
defer classifier.Close()
if !classifier.Load(xmlFile) {
fmt.Printf("Error reading cascade file: %v\n", xmlFile)
return 0, false
}
// loop to detect faces, we loop retryTime time
for range make([]int, retryTime) {
if ok := webcam.Read(&img); !ok {
fmt.Printf("cannot read device %d\n", deviceID)
break
}
if img.Empty() {
fmt.Printf("img empty %d\n", deviceID)
break
}
// detect faces
rects := classifier.DetectMultiScale(img)
// display face detection result for debugging
if debug && display {
// window to show detected face
window := gocv.NewWindow("Detect faces")
defer window.Close()
// color for the rect when faces detected
blue := color.RGBA{0, 0, 255, 0}
// draw a rectangle around each face on the original image,
// along with text identifying as "Human"
for _, r := range rects {
gocv.Rectangle(&img, r, blue, 3)
size := gocv.GetTextSize("Face", gocv.FontHersheyPlain, 1.2, 2)
pt := image.Pt(r.Min.X+(r.Min.X/2)-(size.X/2), r.Min.Y-2)
gocv.PutText(&img, "Face", pt, gocv.FontHersheyPlain, 1.2, blue, 2)
}
// show the image in the window, and wait 1 millisecond
window.IMShow(img)
window.WaitKey(500)
}
time.Sleep(2000 * time.Millisecond)
if len(rects) > 0 {
return len(rects), len(rects) > 0
}
}
return 0, false
}