|
package main |
|
|
|
import ( |
|
"fmt" |
|
"image" |
|
"image/color" |
|
"io/ioutil" |
|
"os" |
|
"path" |
|
|
|
"gocv.io/x/gocv" |
|
) |
|
|
|
const ( |
|
frontalFaceAlt = "haarcascade_frontalface_alt.xml" |
|
frontalFaceDefault = "haarcascade_frontalface_default.xml" |
|
profileFace = "haarcascade_profileface.xml" |
|
) |
|
|
|
type Detector struct { |
|
ccFaceAlt gocv.CascadeClassifier |
|
ccFaceDefault gocv.CascadeClassifier |
|
ccProfileFace gocv.CascadeClassifier |
|
} |
|
|
|
func (d *Detector) cleanUp() { |
|
d.ccFaceAlt.Close() |
|
d.ccFaceDefault.Close() |
|
d.ccProfileFace.Close() |
|
} |
|
|
|
func (d *Detector) prepare(ccDir string) error { |
|
d.ccFaceAlt = gocv.NewCascadeClassifier() |
|
if !d.ccFaceAlt.Load(path.Join(ccDir, frontalFaceAlt)) { |
|
return fmt.Errorf("Error reading cascade classifier file: %v", frontalFaceAlt) |
|
} |
|
d.ccFaceDefault = gocv.NewCascadeClassifier() |
|
if !d.ccFaceDefault.Load(path.Join(ccDir, frontalFaceDefault)) { |
|
return fmt.Errorf("Error reading cascade classifier file: %v", frontalFaceDefault) |
|
} |
|
d.ccProfileFace = gocv.NewCascadeClassifier() |
|
if !d.ccProfileFace.Load(path.Join(ccDir, profileFace)) { |
|
return fmt.Errorf("Error reading cascade classifier file: %v", profileFace) |
|
} |
|
return nil |
|
} |
|
|
|
func (d *Detector) loadImage(inFileName string) (*gocv.Mat, error) { |
|
img := gocv.IMRead(inFileName, gocv.IMReadColor) |
|
if img.Empty() { |
|
return nil, fmt.Errorf("image is empty") |
|
} |
|
return &img, nil |
|
} |
|
|
|
func (d *Detector) detectMultiScale(img *gocv.Mat) ([]image.Rectangle, color.RGBA) { |
|
if rects := d.ccFaceAlt.DetectMultiScale(*img); len(rects) > 0 { |
|
fmt.Print(" [ccFaceAlt]") |
|
return rects, color.RGBA{0, 255, 0, 0} |
|
} |
|
if rects := d.ccFaceDefault.DetectMultiScaleWithParams(*img, 1.15, 4, 0, image.Pt(0, 0), image.Pt(0, 0)); len(rects) > 0 { |
|
fmt.Print(" [ccFaceDefault]") |
|
return rects, color.RGBA{0, 0, 255, 0} |
|
} |
|
if rects := d.ccProfileFace.DetectMultiScaleWithParams(*img, 1.2, 4, 0, image.Pt(0, 0), image.Pt(0, 0)); len(rects) > 0 { |
|
fmt.Print(" [ccProfileFace]") |
|
return rects, color.RGBA{255, 0, 0, 0} |
|
} |
|
return nil, color.RGBA{} |
|
} |
|
|
|
func drawRects(img *gocv.Mat, rects []image.Rectangle, color color.RGBA) { |
|
for _, r := range rects { |
|
gocv.Rectangle(img, r, color, 2) |
|
} |
|
} |
|
|
|
func (d *Detector) detectFaces(img *gocv.Mat) bool { |
|
rects, color := d.detectMultiScale(img) |
|
if rects == nil { |
|
return false |
|
} |
|
drawRects(img, rects, color) |
|
return true |
|
} |
|
|
|
func (d *Detector) detectFeatures(img *gocv.Mat) bool { |
|
imgGray := gocv.NewMat() |
|
defer imgGray.Close() |
|
corners := gocv.NewMat() |
|
defer corners.Close() |
|
gocv.CvtColor(*img, &imgGray, gocv.ColorBGRAToGray) |
|
gocv.GoodFeaturesToTrack(imgGray, &corners, 20, 0.04, 1) |
|
if corners.Empty() { |
|
return false |
|
} |
|
for row := 0; row < corners.Rows(); row++ { |
|
corner := corners.GetVecfAt(row, 0) |
|
x, y := int(corner[0]), int(corner[1]) |
|
gocv.Circle(img, image.Pt(x, y), 5, color.RGBA{0, 255, 0, 0}, 2) |
|
} |
|
return true |
|
} |
|
|
|
func (d *Detector) loadAndDetect(inFileName, outFileName string) error { |
|
img, err := d.loadImage(inFileName) |
|
if err != nil { |
|
return err |
|
} |
|
fmt.Printf(" <-- %s", inFileName) |
|
defer img.Close() |
|
if !d.detectFaces(img) && !d.detectFeatures(img) { |
|
return nil |
|
} |
|
if !gocv.IMWrite(outFileName, *img) { |
|
return fmt.Errorf("Error writing image to: %v", outFileName) |
|
} |
|
fmt.Printf(" %s -->\n", outFileName) |
|
return nil |
|
} |
|
|
|
func doit(ccDir, sourceDir, destinationDir string) error { |
|
detector := Detector{} |
|
if err := detector.prepare(ccDir); err != nil { |
|
return err |
|
} |
|
defer detector.cleanUp() |
|
files, err := ioutil.ReadDir(sourceDir) |
|
if err != nil { |
|
return err |
|
} |
|
for _, file := range files { |
|
if file.IsDir() { |
|
continue |
|
} |
|
inFileName := path.Join(sourceDir, file.Name()) |
|
outFileName := path.Join(destinationDir, file.Name()) |
|
if err := detector.loadAndDetect(inFileName, outFileName); err != nil { |
|
fmt.Printf("Failed to load %s: %s\n", inFileName, err) |
|
} |
|
} |
|
return nil |
|
} |
|
|
|
func main() { |
|
if len(os.Args) < 4 { |
|
fmt.Println("How to run:\n\tshowimage <classifiers_dir> <source_dir> <destination_dir>") |
|
return |
|
} |
|
ccDir := os.Args[1] |
|
sourceDir := os.Args[2] |
|
destinationDir := os.Args[3] |
|
fmt.Printf("Using %s on pics in %s saving in %s\n", ccDir, sourceDir, destinationDir) |
|
if err := doit(ccDir, sourceDir, destinationDir); err != nil { |
|
fmt.Println(err) |
|
} |
|
} |
Uh oh!
There was an error while loading. Please reload this page.