Fast face detection, pupil/eyes localization and facial landmark points detection library in pure Go.

pigo-logo

Build Status go.dev reference license release snapcraft

Pigo is a pure Go face detection, pupil/eyes localization and facial landmark points detection library based on Pixel Intensity Comparison-based Object detection paper (https://arxiv.org/pdf/1305.4537.pdf).

Rectangle face marker Circle face marker
rectangle circle

Motivation

The reason why Pigo has been developed is because almost all of the currently existing solutions for face detection in the Go ecosystem are purely bindings to some C/C++ libraries like OpenCV or dlib, but calling a C program trough cgo introduces huge latencies and implies a significant trade-off in terms of performance. Also in many cases installing OpenCV on various platforms is cumbersome.

The Pigo library does not require any additional modules or third party applications to be installed, however in case you wish to run the library in a real time, webcam based desktop application you might need to have Python and OpenCV installed. Head over to this subtopic for more explanation.

Key features

  • Does not require OpenCV or any 3rd party modules to be installed
  • High processing speed
  • There is no need for image preprocessing prior detection
  • There is no need for the computation of integral images, image pyramid, HOG pyramid or any other similar data structure
  • The face detection is based on pixel intensity comparison encoded in the binary file tree structure
  • Fast detection of in-plane rotated faces
  • The library can detect even faces with eyeglasses
  • Pupils/eyes localization
  • Facial landmark points detection
  • Webassembly support šŸŽ‰

Todo

  • Object detection and description

The library can also detect in plane rotated faces. For this reason a new -angle parameter has been included into the command line utility. The command below will generate the following result (see the table below for all the supported options).

$ pigo -in input.jpg -out output.jpg -cf cascade/facefinder -angle=0.8 -iou=0.01
Input file Output file
input output

Note: In case of in plane rotated faces the angle value should be adapted to the provided image.

Pupils / eyes localization

Starting from v1.2.0 Pigo offers pupils/eyes localization capabilities. The implementation is based on Eye pupil localization with an ensemble of randomized trees.

Check out this example for a realtime demo: https://github.com/esimov/pigo/tree/master/examples/puploc

puploc

Facial landmark points detection

v1.3.0 marks a new milestone in the library evolution, Pigo being able for facial landmark points detection. The implementation is based on Fast Localization of Facial Landmark Points.

Check out this example for a realtime demo: https://github.com/esimov/pigo/tree/master/examples/facial_landmark

flp_example

Install

Install Go, set your GOPATH, and make sure $GOPATH/bin is on your PATH.

$ export GOPATH="$HOME/go"
$ export PATH="$PATH:$GOPATH/bin"

Next download the project and build the binary file.

$ go get -u -f github.com/esimov/pigo/cmd/pigo
$ go install

Binary releases

In case you do not have installed or do not wish to install Go, you can obtain the binary file from the releases folder.

The library can be accessed as a snapcraft function too.

snapcraft pigo

API

Below is a minimal example of using the face detection API.

First you need to load and parse the binary classifier, then convert the image to grayscale mode, and finally to run the cascade function which returns a slice containing the row, column, scale and the detection score.

cascadeFile, err := ioutil.ReadFile("/path/to/cascade/file")
if err != nil {
	log.Fatalf("Error reading the cascade file: %v", err)
}

src, err := pigo.GetImage("/path/to/image")
if err != nil {
	log.Fatalf("Cannot open the image file: %v", err)
}

pixels := pigo.RgbToGrayscale(src)
cols, rows := src.Bounds().Max.X, src.Bounds().Max.Y

cParams := pigo.CascadeParams{
	MinSize:     20,
	MaxSize:     1000,
	ShiftFactor: 0.1,
	ScaleFactor: 1.1,

	ImageParams: pigo.ImageParams{
		Pixels: pixels,
		Rows:   rows,
		Cols:   cols,
		Dim:    cols,
	},
}

pigo := 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.
classifier, err := pigo.Unpack(cascadeFile)
if err != nil {
	log.Fatalf("Error reading the cascade file: %s", err)
}

angle := 0.0 // cascade rotation angle. 0.0 is 0 radians and 1.0 is 2*pi radians

// 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 := classifier.RunCascade(cParams, angle)

// Calculate the intersection over union (IoU) of two clusters.
dets = classifier.ClusterDetections(dets, 0.2)

A note about imports: in order to decode the generated image you have to import image/jpeg or image/png (depending on the provided image type) as in the following example, otherwise you will get a "Image: Unknown format" error.

import (
    _ "image/jpeg"
    pigo "github.com/esimov/pigo/core"
)

Usage

A command line utility is bundled into the library.

$ pigo -in input.jpg -out out.jpg -cf cascade/facefinder

Supported flags:

$ pigo --help

ā”Œā”€ā”ā”¬ā”Œā”€ā”ā”Œā”€ā”
ā”œā”€ā”˜ā”‚ā”‚ ā”¬ā”‚ ā”‚
ā”“  ā”“ā””ā”€ā”˜ā””ā”€ā”˜

Go (Golang) Face detection library.
    Version: 1.4.2

  -angle float
    	0.0 is 0 radians and 1.0 is 2*pi radians
  -cf string
    	Cascade binary file
  -flpc string
    	Facial landmark points cascade directory
  -in string
    	Source image (default "-")
  -iou float
    	Intersection over union (IoU) threshold (default 0.2)
  -json string
    	Output the detection points into a json file
  -mark
    	Mark detected eyes (default true)
  -marker string
    	Detection marker: rect|circle|ellipse (default "rect")
  -max int
    	Maximum size of face (default 1000)
  -min int
    	Minimum size of face (default 20)
  -out string
    	Destination image (default "-")
  -plc string
    	Pupils/eyes localization cascade file
  -scale float
    	Scale detection window by percentage (default 1.1)
  -shift float
    	Shift detection window by percentage (default 0.1)

Important notice: In case you wish to run also the pupil/eyes localization, then you need to use the plc flag and provide a valid path to the pupil localization cascade file. The same applies for facial landmark points detection, only that this time the parameter accepted by the flpc flag is a directory to the facial landmark point cascade files found under cascades/lps.

CLI command examples

You can also use the stdin and stdout pipe commands:

$ cat input/source.jpg | pigo > -in - -out - >out.jpg -cf=/path/to/cascade

in and out default to - so you can also use:

$ cat input/source.jpg | pigo >out.jpg -cf=/path/to/cascade
$ pigo -out out.jpg < input/source.jpg -cf=/path/to/cascade

Using the empty string as value for the -out flag will skip the image generation part. This, combined with the -json flag will encode the detection results into the specified json file. You can also use the pipe - value combined with the -json flag to output the detection coordinates to the standard (stdout) output.

Real time face detection (running as a shared object)

In case you wish to test the library real time face detection capabilities, the examples folder contains a few demos written in Python.

But why Python you might ask? Because the Go ecosystem is (still) missing a cross platform and system independent library for accessing the webcam.

In the Python program we are accessing the webcam and transferring the pixel data as a byte array through cgo as a shared object to the Go program where the core face detection is happening. But as you can imagine this operation is not cost effective, resulting in lower frame rates than the library is capable of.

These demos were created before the Webassembly port and were kept for showing the way how to integrate the Pigo library into other programming languages.

WASM (Webassembly) support šŸŽ‰

Important note: in order to run the Webassembly demos at least Go 1.13 is required!

Starting from version v1.4.0 the library has been ported to WASM. This gives the library a huge performance gain in terms of real time face detection capabilities.

WASM demo

To run the wasm demo select the wasm folder and type make.

For more details check the subpage description: https://github.com/esimov/pigo/tree/master/wasm.

Benchmark results

Below are the benchmark results obtained running Pigo against GoCV using the same conditions.

    BenchmarkGoCV-4   	       3	 414122553 ns/op	     704 B/op	       1 allocs/op
    BenchmarkPIGO-4   	      10	 173664832 ns/op	       0 B/op	       0 allocs/op
    PASS
    ok  	github.com/esimov/gocv-test	4.530s

The code used for the above test can be found under the following link: https://github.com/esimov/pigo-gocv-benchmark

Author

License

Copyright Ā© 2019 Endre Simo

This software is distributed under the MIT license. See the LICENSE file for the full license text.

Owner
Endre Simo
Programmer, crafting things and ideas mostly around graphics, image processing, computer vision and machine learning.
Endre Simo
Comments
  • Eye/Facial Coordinates in JSON?

    Eye/Facial Coordinates in JSON?

    Hi there, is it possible to print/output the Facial Coordinates/Pupil Localization pointers in the JSON file with -json itself? Or any work around for the same?

    or any other way to print the exact coordinates of the pupils or the other facial features?

  • Infinite loop

    Infinite loop

    This particular line is a possible (an in fact, experienced) infinite loop:

    https://github.com/esimov/pigo/blob/0110acf7753778761ddd7b2ac4659de1c65b9c4b/core/pigo.go#L246

    I've figured it out from the stack trace:

    github.com/esimov/pigo/core.(*Pigo).classifyRegion(0xc0001a40a0, 0x42, 0x36, 0x9, 0xc00068e000, 0x238c, 0x238c, 0x5b, 0xbf800000)
    

    The gorutine track trace parameters are as follows:

    • 0xc0001a40a0 (the receiver)
    • 0x42 row (constantly modified)
    • 0x36 col (constantly modified)
    • 0x9 scale (!!does not change!!)
    • other params (do not change)

    The issue comes from a rounding /casting error. The main readme (as well as my code) sets the cascade parameters scale factor as 1.1. When the scale is 9, the factor would come up with 9.9, which again becomes 9 because of the int() conversion.

    The particular issue is that the source image was 91x100 (don't even ask), and I've calculated the face min/max size as 10% and 50% of the image width respectively. The minimum face size thus becomes 9 and with the scale ratio of 1.1 = infinite loop.

    Possible fixes:

    • Clamp the minimum value for a face size where it wouldn't cause problems (10px)
    • Better main loop for this stuff to avoid rounding errors
  • Question: Facial/Identity Recogntition possible?

    Question: Facial/Identity Recogntition possible?

    Would it be possible to extend this library or use it as-is to perform facial recognition and thus identity of persons in images? (as opposed to face detection)

  • Got exit code 1073741819 (0xC0000005) from python demo

    Got exit code 1073741819 (0xC0000005) from python demo

    Hi there, I try to exec demo with Python 3.9 on Windows, sometimes got unexpected exit, with code -1073741819 (0xC0000005) , that err from C shared lib I think, and that because of index out of range usually. I'm not good at Golang and not founded the true reason from code, give me some advice to fix it please, thx.

  • Is there a way to create new landmark point definitions?

    Is there a way to create new landmark point definitions?

    Hi. I'm trying to implement webcam based facial motion capture with with library.

    The current set of face and retina landmarks is quite good, but I'm wondering if it's possible to extend the landmark regions that the PICO process can identify.

    Specifically:

    Option 1: adding new face landmarks

    I'm assuming that new facial landmark locations would require defining new cascade files. Did the paper authors mentioned any recommendations on collecting and tagging example training data, or the training process itself?

    facial landmarks

    Option 2: adding simple paint based landmarks

    Perhaps a simpler option would be to somehow define cascades for recognizing simple shapes like dot marks, or cross marks placed in an actor's face where the missing facial landmark points are needed.

    paint marks

    Let me know if I missed anything.

    Thanks!

    Resources

    AutoDesk SoftImage - The Motion Capture Process

  • Realtime + WASM + blinking

    Realtime + WASM + blinking

    I was wondering if this library can be used for detecting blinks.

    You have a real-time example using python. Since chrome now supports (experimental) shape detection and video camera access: https://paul.kinlan.me/face-detection/ & https://medium.com/@joomiguelcunha/lets-play-with-chrome-s-face-detection-api-ca13017a958f I was wondering if using your WASM port it was able to detect if a face was blinking.

  • Faster detection on ffmpeg videos

    Faster detection on ffmpeg videos

    I'm developing a CCTV face detection app to send frame with good enough face detection quality (qTresh from example) to my backend site with employee face recognition capabilities. Nothing's wrong with Pigo and everything work as expected of this GODLY portable pure-go face-detection pkg. But I'm sort of stucked with some latency problem due to high fps (amount of frames) that the app need to "detect" with Pigo. I tried to decrease the FPS of the video reading to 10 fps but the latency still can't catch up to the video frames send by the CCTV.

    Please help me if you got any tips or recommendation as a CLEARLY more experienced devs ^^.

    I'm using Pigo 1.4.6.

    Here's my code:

    package main
    
    import (
    	"io"
    	"io/ioutil"
    	"log"
    	"os"
    	"time"
    
    	pigo "github.com/esimov/pigo/core"
    	"github.com/fogleman/gg"
    	"github.com/unixpickle/ffmpego"
    )
    
    var f *os.File
    var dc *gg.Context
    
    func videoFileFacetest() {
    	cascadeFile, err := ioutil.ReadFile("./cascade/facefinder")
    	if err != nil {
    		log.Fatalf("Error reading the cascade file: %v", err)
    	}
    
    	vr, _ := ffmpego.NewVideoReaderResampled("sample2.mp4", 10)
    	// vw, _ := ffmpego.NewVideoWriter("output.mp4", vr.VideoInfo().Width, vr.VideoInfo().Height, 10)
    	i := 0
    	for {
    		i++
    		frame, err := vr.ReadFrame()
    		if err == io.EOF {
    			break
    		}
    		start := time.Now()
    		pixels := pigo.RgbToGrayscale(frame)
    
    		cols, rows := frame.Bounds().Max.X, frame.Bounds().Max.Y
    
    		cParams := pigo.CascadeParams{
    			MinSize:     20,
    			MaxSize:     1000,
    			ShiftFactor: 0.1,
    			ScaleFactor: 1.1,
    
    			ImageParams: pigo.ImageParams{
    				Pixels: pixels,
    				Rows:   rows,
    				Cols:   cols,
    				Dim:    cols,
    			},
    		}
    
    		pigoFunc := pigo.NewPigo()
    		classifier, err := pigoFunc.Unpack(cascadeFile)
    		if err != nil {
    			log.Fatalf("Error reading the cascade file: %s", err)
    		}
    		angle := 0.0 // cascade rotation angle. 0.0 is 0 radians and 1.0 is 2*pi radians
    
    		// 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 := classifier.RunCascade(cParams, angle)
    		// // Calculate the intersection over union (IoU) of two clusters.
    
    		// print(len(dets))
    		// print("_")
    		// println(len(dets2))
    
    		var qTresh float32
    		qTresh = 6.8
    		goodQ := []pigo.Detection{}
    		for i := range dets {
    			if dets[i].Q > qTresh {
    				goodQ = append(goodQ, dets[i])
    			}
    		}
    		if len(goodQ) > 0 {
    			dets2 := classifier.ClusterDetections(goodQ, 0.01)
    			if len(dets2) > 0 {
    				// dc = gg.NewContext(cols, rows)
    				// dc.DrawImage(frame, 0, 0)
    
    				for i := 0; i < len(dets2); i++ {
    					if dets2[i].Q > qTresh {
    						println("we got a winner here")
    						// dc.DrawRectangle(
    						// 	float64(dets2[i].Col-dets2[i].Scale/2),
    						// 	float64(dets2[i].Row-dets2[i].Scale/2),
    						// 	float64(dets2[i].Scale),
    						// 	float64(dets2[i].Scale),
    						// )
    
    						// dc.SetLineWidth(20.0)
    						// dc.SetStrokeStyle(gg.NewSolidPattern(color.RGBA{R: 255, G: 0, B: 0, A: 255}))
    						// dc.Stroke()
    
    						// vw.WriteFrame(dc.Image())
    					}
    				}
    			}
    		}
    		elapsed := time.Since(start)
    		log.Printf("Loop took %s", elapsed)
    	}
    
    	vr.Close()
    	// vw.Close()
    	return
    }
    
    func main() {
    	videoFileFacetest()
    }
    
    

    Here's the sample video:

    https://www.pexels.com/video/people-going-in-and-out-of-the-royal-opera-house-1721303/

  • On the CLI, -flpc flag doesn't work without -plc.

    On the CLI, -flpc flag doesn't work without -plc.

    Describe the bug

    -fplc flag doesn't seem to work if -plc isn't specified.

    This command works fine: $ pigo -in image.jpg -out output.jpg -cf ../pigo/cascade/facefinder -plc ../pigo/cascade/puploc -flpc ../pigo/cascade/lps

    But running the following only detects the face but not its facial landmarks: $ pigo -in image.jpg -out output.jpg -cf ../pigo/cascade/facefinder -flpc ../pigo/cascade/lps

    Expected behavior

    -flpc should work without -plc as per the screenshot https://user-images.githubusercontent.com/883386/66802771-3b0cc880-ef26-11e9-9ee3-7e9e981ef3f7.png

  • encountered some problems when i try write a demo by myself

    encountered some problems when i try write a demo by myself

    my demo is : https://github.com/Taoja/web-wasm-faceDetector

    there have two problems:

    • the func rgbaToGrayscale is useless, the picture still colorful when i output imageData and use canvas render it
    • unable detect face, maybe it is because func rgbaToGrayscale is useless
  • Passing output value via a variable (Python)

    Passing output value via a variable (Python)

    Hi there! I'm trying to pass the values for the eyes separately using the blinkdet example, but seems like it outputs the values for both the eyes together and not separately (just outputting max_radius)

    Is there a way to output the values that I get using -json via a particular variable or just output the values (max_radius) of both the eyes separately?

  • web example

    web example

    Hi,

    If I run your web example than I get this error: 2020/04/24 21:51:38 [ERROR] reading next part multipart: NextPart: EOF I must admit that I don't meet the requirements for this example. My python is python3 and my opencv is 4.2. OS is Ubuntu 20.04LTS. I tested as follows

    1. Run: go run main.go -cf "../../cascade/facefinder"
    2. Open URL: localhost:8081/cam in browser
    3. Then this error appears Any ideas? Thanks
  • Likeliness between faces

    Likeliness between faces

    First off, just want to appreciate and thank you for this cool project you're working on. I was wondering if you could add the feature to compare two faces and describe how likely they are.

DOM library for Go and WASM

Go DOM binding (and more) for WebAssembly This library provides a Go API for different Web APIs for WebAssembly target. It's in an active development,

Dec 23, 2022
Library to use HTML5 Canvas from Go-WASM, with all drawing within go code

go-canvas go-canvas is a pure go+webassembly Library for efficiently drawing on a html5 canvas element within the browser from go without requiring ca

Dec 11, 2022
Vugu: A modern UI library for Go+WebAssembly (experimental)

Vugu: A modern UI library for Go+WebAssembly (experimental)

Jan 3, 2023
Interact with browser from Go. Manually-crafted WebAPI interoperation library.

GWeb: golang + js + wasm gweb -- strictly typed WebAPI library on top of syscall/js. Like flow or TypeScript but for Go. You need it if you want to in

Sep 26, 2022
The note-tinygo Go library for communicating with Blues Wireless Notecard via serial or IĀ²C

Blues Wireless The note-tinygo Go library for communicating with Blues Wireless Notecard via serial or IĀ²C. This library allows you to control a Notec

Nov 29, 2021
This library provides WebAssembly capability for goja Javascript engine

This module provides WebAssembly functions into goja javascript engine.

Jan 10, 2022
Golang-WASM provides a simple idiomatic, and comprehensive API and bindings for working with WebAssembly for Go and JavaScript developers
Golang-WASM provides a simple idiomatic, and comprehensive API and bindings for working with WebAssembly for Go and JavaScript developers

A bridge and bindings for JS DOM API with Go WebAssembly. Written by Team Ortix - Hamza Ali and Chan Wen Xu. GOOS=js GOARCH=wasm go get -u github.com/

Dec 22, 2022
Aes for go and java; build go fo wasm and use wasm parse java response.

aes_go_wasm_java aes for go and java; build go fo wasm and use wasm parse java response. vscode setting config settings.json { "go.toolsEnvVars":

Dec 14, 2021
Go compiler for small places. Microcontrollers, WebAssembly, and command-line tools. Based on LLVM.

TinyGo - Go compiler for small places TinyGo is a Go compiler intended for use in small places such as microcontrollers, WebAssembly (Wasm), and comma

Dec 30, 2022
WebAssembly interop between Go and JS values.

vert Package vert provides WebAssembly interop between Go and JS values. Install GOOS=js GOARCH=wasm go get github.com/norunners/vert Examples Hello W

Dec 28, 2022
A package to build progressive web apps with Go programming language and WebAssembly.
A package to build progressive web apps with Go programming language and WebAssembly.

Go-app is a package for building progressive web apps (PWA) with the Go programming language (Golang) and WebAssembly (Wasm). Shaping a UI is done by

Dec 30, 2022
A package to build progressive web apps with Go programming language and WebAssembly.
A package to build progressive web apps with Go programming language and WebAssembly.

Go-app is a package for building progressive web apps (PWA) with the Go programming language (Golang) and WebAssembly (Wasm). Shaping a UI is done by

Jan 2, 2023
āš™ļø Concept of Golang HTML render engine with frontend components and dynamic behavior
āš™ļø Concept of Golang HTML render engine with frontend components and dynamic behavior

An HTML render engine concept that brings frontend-like components experience to the server side with native html/template on steroids. Supports any s

Nov 25, 2022
Live views and components for golang

live Real-time user experiences with server-rendered HTML in Go. Inspired by and borrowing from Phoenix LiveViews. Live is intended as a replacement f

Dec 29, 2022
Bed and Breakfast web app written in Go

BOOKINGS AND RESERVATIONS This repository contains the files for my RareBnB application RareBnB is an "AirBnB" style clone providing a user the abilit

Jan 11, 2022
A Go program that takes an image, uses pigo to detect a face, and creates a gif that zooms in on the face
A Go program that takes an image, uses pigo to detect a face, and creates a gif that zooms in on the face

ok-zoomer Every GIF is a gift. How it works face detection with esimov/pigo color quantization / dithering with esimov/colorquant image resizing with

Nov 27, 2022
A high-performance concurrent scanner written by go, which can be used for survival detection, tcp port detection, and web service detection.
A high-performance concurrent scanner written by go, which can be used for survival detection, tcp port detection, and web service detection.

aScan A high-performance concurrent scanner written by go, which can be used for survival detection, tcp port detection, and web service detection. Fu

Aug 15, 2022
A demo for building recordings from landmark information provided by media pipe

Landmark Recordings A demo for building recordings from landmark information pro

Jul 27, 2022
i18n (Internationalization and localization) engine written in Go, used for translating locale strings.

go-localize Simple and easy to use i18n (Internationalization and localization) engine written in Go, used for translating locale strings. Use with go

Nov 29, 2022
Package i18n provides internationalization and localization for your Go applications.

i18n Package i18n provides internationalization and localization for your Go applications. Installation The minimum requirement of Go is 1.16. go get

Nov 9, 2022