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 (
"fmt"
"math"
"syscall/js"
"github.com/esimov/pigo/wasm/detector"
@@ -17,7 +18,7 @@ type Canvas struct {
window js.Value
doc js.Value
body js.Value
windowSize struct{ width, height float64 }
windowSize struct{ width, height int }
// Canvas properties
canvas js.Value
@@ -37,8 +38,8 @@ func NewCanvas() *Canvas {
c.doc = c.window.Get("document")
c.body = c.doc.Get("body")
c.windowSize.width = c.window.Get("innerWidth").Float()
c.windowSize.height = c.window.Get("innerHeight").Float()
c.windowSize.width = c.window.Get("innerWidth").Int()
c.windowSize.height = c.window.Get("innerHeight").Int()
c.canvas = c.doc.Call("createElement", "canvas")
c.canvas.Set("width", c.windowSize.width)
@@ -52,27 +53,31 @@ func NewCanvas() *Canvas {
// Render calls the `requestAnimationFrame` Javascript function in asynchronous mode.
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{})
det := detector.NewDetector()
c.renderer = js.FuncOf(func(this js.Value, args []js.Value) interface{} {
go func() {
c.reqID = c.window.Call("requestAnimationFrame", c.renderer)
// Draw the webcam frame to the canvas element
c.ctx.Call("drawImage", c.video, 0, 0)
rgba := c.ctx.Call("getImageData", 0, 0, c.windowSize.width, c.windowSize.height).Get("data")
c.Log(rgba.Get("length").Int())
if err := det.UnpackCascades(); err == nil {
c.renderer = js.FuncOf(func(this js.Value, args []js.Value) interface{} {
go func() {
c.reqID = c.window.Call("requestAnimationFrame", c.renderer)
// Draw the webcam frame to the canvas element
c.ctx.Call("drawImage", c.video, 0, 0)
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)
js.CopyBytesToGo(data, uint8Arr)
res := det.FindFaces(data)
fmt.Println(res)
}()
return nil
})
c.window.Call("requestAnimationFrame", c.renderer)
<-c.done
uint8Arr := js.Global().Get("Uint8Array").New(rgba)
js.CopyBytesToGo(data, uint8Arr)
pixels := c.rgbaToGrayscale(data)
res := det.DetectFaces(pixels, width, height)
fmt.Println(res)
}()
return nil
})
c.window.Call("requestAnimationFrame", c.renderer)
<-c.done
}
}
// 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
func (c *Canvas) Log(args ...interface{}) {
c.window.Get("console").Call("log", args...)

View File

@@ -1,7 +1,8 @@
package detector
import (
"log"
"errors"
"fmt"
pigo "github.com/esimov/pigo/core"
)
@@ -27,8 +28,42 @@ var (
mouthCascade = []string{"lp93", "lp84", "lp82", "lp81"}
)
func (d *Detector) FindFaces(pixels []uint8) [][]int {
results := d.clusterDetection(pixels, 480, 640)
func (d *Detector) UnpackCascades() error {
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))
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
// and returns a cluster with the detected faces coordinates.
func (d *Detector) clusterDetection(pixels []uint8, rows, cols int) []pigo.Detection {
det := NewDetector()
func (d *Detector) clusterDetection(pixels []uint8, width, height int) []pigo.Detection {
imgParams = &pigo.ImageParams{
Pixels: pixels,
Rows: rows,
Cols: cols,
Dim: cols,
Rows: width,
Cols: height,
Dim: height,
}
cParams := pigo.CascadeParams{
MinSize: 60,
MaxSize: 600,
MinSize: 100,
MaxSize: 1200,
ShiftFactor: 0.1,
ScaleFactor: 1.1,
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.
// The result contains quadruplets representing the row, column, scale and detection score.
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{} {
go func() {
buffer := args[0]
fmt.Println(buffer.Get("byteLength").Int())
uint8Array := js.Global().Get("Uint8Array").New(buffer)
jsbuf := make([]byte, uint8Array.Get("length").Int())