mirror of
https://github.com/esimov/caire.git
synced 2025-09-26 12:31:16 +08:00
Change gocv face detection with pigo
This commit is contained in:
76
carver.go
76
carver.go
@@ -7,6 +7,7 @@ import (
|
||||
"image/draw"
|
||||
"image/jpeg"
|
||||
_ "image/png"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"math"
|
||||
"os"
|
||||
@@ -14,7 +15,7 @@ import (
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"gocv.io/x/gocv"
|
||||
pigo "github.com/esimov/pigo/core"
|
||||
)
|
||||
|
||||
var usedSeams []UsedSeams
|
||||
@@ -75,7 +76,7 @@ func (c *Carver) set(x, y int, px float64) {
|
||||
// - the minimum energy level is calculated by summing up the current pixel value
|
||||
// with the minimum pixel value of the neighboring pixels from the previous row.
|
||||
func (c *Carver) ComputeSeams(img *image.NRGBA, p *Processor) []float64 {
|
||||
var src *image.NRGBA
|
||||
var srcImg *image.NRGBA
|
||||
newImg := image.NewNRGBA(image.Rect(0, 0, img.Bounds().Dx(), img.Bounds().Dy()))
|
||||
draw.Draw(newImg, newImg.Bounds(), img, image.ZP, draw.Src)
|
||||
|
||||
@@ -88,35 +89,75 @@ func (c *Carver) ComputeSeams(img *image.NRGBA, p *Processor) []float64 {
|
||||
sobel := SobelFilter(Grayscale(newImg), float64(p.SobelThreshold))
|
||||
|
||||
if p.FaceDetect {
|
||||
if len(p.XMLClassifier) == 0 {
|
||||
if len(p.Classifier) == 0 {
|
||||
log.Fatal("Please provide an xml face classifier!")
|
||||
}
|
||||
|
||||
cascadeFile, err := ioutil.ReadFile(p.Classifier)
|
||||
if err != nil {
|
||||
log.Fatalf("Error reading the cascade file: %v", err)
|
||||
}
|
||||
|
||||
tmpImg, err := os.OpenFile(TempImage, os.O_CREATE|os.O_WRONLY, 0755)
|
||||
if err != nil {
|
||||
log.Fatalf("Cannot access temporary image file: %v", err)
|
||||
}
|
||||
|
||||
if err := jpeg.Encode(tmpImg, img, &jpeg.Options{Quality: 100}); err != nil {
|
||||
log.Fatalf("Cannot encode temporary image file: %v", err)
|
||||
}
|
||||
img := gocv.IMRead(TempImage, gocv.IMReadColor)
|
||||
|
||||
// Load classifier to recognize faces.
|
||||
classifier := gocv.NewCascadeClassifier()
|
||||
defer classifier.Close()
|
||||
|
||||
if !classifier.Load(p.XMLClassifier) {
|
||||
log.Fatalf("Error reading cascade file: %v\n", p.XMLClassifier)
|
||||
src, err := pigo.GetImage(TempImage)
|
||||
if err != nil {
|
||||
log.Fatalf("Cannot open the image file: %v", err)
|
||||
}
|
||||
|
||||
// Detect faces.
|
||||
faces := classifier.DetectMultiScaleWithParams(img, 1.25, 4, 0, image.Pt(50, 50), image.Pt(newImg.Bounds().Max.X, newImg.Bounds().Max.Y))
|
||||
pixels := pigo.RgbToGrayscale(src)
|
||||
cols, rows := src.Bounds().Max.X, src.Bounds().Max.Y
|
||||
|
||||
cParams := pigo.CascadeParams{
|
||||
MinSize: 100,
|
||||
MaxSize: int(math.Max(float64(cols), float64(rows))),
|
||||
ShiftFactor: 0.1,
|
||||
ScaleFactor: 1.1,
|
||||
}
|
||||
|
||||
imgParams := 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: %v\n", 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.
|
||||
faces := classifier.RunCascade(imgParams, cParams)
|
||||
|
||||
// Calculate the intersection over union (IoU) of two clusters.
|
||||
faces = classifier.ClusterDetections(faces, 0.2)
|
||||
|
||||
// Range over all the detected faces and draw a white rectangle mask over each of them.
|
||||
// We need to trick the sobel detector to consider them as important image parts.
|
||||
for _, face := range faces {
|
||||
draw.Draw(sobel, face.Bounds(), &image.Uniform{color.RGBA{255, 255, 255, 255}}, image.ZP, draw.Src)
|
||||
if face.Q > 5.0 {
|
||||
rect := image.Rect(
|
||||
face.Col - face.Scale / 2,
|
||||
face.Row - face.Scale / 2,
|
||||
face.Col + face.Scale / 2,
|
||||
face.Row + face.Scale / 2,
|
||||
)
|
||||
draw.Draw(sobel, rect, &image.Uniform{color.RGBA{255, 255, 255, 255}}, image.ZP, draw.Src)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Capture CTRL-C signal and remove the generated temporary image.
|
||||
c := make(chan os.Signal, 2)
|
||||
signal.Notify(c, os.Interrupt, syscall.SIGTERM)
|
||||
@@ -129,13 +170,13 @@ func (c *Carver) ComputeSeams(img *image.NRGBA, p *Processor) []float64 {
|
||||
}
|
||||
|
||||
if p.BlurRadius > 0 {
|
||||
src = StackBlur(sobel, uint32(p.BlurRadius))
|
||||
srcImg = StackBlur(sobel, uint32(p.BlurRadius))
|
||||
} else {
|
||||
src = sobel
|
||||
srcImg = sobel
|
||||
}
|
||||
for x := 0; x < c.Width; x++ {
|
||||
for y := 0; y < c.Height; y++ {
|
||||
r, _, _, a := src.At(x, y).RGBA()
|
||||
r, _, _, a := srcImg.At(x, y).RGBA()
|
||||
c.set(x, y, float64(r)/float64(a))
|
||||
}
|
||||
}
|
||||
@@ -217,6 +258,7 @@ func (c *Carver) FindLowestEnergySeams() []Seam {
|
||||
// RemoveSeam remove the least important columns based on the stored energy (seams) level.
|
||||
func (c *Carver) RemoveSeam(img *image.NRGBA, seams []Seam, debug bool) *image.NRGBA {
|
||||
bounds := img.Bounds()
|
||||
// Reduce the image width with one pixel on each iteration.
|
||||
dst := image.NewNRGBA(image.Rect(0, 0, bounds.Dx()-1, bounds.Dy()))
|
||||
|
||||
for _, seam := range seams {
|
||||
|
@@ -15,11 +15,9 @@ import (
|
||||
)
|
||||
|
||||
const HelpBanner = `
|
||||
______ _
|
||||
/ ____/___ _(_)_______
|
||||
/ / / __ / / ___/ _ \
|
||||
/ /___/ /_/ / / / / __/
|
||||
\____/\____/_/_/ \___/
|
||||
┌─┐┌─┐┬┬─┐┌─┐
|
||||
│ ├─┤│├┬┘├┤
|
||||
└─┘┴ ┴┴┴└─└─┘
|
||||
|
||||
Content aware image resize library.
|
||||
Version: %s
|
||||
@@ -42,7 +40,7 @@ var (
|
||||
debug = flag.Bool("debug", false, "Use debugger")
|
||||
scale = flag.Bool("scale", false, "Proportional scaling")
|
||||
faceDetect = flag.Bool("face", false, "Use face detection")
|
||||
xmlClassifier = flag.String("xml", "", "XML Classifier")
|
||||
classifier = flag.String("class", "", "Image Classifier")
|
||||
)
|
||||
|
||||
func main() {
|
||||
@@ -74,7 +72,7 @@ func main() {
|
||||
Debug: *debug,
|
||||
Scale: *scale,
|
||||
FaceDetect: *faceDetect,
|
||||
XMLClassifier: *xmlClassifier,
|
||||
Classifier: *classifier,
|
||||
}
|
||||
switch mode := fs.Mode(); {
|
||||
case mode.IsDir():
|
||||
|
BIN
data/facefinder
Normal file
BIN
data/facefinder
Normal file
Binary file not shown.
File diff suppressed because it is too large
Load Diff
@@ -32,7 +32,7 @@ type Processor struct {
|
||||
Debug bool
|
||||
Scale bool
|
||||
FaceDetect bool
|
||||
XMLClassifier string
|
||||
Classifier string
|
||||
}
|
||||
|
||||
// Resize implements the Resize method of the Carver interface.
|
||||
|
Reference in New Issue
Block a user