wasm: updated facemask demo

This commit is contained in:
esimov
2022-05-16 16:49:44 +03:00
parent d7a8151873
commit 609eb0df9d
2 changed files with 15 additions and 81 deletions

View File

@@ -41,7 +41,7 @@ type Canvas struct {
// Canvas interaction related variables
showPupil bool
showFrame bool
isBlured bool
isBlurred bool
blurRadius uint32
frame *image.NRGBA
@@ -70,11 +70,11 @@ func NewCanvas() *Canvas {
c.canvas.Set("width", c.windowSize.width)
c.canvas.Set("height", c.windowSize.height)
c.canvas.Set("id", "canvas")
c.ellipse.Set("width", c.windowSize.width)
c.ellipse.Set("height", c.windowSize.height)
c.offscreen.Set("width", c.windowSize.width)
c.offscreen.Set("height", c.windowSize.height)
c.canvas.Set("id", "canvas")
c.body.Call("appendChild", c.canvas)
c.ctx = c.canvas.Call("getContext", "2d")
@@ -83,7 +83,7 @@ func NewCanvas() *Canvas {
c.showPupil = false
c.showFrame = false
c.isBlured = true
c.isBlurred = true
c.blurRadius = 20
pigo = detector.NewDetector()
@@ -234,7 +234,7 @@ func (c *Canvas) drawDetection(data []uint8, dets [][]int) error {
row, col, scale := det[1], det[0], int(float64(det[2])*1.2)
if c.isBlured {
if c.isBlurred {
// Substract the image under the detected face region.
imgData := make([]byte, scale*scale*4)
subimg := c.ctx.Call("getImageData", row-scale/2, col-scale/2, scale, scale).Get("data")
@@ -289,7 +289,7 @@ func (c *Canvas) drawDetection(data []uint8, dets [][]int) error {
// Clear out the canvas on each frame.
c.ctxOffscr.Call("clearRect", 0, 0, c.windowSize.width, c.windowSize.height)
// Replace the underlying face region with the blured image.
// Replace the underlying face region with the blurred image.
c.ctxOffscr.Call("putImageData", rawData, 0, 0)
c.ctxOffscr.Call("save")
@@ -339,7 +339,7 @@ func (c *Canvas) detectKeyPress() {
case keyCode.String() == "f":
c.showFrame = !c.showFrame
case keyCode.String() == "b":
c.isBlured = !c.isBlured
c.isBlurred = !c.isBlurred
case keyCode.String() == "]":
if c.blurRadius <= maxBlurRadius {
c.blurRadius++

View File

@@ -1,20 +1,14 @@
package facemask
import (
"encoding/base64"
"fmt"
"image"
"image/color"
"io/ioutil"
"log"
"math"
"net/http"
"net/url"
"sync"
"syscall/js"
"time"
"github.com/esimov/pigo-wasm-demos/detector"
"github.com/esimov/pigo-wasm-demos/pixels"
"github.com/esimov/triangle"
"golang.org/x/sync/errgroup"
)
@@ -153,7 +147,7 @@ func (c *Canvas) Render() error {
var data = make([]byte, width*height*4)
c.done = make(chan struct{})
img := c.loadImage("/images/surgical-mask.png")
img := pixels.LoadImage("/images/surgical-mask.png")
mask = js.Global().Call("eval", "new Image()")
mask.Set("src", "data:image/png;base64,"+img)
@@ -179,7 +173,9 @@ func (c *Canvas) Render() error {
// be able to transfer it from Javascript to Go via the js.CopyBytesToGo function.
uint8Arr := js.Global().Get("Uint8Array").New(rgba)
js.CopyBytesToGo(data, uint8Arr)
gray := c.rgbaToGrayscale(data)
dx, dy := c.windowSize.width, c.windowSize.height
gray := pixels.RgbaToGrayscale(data, dx, dy)
// Reset the data slice to its default values to avoid unnecessary memory allocation.
// Otherwise, the GC won't clean up the memory address allocated by this slice
@@ -267,50 +263,10 @@ func (c *Canvas) StartWebcam() (*Canvas, error) {
}
}
// rgbaToGrayscale converts the rgb pixel values to grayscale
func (c *Canvas) rgbaToGrayscale(data []uint8) []uint8 {
rows, cols := c.windowSize.width, c.windowSize.height
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
}
// pixToImage converts an array buffer to an image.
func (c *Canvas) pixToImage(pixels []uint8, dim int) image.Image {
c.frame = image.NewNRGBA(image.Rect(0, 0, dim, dim))
bounds := c.frame.Bounds()
dx, dy := bounds.Max.X, bounds.Max.Y
col := color.NRGBA{
R: uint8(0),
G: uint8(0),
B: uint8(0),
A: uint8(255),
}
for y := bounds.Min.Y; y < dy; y++ {
for x := bounds.Min.X; x < dx*4; x += 4 {
col.R = uint8(pixels[x+y*dx*4])
col.G = uint8(pixels[x+y*dx*4+1])
col.B = uint8(pixels[x+y*dx*4+2])
col.A = uint8(pixels[x+y*dx*4+3])
c.frame.SetNRGBA(y, int(x/4), col)
}
}
return c.frame
}
// triangulate triangulates the image passed as pixel data
func (c *Canvas) triangulate(data []uint8, dets []int) ([]uint8, error) {
// Converts the buffer array to an image.
img := c.pixToImage(data, int(float64(dets[2])))
img := pixels.PixToImage(data, int(float64(dets[2])))
// Call the face triangulation algorithm.
res, _, _, err := c.triangle.Draw(img, *c.processor, func() {})
@@ -374,8 +330,6 @@ func (c *Canvas) drawDetection(data []uint8, dets [][]int) error {
c.ctx.Call("translate", js.ValueOf(tx).Int(), js.ValueOf(ty).Int())
c.ctx.Call("rotate", js.ValueOf(angle).Float())
c.lock.Lock()
// Substract the image under the detected face region.
imgData := make([]byte, scale*scale*4)
subimg := c.ctx.Call("getImageData", row-scale/2, col-scale/2, scale, scale).Get("data")
@@ -383,10 +337,13 @@ func (c *Canvas) drawDetection(data []uint8, dets [][]int) error {
js.CopyBytesToGo(imgData, uint8Arr)
// Triangulate the facemask part.
c.lock.Lock()
triangle, err := c.triangulate(imgData, det)
if err != nil {
return err
}
c.lock.Unlock()
uint8Arr = js.Global().Get("Uint8Array").New(scale * scale * 4)
js.CopyBytesToJS(uint8Arr, triangle)
@@ -404,7 +361,6 @@ func (c *Canvas) drawDetection(data []uint8, dets [][]int) error {
js.ValueOf(width).Int(), js.ValueOf(height).Int(),
)
c.ctx.Call("restore")
c.lock.Unlock()
}
if c.showFrame {
@@ -477,25 +433,3 @@ func (c *Canvas) Alert(args ...interface{}) {
alert := c.window.Get("alert")
alert.Invoke(args...)
}
// loadImage load the source image and encodes it to base64 format.
func (c *Canvas) loadImage(path string) string {
href := js.Global().Get("location").Get("href")
u, err := url.Parse(href.String())
if err != nil {
log.Fatal(err)
}
u.Path = path
u.RawQuery = fmt.Sprint(time.Now().UnixNano())
resp, err := http.Get(u.String())
if err != nil {
log.Fatal(err)
}
defer resp.Body.Close()
b, err := ioutil.ReadAll(resp.Body)
if err != nil {
log.Fatal(err)
}
return base64.StdEncoding.EncodeToString(b)
}