mirror of
https://github.com/Kagami/go-face.git
synced 2025-10-05 07:36:50 +08:00
161 lines
3.7 KiB
Go
161 lines
3.7 KiB
Go
package face
|
|
|
|
// #include <stdlib.h>
|
|
// #include <stdint.h>
|
|
// #include "facerec.h"
|
|
import "C"
|
|
import (
|
|
"image"
|
|
"io/ioutil"
|
|
"os"
|
|
"unsafe"
|
|
)
|
|
|
|
const (
|
|
rectLen = 4
|
|
descrLen = 128
|
|
)
|
|
|
|
// Preinitialized recognizer.
|
|
type FaceRec struct {
|
|
p *_Ctype_struct_facerec
|
|
}
|
|
|
|
// Face structure.
|
|
type Face struct {
|
|
Rectangle image.Rectangle
|
|
Descriptor FaceDescriptor
|
|
}
|
|
|
|
// Descriptor alias.
|
|
type FaceDescriptor [descrLen]float32
|
|
|
|
func NewFaceRec(modelDir string) (rec *FaceRec, err error) {
|
|
cModelDir := C.CString(modelDir)
|
|
defer C.free(unsafe.Pointer(cModelDir))
|
|
p := C.facerec_init(cModelDir)
|
|
|
|
if p.err_str != nil {
|
|
defer C.facerec_free(p)
|
|
defer C.free(unsafe.Pointer(p.err_str))
|
|
err = makeError(C.GoString(p.err_str), int(p.err_code))
|
|
return
|
|
}
|
|
|
|
rec = &FaceRec{p}
|
|
return
|
|
}
|
|
|
|
func (rec *FaceRec) recognize(imgData []byte, maxFaces int) (faces []Face, err error) {
|
|
if len(imgData) == 0 {
|
|
err = ImageLoadError("Empty image")
|
|
return
|
|
}
|
|
cImgData := (*C.uchar)(&imgData[0])
|
|
cLen := C.int(len(imgData))
|
|
cMaxFaces := C.int(maxFaces)
|
|
ret := C.facerec_recognize(rec.p, cImgData, cLen, cMaxFaces)
|
|
defer C.free(unsafe.Pointer(ret))
|
|
|
|
if ret.err_str != nil {
|
|
defer C.free(unsafe.Pointer(ret.err_str))
|
|
err = makeError(C.GoString(ret.err_str), int(ret.err_code))
|
|
return
|
|
}
|
|
|
|
// No faces.
|
|
numFaces := int(ret.num_faces)
|
|
if numFaces == 0 {
|
|
return
|
|
}
|
|
|
|
// Copy faces data to Go structure.
|
|
defer C.free(unsafe.Pointer(ret.rectangles))
|
|
defer C.free(unsafe.Pointer(ret.descriptors))
|
|
|
|
rDataLen := numFaces * rectLen
|
|
rDataPtr := unsafe.Pointer(ret.rectangles)
|
|
rData := (*[1 << 30]C.long)(rDataPtr)[:rDataLen:rDataLen]
|
|
|
|
dDataLen := numFaces * descrLen
|
|
dDataPtr := unsafe.Pointer(ret.descriptors)
|
|
dData := (*[1 << 30]float32)(dDataPtr)[:dDataLen:dDataLen]
|
|
|
|
for i := 0; i < numFaces; i++ {
|
|
face := Face{}
|
|
x0 := int(rData[i*rectLen])
|
|
y0 := int(rData[i*rectLen+1])
|
|
x1 := int(rData[i*rectLen+2])
|
|
y1 := int(rData[i*rectLen+3])
|
|
face.Rectangle = image.Rect(x0, y0, x1, y1)
|
|
copy(face.Descriptor[:], dData[i*descrLen:(i+1)*descrLen])
|
|
faces = append(faces, face)
|
|
}
|
|
return
|
|
}
|
|
|
|
// Convenient method.
|
|
func (rec *FaceRec) recognizeFile(imgPath string, maxFaces int) (face []Face, err error) {
|
|
fd, err := os.Open(imgPath)
|
|
if err != nil {
|
|
return
|
|
}
|
|
imgData, err := ioutil.ReadAll(fd)
|
|
if err != nil {
|
|
return
|
|
}
|
|
return rec.recognize(imgData, maxFaces)
|
|
}
|
|
|
|
// Recognize all image faces.
|
|
// Empty list is returned if there are no faces, error is returned if
|
|
// there was some error while decoding/processing image.
|
|
func (rec *FaceRec) Recognize(imgData []byte) (faces []Face, err error) {
|
|
return rec.recognize(imgData, 0)
|
|
}
|
|
|
|
// Recognize if image has single face or return nil otherwise.
|
|
func (rec *FaceRec) RecognizeSingle(imgData []byte) (face *Face, err error) {
|
|
faces, err := rec.recognize(imgData, 1)
|
|
if err != nil || len(faces) != 1 {
|
|
return
|
|
}
|
|
face = &faces[0]
|
|
return
|
|
}
|
|
|
|
func (rec *FaceRec) RecognizeFile(imgPath string) (faces []Face, err error) {
|
|
return rec.recognizeFile(imgPath, 0)
|
|
}
|
|
|
|
func (rec *FaceRec) RecognizeSingleFile(imgPath string) (face *Face, err error) {
|
|
faces, err := rec.recognizeFile(imgPath, 1)
|
|
if err != nil || len(faces) != 1 {
|
|
return
|
|
}
|
|
face = &faces[0]
|
|
return
|
|
}
|
|
|
|
// Set known samples for the future use.
|
|
func (rec *FaceRec) SetSamples(samples []FaceDescriptor) {
|
|
if len(samples) == 0 {
|
|
return
|
|
}
|
|
cSamples := (*C.float)(unsafe.Pointer(&samples[0]))
|
|
cLen := C.int(len(samples))
|
|
C.facerec_set_samples(rec.p, cSamples, cLen)
|
|
}
|
|
|
|
// Return class for the unknown descriptor. Negative index is returned
|
|
// if no match.
|
|
func (rec *FaceRec) Classify(testSample FaceDescriptor) int {
|
|
cTestSample := (*C.float)(unsafe.Pointer(&testSample))
|
|
return int(C.facerec_classify(rec.p, cTestSample))
|
|
}
|
|
|
|
func (rec *FaceRec) Close() {
|
|
C.facerec_free(rec.p)
|
|
rec.p = nil
|
|
}
|