Refactor detector function

This commit is contained in:
esimov
2019-11-11 16:56:50 +02:00
parent ef71fb276f
commit 738690cde7
3 changed files with 95 additions and 64 deletions

View File

@@ -2,6 +2,7 @@ package canvas
import ( import (
"fmt" "fmt"
"math"
"syscall/js" "syscall/js"
"github.com/esimov/pigo/wasm/detector" "github.com/esimov/pigo/wasm/detector"
@@ -17,7 +18,7 @@ type Canvas struct {
window js.Value window js.Value
doc js.Value doc js.Value
body js.Value body js.Value
windowSize struct{ width, height float64 } windowSize struct{ width, height int }
// Canvas properties // Canvas properties
canvas js.Value canvas js.Value
@@ -37,8 +38,8 @@ func NewCanvas() *Canvas {
c.doc = c.window.Get("document") c.doc = c.window.Get("document")
c.body = c.doc.Get("body") c.body = c.doc.Get("body")
c.windowSize.width = c.window.Get("innerWidth").Float() c.windowSize.width = c.window.Get("innerWidth").Int()
c.windowSize.height = c.window.Get("innerHeight").Float() c.windowSize.height = c.window.Get("innerHeight").Int()
c.canvas = c.doc.Call("createElement", "canvas") c.canvas = c.doc.Call("createElement", "canvas")
c.canvas.Set("width", c.windowSize.width) c.canvas.Set("width", c.windowSize.width)
@@ -52,27 +53,31 @@ func NewCanvas() *Canvas {
// Render calls the `requestAnimationFrame` Javascript function in asynchronous mode. // Render calls the `requestAnimationFrame` Javascript function in asynchronous mode.
func (c *Canvas) Render() { func (c *Canvas) Render() {
var data = make([]byte, int(c.windowSize.width*c.windowSize.height*4)) var data = make([]byte, c.windowSize.width*c.windowSize.height*4)
c.done = make(chan struct{}) c.done = make(chan struct{})
det := detector.NewDetector() det := detector.NewDetector()
c.renderer = js.FuncOf(func(this js.Value, args []js.Value) interface{} { if err := det.UnpackCascades(); err == nil {
go func() { c.renderer = js.FuncOf(func(this js.Value, args []js.Value) interface{} {
c.reqID = c.window.Call("requestAnimationFrame", c.renderer) go func() {
// Draw the webcam frame to the canvas element c.reqID = c.window.Call("requestAnimationFrame", c.renderer)
c.ctx.Call("drawImage", c.video, 0, 0) // Draw the webcam frame to the canvas element
rgba := c.ctx.Call("getImageData", 0, 0, c.windowSize.width, c.windowSize.height).Get("data") c.ctx.Call("drawImage", c.video, 0, 0)
c.Log(rgba.Get("length").Int()) width, height := c.windowSize.width, c.windowSize.height
rgba := c.ctx.Call("getImageData", 0, 0, width, height).Get("data")
//c.Log(rgba.Get("length").Int())
uint8Arr := js.Global().Get("Uint8Array").New(rgba) uint8Arr := js.Global().Get("Uint8Array").New(rgba)
js.CopyBytesToGo(data, uint8Arr) js.CopyBytesToGo(data, uint8Arr)
res := det.FindFaces(data) pixels := c.rgbaToGrayscale(data)
fmt.Println(res) res := det.DetectFaces(pixels, width, height)
}() fmt.Println(res)
return nil }()
}) return nil
c.window.Call("requestAnimationFrame", c.renderer) })
<-c.done c.window.Call("requestAnimationFrame", c.renderer)
<-c.done
}
} }
// Stop stops the rendering. // Stop stops the rendering.
@@ -146,6 +151,31 @@ func (c *Canvas) StartWebcam() (*Canvas, error) {
} }
} }
func (c *Canvas) rgbaToGrayscale(data []uint8) []uint8 {
rows, cols := c.windowSize.width, c.windowSize.height
// for i := 0; i < rows*cols; i++ {
// r := data[i*4+0]
// g := data[i*4+1]
// b := data[i*4+2]
// var gray = uint8(math.Round(0.2126*float64(r) + 0.7152*float64(g) + 0.0722*float64(b)))
// data[i*4+0] = gray
// data[i*4+1] = gray
// data[i*4+2] = gray
// }
for r := 0; r < rows; r++ {
for c := 0; c < cols; c++ {
// gray = 0.2*red + 0.7*green + 0.1*blue
data[r*cols+c] = uint8(math.Round(
0.2126*float64(data[r*4*cols+4*c+0]) +
0.7152*float64(data[r*4*cols+4*c+1]) +
0.0722*float64(data[r*4*cols+4*c+2])))
}
}
return data
}
// Log calls the `console.log` Javascript function // Log calls the `console.log` Javascript function
func (c *Canvas) Log(args ...interface{}) { func (c *Canvas) Log(args ...interface{}) {
c.window.Get("console").Call("log", args...) c.window.Get("console").Call("log", args...)

View File

@@ -1,7 +1,8 @@
package detector package detector
import ( import (
"log" "errors"
"fmt"
pigo "github.com/esimov/pigo/core" pigo "github.com/esimov/pigo/core"
) )
@@ -27,8 +28,42 @@ var (
mouthCascade = []string{"lp93", "lp84", "lp82", "lp81"} mouthCascade = []string{"lp93", "lp84", "lp82", "lp81"}
) )
func (d *Detector) FindFaces(pixels []uint8) [][]int { func (d *Detector) UnpackCascades() error {
results := d.clusterDetection(pixels, 480, 640) p := pigo.NewPigo()
cascade, err = d.FetchCascade("https://raw.githubusercontent.com/esimov/pigo/master/cascade/facefinder")
if err != nil {
return errors.New("error reading the facefinder cascade file")
}
// 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 {
return errors.New("error unpacking the facefinder cascade file")
}
plc := pigo.NewPuplocCascade()
puplocCascade, err = d.FetchCascade("https://raw.githubusercontent.com/esimov/pigo/master/cascade/puploc")
if err != nil {
return errors.New("error reading the puploc cascade file")
}
puplocClassifier, err = plc.UnpackCascade(puplocCascade)
if err != nil {
return errors.New("error unpacking the puploc cascade file")
}
flpcs, err = d.parseCascadeFiles("https://raw.githubusercontent.com/esimov/pigo/master/cascade/lps/")
if err != nil {
return errors.New("error unpacking the facial landmark points detection cascades")
}
return nil
}
func (d *Detector) DetectFaces(pixels []uint8, width, height int) [][]int {
results := d.clusterDetection(pixels, width, height)
fmt.Println(results)
dets := make([][]int, len(results)) dets := make([][]int, len(results))
for i := 0; i < len(results); i++ { for i := 0; i < len(results); i++ {
@@ -92,56 +127,21 @@ func (d *Detector) FindFaces(pixels []uint8) [][]int {
// clusterDetection runs Pigo face detector core methods // clusterDetection runs Pigo face detector core methods
// and returns a cluster with the detected faces coordinates. // and returns a cluster with the detected faces coordinates.
func (d *Detector) clusterDetection(pixels []uint8, rows, cols int) []pigo.Detection { func (d *Detector) clusterDetection(pixels []uint8, width, height int) []pigo.Detection {
det := NewDetector()
imgParams = &pigo.ImageParams{ imgParams = &pigo.ImageParams{
Pixels: pixels, Pixels: pixels,
Rows: rows, Rows: width,
Cols: cols, Cols: height,
Dim: cols, Dim: height,
} }
cParams := pigo.CascadeParams{ cParams := pigo.CascadeParams{
MinSize: 60, MinSize: 100,
MaxSize: 600, MaxSize: 1200,
ShiftFactor: 0.1, ShiftFactor: 0.1,
ScaleFactor: 1.1, ScaleFactor: 1.1,
ImageParams: *imgParams, ImageParams: *imgParams,
} }
// Ensure that the face detection classifier is loaded only once.
if len(cascade) == 0 {
cascade, err = det.FetchCascade("https://raw.githubusercontent.com/esimov/pigo/master/cascade/facefinder")
if err != nil {
det.Log("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 = det.FetchCascade("https://raw.githubusercontent.com/esimov/pigo/master/cascade/puploc")
if err != nil {
det.Log("Error reading the puploc cascade file: %v", err)
}
puplocClassifier, err = puplocClassifier.UnpackCascade(puplocCascade)
if err != nil {
log.Fatalf("Error unpacking the puploc cascade file: %s", err)
}
flpcs, err = d.parseCascadeFiles("https://raw.githubusercontent.com/esimov/pigo/master/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. // 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. // The result contains quadruplets representing the row, column, scale and detection score.
dets := faceClassifier.RunCascade(cParams, 0.0) dets := faceClassifier.RunCascade(cParams, 0.0)

View File

@@ -34,6 +34,7 @@ func (d *Detector) FetchCascade(url string) ([]byte, error) {
response.Call("arrayBuffer").Call("then", js.FuncOf(func(this js.Value, args []js.Value) interface{} { response.Call("arrayBuffer").Call("then", js.FuncOf(func(this js.Value, args []js.Value) interface{} {
go func() { go func() {
buffer := args[0] buffer := args[0]
fmt.Println(buffer.Get("byteLength").Int())
uint8Array := js.Global().Get("Uint8Array").New(buffer) uint8Array := js.Global().Get("Uint8Array").New(buffer)
jsbuf := make([]byte, uint8Array.Get("length").Int()) jsbuf := make([]byte, uint8Array.Get("length").Int())