mirror of
https://github.com/esimov/pigo.git
synced 2025-09-26 20:21:28 +08:00
Implemented Facial landmark points detection
This commit is contained in:
BIN
cascade/lps/lp312
Normal file
BIN
cascade/lps/lp312
Normal file
Binary file not shown.
BIN
cascade/lps/lp38
Normal file
BIN
cascade/lps/lp38
Normal file
Binary file not shown.
BIN
cascade/lps/lp42
Normal file
BIN
cascade/lps/lp42
Normal file
Binary file not shown.
BIN
cascade/lps/lp44
Normal file
BIN
cascade/lps/lp44
Normal file
Binary file not shown.
BIN
cascade/lps/lp46
Normal file
BIN
cascade/lps/lp46
Normal file
Binary file not shown.
BIN
cascade/lps/lp81
Normal file
BIN
cascade/lps/lp81
Normal file
Binary file not shown.
BIN
cascade/lps/lp82
Normal file
BIN
cascade/lps/lp82
Normal file
Binary file not shown.
BIN
cascade/lps/lp84
Normal file
BIN
cascade/lps/lp84
Normal file
Binary file not shown.
BIN
cascade/lps/lp93
Normal file
BIN
cascade/lps/lp93
Normal file
Binary file not shown.
159
cmd/pigo/main.go
159
cmd/pigo/main.go
@@ -35,9 +35,15 @@ var Version string
|
||||
|
||||
var (
|
||||
dc *gg.Context
|
||||
plc *pigo.PuplocCascade
|
||||
imgParams *pigo.ImageParams
|
||||
fd *faceDetector
|
||||
plc *pigo.PuplocCascade
|
||||
flpcs map[string][]*pigo.FlpCascade
|
||||
imgParams *pigo.ImageParams
|
||||
)
|
||||
|
||||
var (
|
||||
eyeCascades = []string{"lp46", "lp44", "lp42", "lp38", "lp312"}
|
||||
mouthCascade = []string{"lp93", "lp84", "lp82", "lp81"}
|
||||
)
|
||||
|
||||
// faceDetector struct contains Pigo face detector general settings.
|
||||
@@ -50,8 +56,10 @@ type faceDetector struct {
|
||||
shiftFactor float64
|
||||
scaleFactor float64
|
||||
iouThreshold float64
|
||||
doPuploc bool
|
||||
puploc bool
|
||||
puplocCascade string
|
||||
flploc bool
|
||||
flplocDir string
|
||||
markDetEyes bool
|
||||
}
|
||||
|
||||
@@ -72,10 +80,12 @@ func main() {
|
||||
scaleFactor = flag.Float64("scale", 1.1, "Scale detection window by percentage")
|
||||
angle = flag.Float64("angle", 0.0, "0.0 is 0 radians and 1.0 is 2*pi radians")
|
||||
iouThreshold = flag.Float64("iou", 0.2, "Intersection over union (IoU) threshold")
|
||||
circleMarker = flag.Bool("circle", false, "Use circle as detection marker")
|
||||
doPuploc = flag.Bool("pl", false, "Pupils/eyes localization")
|
||||
isCircle = flag.Bool("circle", false, "Use circle as detection marker")
|
||||
puploc = flag.Bool("pl", false, "Pupils/eyes localization")
|
||||
puplocCascade = flag.String("plc", "", "Pupil localization cascade file")
|
||||
markDetEyes = flag.Bool("rect", true, "Mark detected eyes")
|
||||
markEyes = flag.Bool("mark", true, "Mark detected eyes")
|
||||
flploc = flag.Bool("flp", false, "Use facial landmark points localization")
|
||||
flplocDir = flag.String("flpdir", "", "The facial landmark points base directory")
|
||||
outputAsJSON = flag.Bool("json", false, "Output face box coordinates into a json file")
|
||||
)
|
||||
|
||||
@@ -89,10 +99,14 @@ func main() {
|
||||
log.Fatal("Usage: pigo -in input.jpg -out out.png -cf cascade/facefinder")
|
||||
}
|
||||
|
||||
if *doPuploc && len(*puplocCascade) == 0 {
|
||||
if *puploc && len(*puplocCascade) == 0 {
|
||||
log.Fatal("Missing the cascade binary file for pupils localization")
|
||||
}
|
||||
|
||||
if *flploc && len(*flplocDir) == 0 {
|
||||
//log.Fatal("Please specify the base directory of the facial landmark points binary files")
|
||||
}
|
||||
|
||||
fileTypes := []string{".jpg", ".jpeg", ".png"}
|
||||
ext := filepath.Ext(*destination)
|
||||
|
||||
@@ -118,16 +132,18 @@ func main() {
|
||||
shiftFactor: *shiftFactor,
|
||||
scaleFactor: *scaleFactor,
|
||||
iouThreshold: *iouThreshold,
|
||||
doPuploc: *doPuploc,
|
||||
puploc: *puploc,
|
||||
puplocCascade: *puplocCascade,
|
||||
markDetEyes: *markDetEyes,
|
||||
flploc: *flploc,
|
||||
flplocDir: *flplocDir,
|
||||
markDetEyes: *markEyes,
|
||||
}
|
||||
faces, err := fd.detectFaces(*source)
|
||||
if err != nil {
|
||||
log.Fatalf("Detection error: %v", err)
|
||||
}
|
||||
|
||||
_, rects, err := fd.drawFaces(faces, *circleMarker)
|
||||
_, rects, err := fd.drawFaces(faces, *isCircle)
|
||||
|
||||
if err != nil {
|
||||
log.Fatalf("Error creating the image output: %s", err)
|
||||
@@ -188,8 +204,8 @@ func (fd *faceDetector) detectFaces(source string) ([]pigo.Detection, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if fd.doPuploc {
|
||||
pl := pigo.PuplocCascade{}
|
||||
if fd.puploc {
|
||||
pl := pigo.NewPuplocCascade()
|
||||
|
||||
cascade, err := ioutil.ReadFile(fd.puplocCascade)
|
||||
if err != nil {
|
||||
@@ -199,6 +215,13 @@ func (fd *faceDetector) detectFaces(source string) ([]pigo.Detection, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if fd.flploc {
|
||||
flpcs, err = pl.ReadCascadeDir(fd.flplocDir)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Run the classifier over the obtained leaf nodes and return the detection results.
|
||||
@@ -248,7 +271,7 @@ func (fd *faceDetector) drawFaces(faces []pigo.Detection, isCircle bool) ([]byte
|
||||
dc.SetStrokeStyle(gg.NewSolidPattern(color.RGBA{R: 255, G: 0, B: 0, A: 255}))
|
||||
dc.Stroke()
|
||||
|
||||
if fd.doPuploc && face.Scale > 50 {
|
||||
if fd.puploc && face.Scale > 50 {
|
||||
rect := image.Rect(
|
||||
face.Col-face.Scale/2,
|
||||
face.Row-face.Scale/2,
|
||||
@@ -263,16 +286,18 @@ func (fd *faceDetector) drawFaces(faces []pigo.Detection, isCircle bool) ([]byte
|
||||
puploc = &pigo.Puploc{
|
||||
Row: face.Row - int(0.075*float32(face.Scale)),
|
||||
Col: face.Col - int(0.175*float32(face.Scale)),
|
||||
Scale: float32(face.Scale) * 0.25,
|
||||
Scale: float32(face.Scale) * 0.15,
|
||||
Perturbs: perturb,
|
||||
}
|
||||
det := plc.RunDetector(*puploc, *imgParams, fd.angle)
|
||||
if det.Row > 0 && det.Col > 0 {
|
||||
leftEye := plc.RunDetector(*puploc, *imgParams, fd.angle, false)
|
||||
if leftEye.Row > 0 && leftEye.Col > 0 {
|
||||
if fd.angle > 0 {
|
||||
drawDetections(ctx,
|
||||
float64(cols/2-(face.Col-det.Col)),
|
||||
float64(rows/2-(face.Row-det.Row)),
|
||||
float64(det.Scale),
|
||||
float64(cols/2-(face.Col-leftEye.Col)),
|
||||
float64(rows/2-(face.Row-leftEye.Row)),
|
||||
float64(leftEye.Scale),
|
||||
color.RGBA{R: 255, G: 0, B: 0, A: 255},
|
||||
fd.markDetEyes,
|
||||
)
|
||||
angle := (fd.angle * 180) / math.Pi
|
||||
rotated := imaging.Rotate(faceZone, 2*angle, color.Transparent)
|
||||
@@ -281,9 +306,11 @@ func (fd *faceDetector) drawFaces(faces []pigo.Detection, isCircle bool) ([]byte
|
||||
dc.DrawImage(final, face.Col-face.Scale/2, face.Row-face.Scale/2)
|
||||
} else {
|
||||
drawDetections(dc,
|
||||
float64(det.Col),
|
||||
float64(det.Row),
|
||||
float64(det.Scale),
|
||||
float64(leftEye.Col),
|
||||
float64(leftEye.Row),
|
||||
float64(leftEye.Scale),
|
||||
color.RGBA{R: 255, G: 0, B: 0, A: 255},
|
||||
fd.markDetEyes,
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -292,17 +319,19 @@ func (fd *faceDetector) drawFaces(faces []pigo.Detection, isCircle bool) ([]byte
|
||||
puploc = &pigo.Puploc{
|
||||
Row: face.Row - int(0.075*float32(face.Scale)),
|
||||
Col: face.Col + int(0.185*float32(face.Scale)),
|
||||
Scale: float32(face.Scale) * 0.25,
|
||||
Scale: float32(face.Scale) * 0.15,
|
||||
Perturbs: perturb,
|
||||
}
|
||||
|
||||
det = plc.RunDetector(*puploc, *imgParams, fd.angle)
|
||||
if det.Row > 0 && det.Col > 0 {
|
||||
rightEye := plc.RunDetector(*puploc, *imgParams, fd.angle, false)
|
||||
if rightEye.Row > 0 && rightEye.Col > 0 {
|
||||
if fd.angle > 0 {
|
||||
drawDetections(ctx,
|
||||
float64(cols/2-(face.Col-det.Col)),
|
||||
float64(rows/2-(face.Row-det.Row)),
|
||||
float64(det.Scale),
|
||||
float64(cols/2-(face.Col-rightEye.Col)),
|
||||
float64(rows/2-(face.Row-rightEye.Row)),
|
||||
float64(rightEye.Scale),
|
||||
color.RGBA{R: 255, G: 0, B: 0, A: 255},
|
||||
fd.markDetEyes,
|
||||
)
|
||||
// convert radians to angle
|
||||
angle := (fd.angle * 180) / math.Pi
|
||||
@@ -312,9 +341,63 @@ func (fd *faceDetector) drawFaces(faces []pigo.Detection, isCircle bool) ([]byte
|
||||
dc.DrawImage(final, face.Col-face.Scale/2, face.Row-face.Scale/2)
|
||||
} else {
|
||||
drawDetections(dc,
|
||||
float64(det.Col),
|
||||
float64(det.Row),
|
||||
float64(det.Scale),
|
||||
float64(rightEye.Col),
|
||||
float64(rightEye.Row),
|
||||
float64(rightEye.Scale),
|
||||
color.RGBA{R: 255, G: 0, B: 0, A: 255},
|
||||
fd.markDetEyes,
|
||||
)
|
||||
}
|
||||
}
|
||||
if fd.flploc {
|
||||
for _, eye := range eyeCascades {
|
||||
for _, flpc := range flpcs[eye] {
|
||||
flp := flpc.FindLandmarkPoints(leftEye, rightEye, *imgParams, perturb, "left")
|
||||
if flp.Row > 0 && flp.Col > 0 {
|
||||
drawDetections(dc,
|
||||
float64(flp.Col),
|
||||
float64(flp.Row),
|
||||
float64(flp.Scale*0.15),
|
||||
color.RGBA{R: 0, G: 0, B: 255, A: 255},
|
||||
false,
|
||||
)
|
||||
}
|
||||
|
||||
flp = flpc.FindLandmarkPoints(leftEye, rightEye, *imgParams, perturb, "right")
|
||||
if flp.Row > 0 && flp.Col > 0 {
|
||||
drawDetections(dc,
|
||||
float64(flp.Col),
|
||||
float64(flp.Row),
|
||||
float64(flp.Scale*0.15),
|
||||
color.RGBA{R: 0, G: 0, B: 255, A: 255},
|
||||
false,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, mouth := range mouthCascade {
|
||||
for _, flpc := range flpcs[mouth] {
|
||||
flp := flpc.FindLandmarkPoints(leftEye, rightEye, *imgParams, perturb, "left")
|
||||
if flp.Row > 0 && flp.Col > 0 {
|
||||
drawDetections(dc,
|
||||
float64(flp.Col),
|
||||
float64(flp.Row),
|
||||
float64(flp.Scale*0.15),
|
||||
color.RGBA{R: 0, G: 0, B: 255, A: 255},
|
||||
false,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
flp := flpcs["lp84"][0].FindLandmarkPoints(leftEye, rightEye, *imgParams, perturb, "right")
|
||||
if flp.Row > 0 && flp.Col > 0 {
|
||||
drawDetections(dc,
|
||||
float64(flp.Col),
|
||||
float64(flp.Row),
|
||||
float64(flp.Scale*0.15),
|
||||
color.RGBA{R: 0, G: 0, B: 255, A: 255},
|
||||
false,
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -370,10 +453,10 @@ func (s *spinner) stop() {
|
||||
s.stopChan <- struct{}{}
|
||||
}
|
||||
|
||||
// inSlice check if a slice contains the string value.
|
||||
func inSlice(ext string, types []string) bool {
|
||||
for _, t := range types {
|
||||
if t == ext {
|
||||
// inSlice checks if the item exists in the slice.
|
||||
func inSlice(item string, slice []string) bool {
|
||||
for _, it := range slice {
|
||||
if it == item {
|
||||
return true
|
||||
}
|
||||
}
|
||||
@@ -381,12 +464,12 @@ func inSlice(ext string, types []string) bool {
|
||||
}
|
||||
|
||||
// drawDetections helper function to draw the detection marks
|
||||
func drawDetections(ctx *gg.Context, x, y, r float64) {
|
||||
func drawDetections(ctx *gg.Context, x, y, r float64, c color.RGBA, markDet bool) {
|
||||
ctx.DrawArc(x, y, r*0.5, 0, 2*math.Pi)
|
||||
ctx.SetFillStyle(gg.NewSolidPattern(color.RGBA{R: 255, G: 0, B: 0, A: 255}))
|
||||
ctx.SetFillStyle(gg.NewSolidPattern(c))
|
||||
ctx.Fill()
|
||||
|
||||
if fd.markDetEyes {
|
||||
if markDet {
|
||||
ctx.DrawRectangle(x-(r*1.5), y-(r*1.5), r*3, r*3)
|
||||
ctx.SetLineWidth(2.0)
|
||||
ctx.SetStrokeStyle(gg.NewSolidPattern(color.RGBA{R: 255, G: 255, B: 0, A: 255}))
|
||||
|
70
core/flploc.go
Normal file
70
core/flploc.go
Normal file
@@ -0,0 +1,70 @@
|
||||
package pigo
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io/ioutil"
|
||||
"math"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
// FlpCascade holds the binary representation of the facial landmark points cascade files
|
||||
type FlpCascade struct {
|
||||
*PuplocCascade
|
||||
error
|
||||
}
|
||||
|
||||
// UnpackFlp unpacks the facial landmark points cascade file.
|
||||
// This will return the binary representation of the cascade file.
|
||||
func (plc *PuplocCascade) UnpackFlp(cf string) (*PuplocCascade, error) {
|
||||
flpc, err := ioutil.ReadFile(cf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return plc.UnpackCascade(flpc)
|
||||
}
|
||||
|
||||
// FindLandmarkPoints detects the facial landmark points based on the pupil localization results.
|
||||
func (plc *PuplocCascade) FindLandmarkPoints(leftEye, rightEye *Puploc, img ImageParams, perturb int, position string) *Puploc {
|
||||
var flploc *Puploc
|
||||
dist1 := (leftEye.Row - rightEye.Row) * (leftEye.Row - rightEye.Row)
|
||||
dist2 := (leftEye.Col - rightEye.Col) * (leftEye.Col - rightEye.Col)
|
||||
dist := math.Sqrt(float64(dist1 + dist2))
|
||||
|
||||
row := float64(leftEye.Row+rightEye.Row)/2.0 + 0.25*dist
|
||||
col := float64(leftEye.Col+rightEye.Col)/2.0 + 0.15*dist
|
||||
scale := 3.0 * dist
|
||||
|
||||
flploc = &Puploc{
|
||||
Row: int(row),
|
||||
Col: int(col),
|
||||
Scale: float32(scale),
|
||||
Perturbs: perturb,
|
||||
}
|
||||
|
||||
if position == "right" {
|
||||
return plc.RunDetector(*flploc, img, 0.0, true)
|
||||
}
|
||||
return plc.RunDetector(*flploc, img, 0.0, false)
|
||||
}
|
||||
|
||||
// ReadCascadeDir reads the facial landmark points cascade files from the provided directory.
|
||||
func (plc *PuplocCascade) ReadCascadeDir(path string) (map[string][]*FlpCascade, error) {
|
||||
cascades, err := ioutil.ReadDir(path)
|
||||
if len(cascades) == 0 {
|
||||
return nil, errors.New("the provided directory is empty")
|
||||
}
|
||||
flpcs := make(map[string][]*FlpCascade)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, cascade := range cascades {
|
||||
cf, err := filepath.Abs(path + "/" + cascade.Name())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
flpc, err := plc.UnpackFlp(cf)
|
||||
flpcs[cascade.Name()] = append(flpcs[cascade.Name()], &FlpCascade{flpc, err})
|
||||
}
|
||||
return flpcs, err
|
||||
}
|
@@ -42,7 +42,7 @@ type Pigo struct {
|
||||
treeThreshold []float32
|
||||
}
|
||||
|
||||
// NewPigo instantiate a new pigo struct.
|
||||
// NewPigo initializes the Pigo constructor method.
|
||||
func NewPigo() *Pigo {
|
||||
return &Pigo{}
|
||||
}
|
||||
|
@@ -29,6 +29,11 @@ type PuplocCascade struct {
|
||||
treePreds []float32
|
||||
}
|
||||
|
||||
// NewPuplocCascade initializes the PuplocCascade constructor method.
|
||||
func NewPuplocCascade() *PuplocCascade {
|
||||
return &PuplocCascade{}
|
||||
}
|
||||
|
||||
// UnpackCascade unpacks the pupil localization cascade file.
|
||||
func (plc *PuplocCascade) UnpackCascade(packet []byte) (*PuplocCascade, error) {
|
||||
var (
|
||||
@@ -127,7 +132,9 @@ func (plc *PuplocCascade) UnpackCascade(packet []byte) (*PuplocCascade, error) {
|
||||
}
|
||||
|
||||
// classifyRegion applies the face classification function over an image.
|
||||
func (plc *PuplocCascade) classifyRegion(r, c, s float32, nrows, ncols int, pixels []uint8, dim int) []float32 {
|
||||
func (plc *PuplocCascade) classifyRegion(r, c, s float32, nrows, ncols int, pixels []uint8, dim int, flipV bool) []float32 {
|
||||
var c1, c2 int
|
||||
|
||||
root := 0
|
||||
treeDepth := int(math.Pow(2, float64(plc.treeDepth)))
|
||||
|
||||
@@ -138,10 +145,17 @@ func (plc *PuplocCascade) classifyRegion(r, c, s float32, nrows, ncols int, pixe
|
||||
idx := 0
|
||||
for k := 0; k < int(plc.treeDepth); k++ {
|
||||
r1 := min(nrows-1, max(0, (256*int(r)+int(plc.treeCodes[root+4*idx+0])*int(round(float64(s))))>>8))
|
||||
c1 := min(ncols-1, max(0, (256*int(c)+int(plc.treeCodes[root+4*idx+1])*int(round(float64(s))))>>8))
|
||||
r2 := min(nrows-1, max(0, (256*int(r)+int(plc.treeCodes[root+4*idx+2])*int(round(float64(s))))>>8))
|
||||
c2 := min(ncols-1, max(0, (256*int(c)+int(plc.treeCodes[root+4*idx+3])*int(round(float64(s))))>>8))
|
||||
|
||||
// flipV means that we wish to flip the column coordinates sign in the tree nodes.
|
||||
// This is required when we are running the facial landmark detector over the right side of the detected eyes.
|
||||
if flipV {
|
||||
c1 = min(ncols-1, max(0, (256*int(c)+int(-plc.treeCodes[root+4*idx+1])*int(round(float64(s))))>>8))
|
||||
c2 = min(ncols-1, max(0, (256*int(c)+int(-plc.treeCodes[root+4*idx+3])*int(round(float64(s))))>>8))
|
||||
} else {
|
||||
c1 = min(ncols-1, max(0, (256*int(c)+int(plc.treeCodes[root+4*idx+1])*int(round(float64(s))))>>8))
|
||||
c2 = min(ncols-1, max(0, (256*int(c)+int(plc.treeCodes[root+4*idx+3])*int(round(float64(s))))>>8))
|
||||
}
|
||||
bintest := func(p1, p2 uint8) uint8 {
|
||||
if p1 > p2 {
|
||||
return 1
|
||||
@@ -153,8 +167,11 @@ func (plc *PuplocCascade) classifyRegion(r, c, s float32, nrows, ncols int, pixe
|
||||
lutIdx := 2 * (int(plc.trees)*treeDepth*i + treeDepth*j + idx - (treeDepth - 1))
|
||||
|
||||
dr += plc.treePreds[lutIdx+0]
|
||||
dc += plc.treePreds[lutIdx+1]
|
||||
|
||||
if flipV {
|
||||
dc += -plc.treePreds[lutIdx+1]
|
||||
} else {
|
||||
dc += plc.treePreds[lutIdx+1]
|
||||
}
|
||||
root += 4*treeDepth - 4
|
||||
}
|
||||
|
||||
@@ -166,7 +183,9 @@ func (plc *PuplocCascade) classifyRegion(r, c, s float32, nrows, ncols int, pixe
|
||||
}
|
||||
|
||||
// classifyRotatedRegion applies the face classification function over a rotated image.
|
||||
func (plc *PuplocCascade) classifyRotatedRegion(r, c, s float32, a float64, nrows, ncols int, pixels []uint8, dim int) []float32 {
|
||||
func (plc *PuplocCascade) classifyRotatedRegion(r, c, s float32, a float64, nrows, ncols int, pixels []uint8, dim int, flipV bool) []float32 {
|
||||
var row1, col1, row2, col2 int
|
||||
|
||||
root := 0
|
||||
treeDepth := int(math.Pow(2, float64(plc.treeDepth)))
|
||||
|
||||
@@ -182,11 +201,23 @@ func (plc *PuplocCascade) classifyRotatedRegion(r, c, s float32, a float64, nrow
|
||||
for j := 0; j < int(plc.trees); j++ {
|
||||
idx := 0
|
||||
for k := 0; k < int(plc.treeDepth); k++ {
|
||||
r1 := min(nrows-1, max(0, 65536*int(r)+int(qcos)*int(plc.treeCodes[root+4*idx+0])-int(qsin)*int(plc.treeCodes[root+4*idx+1]))>>16)
|
||||
c1 := min(ncols-1, max(0, 65536*int(c)+int(qsin)*int(plc.treeCodes[root+4*idx+0])+int(qcos)*int(plc.treeCodes[root+4*idx+1]))>>16)
|
||||
row1 = int(plc.treeCodes[root+4*idx+0])
|
||||
row2 = int(plc.treeCodes[root+4*idx+2])
|
||||
|
||||
r2 := min(nrows-1, max(0, 65536*int(r)+int(qcos)*int(plc.treeCodes[root+4*idx+2])-int(qsin)*int(plc.treeCodes[root+4*idx+3]))>>16)
|
||||
c2 := min(ncols-1, max(0, 65536*int(c)+int(qsin)*int(plc.treeCodes[root+4*idx+2])+int(qcos)*int(plc.treeCodes[root+4*idx+3]))>>16)
|
||||
// flipV means that we wish to flip the column coordinates sign in the tree nodes.
|
||||
// This is required when we are running the facial landmark detector over the right side of the detected eyes.
|
||||
if flipV {
|
||||
col1 = int(-plc.treeCodes[root+4*idx+1])
|
||||
col2 = int(-plc.treeCodes[root+4*idx+3])
|
||||
} else {
|
||||
col1 = int(plc.treeCodes[root+4*idx+1])
|
||||
col2 = int(plc.treeCodes[root+4*idx+3])
|
||||
}
|
||||
|
||||
r1 := min(nrows-1, max(0, 65536*int(r)+int(qcos)*row1-int(qsin)*col1)>>16)
|
||||
c1 := min(ncols-1, max(0, 65536*int(c)+int(qsin)*row1+int(qcos)*col1)>>16)
|
||||
r2 := min(nrows-1, max(0, 65536*int(r)+int(qcos)*row2-int(qsin)*col2)>>16)
|
||||
c2 := min(ncols-1, max(0, 65536*int(c)+int(qsin)*row2+int(qcos)*col2)>>16)
|
||||
|
||||
bintest := func(px1, px2 uint8) int {
|
||||
if px1 <= px2 {
|
||||
@@ -199,8 +230,11 @@ func (plc *PuplocCascade) classifyRotatedRegion(r, c, s float32, a float64, nrow
|
||||
lutIdx := 2 * (int(plc.trees)*treeDepth*i + treeDepth*j + idx - (treeDepth - 1))
|
||||
|
||||
dr += plc.treePreds[lutIdx+0]
|
||||
dc += plc.treePreds[lutIdx+1]
|
||||
|
||||
if flipV {
|
||||
dc += -plc.treePreds[lutIdx+1]
|
||||
} else {
|
||||
dc += plc.treePreds[lutIdx+1]
|
||||
}
|
||||
root += 4*treeDepth - 4
|
||||
}
|
||||
|
||||
@@ -212,22 +246,22 @@ func (plc *PuplocCascade) classifyRotatedRegion(r, c, s float32, a float64, nrow
|
||||
}
|
||||
|
||||
// RunDetector runs the pupil localization function.
|
||||
func (plc *PuplocCascade) RunDetector(pl Puploc, img ImageParams, angle float64) *Puploc {
|
||||
func (plc *PuplocCascade) RunDetector(pl Puploc, img ImageParams, angle float64, flipV bool) *Puploc {
|
||||
rows, cols, scale := []float32{}, []float32{}, []float32{}
|
||||
res := []float32{}
|
||||
|
||||
for i := 0; i < pl.Perturbs; i++ {
|
||||
row := float32(pl.Row) + float32(pl.Scale)*0.15*(0.5-rand.Float32())
|
||||
col := float32(pl.Col) + float32(pl.Scale)*0.15*(0.5-rand.Float32())
|
||||
sc := float32(pl.Scale) * (0.25 + rand.Float32())
|
||||
sc := float32(pl.Scale) * (0.925 + 0.15*rand.Float32())
|
||||
|
||||
if angle > 0.0 {
|
||||
if angle > 1.0 {
|
||||
angle = 1.0
|
||||
}
|
||||
res = plc.classifyRotatedRegion(row, col, sc, angle, img.Rows, img.Cols, img.Pixels, img.Dim)
|
||||
res = plc.classifyRotatedRegion(row, col, sc, angle, img.Rows, img.Cols, img.Pixels, img.Dim, flipV)
|
||||
} else {
|
||||
res = plc.classifyRegion(row, col, sc, img.Rows, img.Cols, img.Pixels, img.Dim)
|
||||
res = plc.classifyRegion(row, col, sc, img.Rows, img.Cols, img.Pixels, img.Dim, flipV)
|
||||
}
|
||||
|
||||
rows = append(rows, res[0])
|
||||
|
Reference in New Issue
Block a user