Compare commits

...

2 Commits

Author SHA1 Message Date
Lukas Herman
dda8d2502f Use square instead 2020-11-05 21:03:22 -08:00
Lukas Herman
d593404e39 WIP 2020-11-04 22:34:17 -08:00

View File

@@ -1,20 +1,26 @@
package main package main
import ( import (
"fmt"
"image" "image"
"io/ioutil" "io/ioutil"
"log" "log"
"time" "net"
"os"
pigo "github.com/esimov/pigo/core" pigo "github.com/esimov/pigo/core"
"github.com/pion/mediadevices" "github.com/pion/mediadevices"
"github.com/pion/mediadevices/pkg/codec/vpx" // This is required to use h264 video encoder
_ "github.com/pion/mediadevices/pkg/driver/camera" // This is required to register camera adapter _ "github.com/pion/mediadevices/pkg/driver/camera" // This is required to register camera adapter
"github.com/pion/mediadevices/pkg/frame" "github.com/pion/mediadevices/pkg/frame"
"github.com/pion/mediadevices/pkg/io/video"
"github.com/pion/mediadevices/pkg/prop" "github.com/pion/mediadevices/pkg/prop"
) )
const ( const (
confidenceLevel = 5.0 confidenceLevel = 9.5
mtu = 1000
thickness = 5
) )
var ( var (
@@ -28,7 +34,7 @@ func must(err error) {
} }
} }
func detectFace(frame *image.YCbCr) bool { func detectFaces(frame *image.YCbCr) []pigo.Detection {
bounds := frame.Bounds() bounds := frame.Bounds()
cascadeParams := pigo.CascadeParams{ cascadeParams := pigo.CascadeParams{
MinSize: 100, MinSize: 100,
@@ -49,17 +55,77 @@ func detectFace(frame *image.YCbCr) bool {
// Calculate the intersection over union (IoU) of two clusters. // Calculate the intersection over union (IoU) of two clusters.
dets = classifier.ClusterDetections(dets, 0) dets = classifier.ClusterDetections(dets, 0)
return dets
}
func drawRect(frame *image.YCbCr, x0, y0, size int) {
if x0 < 0 {
x0 = 0
}
if y0 < 0 {
y0 = 0
}
width := frame.Bounds().Dx()
height := frame.Bounds().Dy()
x1 := x0 + size
y1 := y0 + size
if x1 >= width {
x1 = width - 1
}
if y1 >= height {
y1 = height - 1
}
convert := func(x, y int) int {
return y*width + x
}
for x := x0; x < x1; x++ {
for t := 0; t < thickness; t++ {
frame.Y[convert(x, y0+t)] = 0
frame.Y[convert(x, y1-t)] = 0
}
}
for y := y0; y < y1; y++ {
for t := 0; t < thickness; t++ {
frame.Y[convert(x0+t, y)] = 0
frame.Y[convert(x1-t, y)] = 0
}
}
}
func detectFace(r video.Reader) video.Reader {
return video.ReaderFunc(func() (img image.Image, release func(), err error) {
img, release, err = r.Read()
if err != nil {
return
}
yuv := img.(*image.YCbCr)
dets := detectFaces(yuv)
for _, det := range dets { for _, det := range dets {
if det.Q >= confidenceLevel { if det.Q < confidenceLevel {
return true continue
}
} }
return false drawRect(yuv, det.Col-det.Scale/2, det.Row-det.Scale/2, det.Scale)
}
return
})
} }
func main() { func main() {
if len(os.Args) != 2 {
fmt.Printf("usage: %s host:port\n", os.Args[0])
return
}
dest := os.Args[1]
// prepare face detector // prepare face detector
var err error var err error
cascade, err = ioutil.ReadFile("facefinder") cascade, err = ioutil.ReadFile("facefinder")
@@ -75,12 +141,21 @@ func main() {
log.Fatalf("Error unpacking the cascade file: %s", err) log.Fatalf("Error unpacking the cascade file: %s", err)
} }
vp8Params, err := vpx.NewVP8Params()
must(err)
vp8Params.BitRate = 1_000_000 // 100kbps
codecSelector := mediadevices.NewCodecSelector(
mediadevices.WithVideoEncoders(&vp8Params),
)
mediaStream, err := mediadevices.GetUserMedia(mediadevices.MediaStreamConstraints{ mediaStream, err := mediadevices.GetUserMedia(mediadevices.MediaStreamConstraints{
Video: func(c *mediadevices.MediaTrackConstraints) { Video: func(c *mediadevices.MediaTrackConstraints) {
c.FrameFormat = prop.FrameFormatExact(frame.FormatUYVY) c.FrameFormat = prop.FrameFormatExact(frame.FormatUYVY)
c.Width = prop.Int(640) c.Width = prop.Int(640)
c.Height = prop.Int(480) c.Height = prop.Int(480)
}, },
Codec: codecSelector,
}) })
must(err) must(err)
@@ -88,18 +163,27 @@ func main() {
videoTrack := mediaStream.GetVideoTracks()[0].(*mediadevices.VideoTrack) videoTrack := mediaStream.GetVideoTracks()[0].(*mediadevices.VideoTrack)
defer videoTrack.Close() defer videoTrack.Close()
videoReader := videoTrack.NewReader(false) videoTrack.Transform(detectFace)
// To save resources, we can simply use 4 fps to detect faces.
ticker := time.NewTicker(time.Millisecond * 250)
defer ticker.Stop()
for range ticker.C { rtpReader, err := videoTrack.NewRTPReader(vp8Params.RTPCodec().Name, mtu)
frame, release, err := videoReader.Read()
must(err) must(err)
// Since we asked the frame format to be exactly YUY2 in GetUserMedia, we can guarantee that it must be YCbCr addr, err := net.ResolveUDPAddr("udp", dest)
if detectFace(frame.(*image.YCbCr)) { must(err)
log.Println("Detect a face") conn, err := net.DialUDP("udp", nil, addr)
must(err)
buff := make([]byte, mtu)
for {
pkts, release, err := rtpReader.Read()
must(err)
for _, pkt := range pkts {
n, err := pkt.MarshalTo(buff)
must(err)
_, err = conn.Write(buff[:n])
must(err)
} }
release() release()