-
Notifications
You must be signed in to change notification settings - Fork 874
/
Copy pathmain.go
252 lines (214 loc) · 6.15 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
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
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
// What it does:
//
// This example shows how to perform pose detection using models from OpenPose, an open source
// human body, hand, and facial keypoint detector.
//
// For more information about OpenPose, please go to:
// https://github.com/CMU-Perceptual-Computing-Lab/openpose
//
// Before using running this example, you'll need to download a pretrained Caffe model,
// and the respective prototxt config file.
//
// http://posefs1.perception.cs.cmu.edu/OpenPose/models/pose/coco/pose_iter_440000.caffemodel
// https://raw.githubusercontent.com/opencv/opencv_extra/master/testdata/dnn/openpose_pose_coco.prototxt
//
// You can also try the hand pose model:
// http://posefs1.perception.cs.cmu.edu/OpenPose/models/hand/pose_iter_102000.caffemodel
// https://raw.githubusercontent.com/CMU-Perceptual-Computing-Lab/openpose/master/models/hand/pose_deploy.prototxt
//
//
// How to run:
//
// go run ./cmd/dnn-pose-detection/main.go 0 ~/Downloads/pose_iter_440000.caffemodel ~/Downloads/openpose_pose_coco.prototxt openvino fp16
//
package main
import (
"fmt"
"image"
"image/color"
"os"
"gocv.io/x/gocv"
)
var net *gocv.Net
var images chan *gocv.Mat
var poses chan [][]image.Point
var pose [][]image.Point
func main() {
if len(os.Args) < 4 {
fmt.Println("How to run:\ndnn-pose-detection [videosource] [modelfile] [configfile] ([backend] [device])")
return
}
// parse args
deviceID := os.Args[1]
model := os.Args[2]
proto := os.Args[3]
backend := gocv.NetBackendDefault
if len(os.Args) > 4 {
backend = gocv.ParseNetBackend(os.Args[4])
}
target := gocv.NetTargetCPU
if len(os.Args) > 5 {
target = gocv.ParseNetTarget(os.Args[5])
}
// open capture device
webcam, err := gocv.OpenVideoCapture(deviceID)
if err != nil {
fmt.Printf("Error opening video capture device: %v\n", deviceID)
return
}
defer webcam.Close()
window := gocv.NewWindow("DNN Pose Detection")
defer window.Close()
img := gocv.NewMat()
defer img.Close()
// open OpenPose model
n := gocv.ReadNet(model, proto)
net = &n
if net.Empty() {
fmt.Printf("Error reading network model from : %v %v\n", model, proto)
return
}
defer net.Close()
net.SetPreferableBackend(gocv.NetBackendType(backend))
net.SetPreferableTarget(gocv.NetTargetType(target))
fmt.Printf("Start reading device: %v\n", deviceID)
images = make(chan *gocv.Mat, 1)
poses = make(chan [][]image.Point)
if ok := webcam.Read(&img); !ok {
fmt.Printf("Error cannot read device %v\n", deviceID)
return
}
processFrame(&img)
go performDetection()
for {
if ok := webcam.Read(&img); !ok {
fmt.Printf("Device closed: %v\n", deviceID)
return
}
if img.Empty() {
continue
}
select {
case pose = <-poses:
// we've received the next pose from channel, so send next image frame for detection
processFrame(&img)
default:
// show current frame without blocking, so do nothing here
}
drawPose(&img)
window.IMShow(img)
if window.WaitKey(1) >= 0 {
break
}
}
}
func processFrame(i *gocv.Mat) {
frame := gocv.NewMat()
i.CopyTo(&frame)
images <- &frame
}
// performDetection analyzes the results from the detector network.
// the result is an array of "heatmaps" which are the probability
// of a body part being in location x,y
func performDetection() {
for {
// get next frame from channel
frame := <-images
// convert image Mat to 368x368 blob that the pose detector can analyze
blob := gocv.BlobFromImage(*frame, 1.0/255.0, image.Pt(368, 368), gocv.NewScalar(0, 0, 0, 0), false, false)
// feed the blob into the detector
net.SetInput(blob, "")
// run a forward pass thru the network
prob := net.Forward("")
var midx int
s := prob.Size()
nparts, h, w := s[1], s[2], s[3]
// find out, which model we have
switch nparts {
case 19:
// COCO body
midx = 0
nparts = 18 // skip background
case 16:
// MPI body
midx = 1
nparts = 15 // skip background
case 22:
// hand
midx = 2
default:
fmt.Println("there should be 19 parts for the COCO model, 16 for MPI, or 22 for the hand model")
return
}
// find the most likely match for each part
pts := make([]image.Point, 22)
for i := 0; i < nparts; i++ {
pts[i] = image.Pt(-1, -1)
heatmap, _ := prob.FromPtr(h, w, gocv.MatTypeCV32F, 0, i)
_, maxVal, _, maxLoc := gocv.MinMaxLoc(heatmap)
if maxVal > 0.1 {
pts[i] = maxLoc
}
heatmap.Close()
}
// determine scale factor
sX := int(float32(frame.Cols()) / float32(w))
sY := int(float32(frame.Rows()) / float32(h))
// create the results array of pairs of points with the lines that best fit
// each body part, e.g.
// [[point A for body part 1, point B for body part 1],
// [point A for body part 2, point B for body part 2], ...]
results := [][]image.Point{}
for _, p := range PosePairs[midx] {
a := pts[p[0]]
b := pts[p[1]]
// high enough confidence in this pose?
if a.X <= 0 || a.Y <= 0 || b.X <= 0 || b.Y <= 0 {
continue
}
// scale to image size
a.X *= sX
a.Y *= sY
b.X *= sX
b.Y *= sY
results = append(results, []image.Point{a, b})
}
prob.Close()
blob.Close()
frame.Close()
// send pose results in channel
poses <- results
}
}
func drawPose(frame *gocv.Mat) {
for _, pts := range pose {
gocv.Line(frame, pts[0], pts[1], color.RGBA{0, 255, 0, 0}, 2)
gocv.Circle(frame, pts[0], 3, color.RGBA{0, 0, 200, 0}, -1)
gocv.Circle(frame, pts[1], 3, color.RGBA{0, 0, 200, 0}, -1)
}
}
// PosePairs is a table of the body part connections in the format [model_id][pair_id][from/to]
// For details please see:
// https://github.com/CMU-Perceptual-Computing-Lab/openpose/blob/master/doc/output.md
var PosePairs = [3][20][2]int{
{ // COCO body
{1, 2}, {1, 5}, {2, 3},
{3, 4}, {5, 6}, {6, 7},
{1, 8}, {8, 9}, {9, 10},
{1, 11}, {11, 12}, {12, 13},
{1, 0}, {0, 14},
{14, 16}, {0, 15}, {15, 17},
},
{ // MPI body
{0, 1}, {1, 2}, {2, 3},
{3, 4}, {1, 5}, {5, 6},
{6, 7}, {1, 14}, {14, 8}, {8, 9},
{9, 10}, {14, 11}, {11, 12}, {12, 13},
},
{ // hand
{0, 1}, {1, 2}, {2, 3}, {3, 4}, // thumb
{0, 5}, {5, 6}, {6, 7}, {7, 8}, // pinkie
{0, 9}, {9, 10}, {10, 11}, {11, 12}, // middle
{0, 13}, {13, 14}, {14, 15}, {15, 16}, // ring
{0, 17}, {17, 18}, {18, 19}, {19, 20}, // small
}}