package face
|
|
/*
|
#cgo LDFLAGS: -Wl,-rpath,${SRCDIR}/libface/sdk/lib
|
#cgo LDFLAGS: -L${SRCDIR}/libs -lwface
|
#cgo LDFLAGS: -Wl,-rpath,/opt/toolkits/cuda-10.2/lib64
|
#include <stdlib.h>
|
#include "cface.h"
|
*/
|
import "C"
|
import (
|
"errors"
|
"unsafe"
|
|
"basic.com/libgowrapper/sdkstruct.git"
|
"basic.com/pubsub/protomsg.git"
|
"github.com/gogo/protobuf/proto"
|
)
|
|
// SDKFace sdk
|
type SDKFace struct {
|
handle unsafe.Pointer
|
detector bool
|
extractor bool
|
propertizer bool
|
tracker bool
|
fnLogger func(...interface{})
|
}
|
|
// NewSDK sdk
|
func NewSDK(fn func(...interface{})) *SDKFace {
|
h := C.create_sdkface()
|
if h == nil {
|
return nil
|
}
|
|
return &SDKFace{
|
handle: h,
|
detector: false,
|
extractor: false,
|
propertizer: false,
|
tracker: false,
|
fnLogger: fn,
|
}
|
}
|
|
// Free free
|
func (s *SDKFace) Free() {
|
if s != nil && s.handle != nil {
|
C.release(s.handle)
|
}
|
}
|
|
func (s *SDKFace) printLog(l ...interface{}) {
|
if s.fnLogger != nil {
|
s.fnLogger(l)
|
}
|
}
|
|
// Detector detector
|
func (s *SDKFace) Detector(minFaces, rollAngles, threadMax, gpu int) bool {
|
|
if s.detector {
|
return true
|
}
|
ret := C.init_detector(s.handle, C.int(minFaces), C.int(rollAngles), C.int(threadMax), C.int(gpu))
|
if ret <= 0 {
|
s.printLog("->face--> CREATE Detector ERROR: ", ret)
|
return false
|
}
|
s.detector = true
|
return true
|
}
|
|
// Extractor ext
|
func (s *SDKFace) Extractor(threadMax, gpu int) bool {
|
|
if s.extractor {
|
return true
|
}
|
ret := C.init_extractor(s.handle, C.int(threadMax), C.int(gpu))
|
if ret <= 0 {
|
s.printLog("->face--> CREATE Extractor ERROR: ", ret)
|
return false
|
}
|
s.extractor = true
|
return true
|
}
|
|
// Propertizer prop
|
func (s *SDKFace) Propertizer(threadMax int) bool {
|
|
if s.propertizer {
|
return true
|
}
|
ret := C.init_propertizer(s.handle, C.int(threadMax))
|
if ret <= 0 {
|
s.printLog("->face--> CREATE Propertizer ERROR: ", ret)
|
return false
|
}
|
s.propertizer = true
|
return true
|
}
|
|
// Tracker track
|
func (s *SDKFace) Tracker(w, h, maxFaces, interval, sampleSize, threadMax, gpu int) bool {
|
|
if s.tracker {
|
return s.tracker
|
}
|
ret := C.init_tracker(s.handle, C.int(w), C.int(h), C.int(maxFaces), C.int(interval), C.int(sampleSize), C.int(threadMax), C.int(gpu))
|
|
if ret <= 0 {
|
s.printLog("->face--> CREATE Tracker ERROR: ", ret)
|
return false
|
}
|
s.tracker = true
|
return true
|
}
|
|
// CFacePosArrayToGoArray convert cFacePos array to go
|
func CFacePosArrayToGoArray(cArray unsafe.Pointer, count int) (goArray []sdkstruct.CFacePos) {
|
p := uintptr(cArray)
|
|
for i := 0; i < count; i++ {
|
j := *(*sdkstruct.CFacePos)(unsafe.Pointer(p))
|
|
goArray = append(goArray, j)
|
|
p += unsafe.Sizeof(j)
|
}
|
return
|
}
|
|
// Detect det
|
func (s *SDKFace) Detect(data []byte, w, h, c int, ch int) []sdkstruct.CFacePos {
|
if !s.detector {
|
return nil
|
}
|
|
var cfpos unsafe.Pointer
|
var count C.int
|
ret := C.detect(s.handle, unsafe.Pointer(&data[0]), C.int(w), C.int(h), C.int(c), C.int(ch), &cfpos, &count)
|
if ret > 0 {
|
return CFacePosArrayToGoArray(cfpos, int(count))
|
}
|
s.printLog("->face--> Detect No One, Ret: ", ret)
|
return nil
|
}
|
|
// Extract extract
|
func (s *SDKFace) Extract(fpos sdkstruct.CFacePos, data []byte, w, h, c int, ch int) []byte {
|
|
pos := (*C.cFacePos)(unsafe.Pointer(&fpos))
|
|
//(void *handle, const cFacePos *pos, const void*data, const int w, const int h, const int c, const int chan, void **feat, int *featLen);
|
var feat unsafe.Pointer
|
var featLen C.int
|
ret := C.extract(s.handle, pos, unsafe.Pointer(&data[0]), C.int(w), C.int(h), C.int(c), C.int(ch), &feat, &featLen)
|
if ret > 0 {
|
return C.GoBytes(feat, featLen)
|
}
|
s.printLog("->face--> Extract Nothing, Ret: ", ret)
|
return nil
|
}
|
|
// #if FEATURE_NORMALIZE
|
// float CosineDistance(const BYTE* fea1, const BYTE* fea2, int length)
|
// {
|
// int i;
|
// const float* jfea1 = (const float*)fea1;
|
// const float* jfea2 = (const float*)fea2;
|
|
// float score = 0.0f;
|
// for (i = 0; i < length; i++)
|
// {
|
// score += jfea1[i] * jfea2[i];
|
// }
|
|
// return score;
|
// }
|
// #else
|
// float CosineDistance(const BYTE* fea1, const BYTE* fea2, int length)
|
// {
|
// int i;
|
// const float* jfea1 = (const float*)fea1;
|
// const float* jfea2 = (const float*)fea2;
|
// double normTemp1 = 0.0;
|
// double normTemp2 = 0.0;
|
// double normTemp = 0.0;
|
|
// double score = 0.0f;
|
// for (i = 0; i < length; i++) {
|
// score += jfea1[i] * jfea2[i];
|
// normTemp1 += jfea1[i] * jfea1[i];
|
// normTemp2 += jfea2[i] * jfea2[i];
|
// }
|
// normTemp = sqrt(normTemp1)*sqrt(normTemp2);
|
|
// score = score / normTemp;
|
|
// return score;
|
// }
|
// #endif
|
|
// int feaDim = FEATURE_RAW_SIZE / 4;
|
|
// THFEATURE_API float EF_Compare(BYTE* pFeature1,BYTE* pFeature2)
|
// {
|
// if(pFeature1==NULL||pFeature2==NULL) return 0.0f;
|
|
// float fscore;
|
|
// BYTE* pFea1=pFeature1;
|
// BYTE* pFea2=pFeature2;
|
|
// fscore = CosineDistance(pFea1, pFea2, feaDim);
|
// fscore+=0.05f;
|
|
// if(fscore>0.9999f) fscore=0.9999f;
|
// if(fscore<0.0001f) fscore=0.0001f;
|
|
// return fscore;
|
// }
|
//
|
|
func byteSlice2float32Slice(src []byte) []float32 {
|
if len(src) == 0 {
|
return nil
|
}
|
|
l := len(src) / 4
|
ptr := unsafe.Pointer(&src[0])
|
|
return (*[1 << 26]float32)(ptr)[:l:l]
|
}
|
|
// Compare face compare
|
func (s *SDKFace) Compare(feat1 []byte, feat2 []byte) float32 {
|
|
ffeat1 := byteSlice2float32Slice(feat1)
|
ffeat2 := byteSlice2float32Slice(feat2)
|
if len(ffeat1) != len(ffeat2) {
|
return 0
|
}
|
// normalize
|
var score float32
|
for i := 0; i < len(ffeat1); i++ {
|
score += ffeat1[i] * ffeat2[i]
|
}
|
score += 0.05
|
if score > 0.9999 {
|
score = 0.9999
|
}
|
if score < 0.0001 {
|
score = 0.0001
|
}
|
return score
|
|
// if !s.extractor {
|
// return 0
|
// }
|
// res := C.compare(s.handle, (*C.uchar)(unsafe.Pointer(&feat1[0])), (*C.uchar)(unsafe.Pointer(&feat2[0])))
|
// return float32(res)
|
}
|
|
// Propertize prop
|
func (s *SDKFace) Propertize(fpos sdkstruct.CFacePos, data []byte, w, h, c int, ch int) *sdkstruct.CThftResult {
|
if !s.propertizer {
|
return nil
|
}
|
|
pos := (*C.cFacePos)(unsafe.Pointer(&fpos))
|
|
var thft unsafe.Pointer
|
ret := C.propertize(s.handle, pos, unsafe.Pointer(&data[0]), C.int(w), C.int(h), C.int(c), C.int(ch), &thft)
|
if ret == 0 {
|
gothft := *(*sdkstruct.CThftResult)(thft)
|
C.free(thft)
|
return &gothft
|
}
|
s.printLog("->face--> Propertize Nothing, Ret: ", ret)
|
return nil
|
}
|
|
// CFaceInfoArrayToGoArray convert cFaceInfo array to go
|
func CFaceInfoArrayToGoArray(cArray unsafe.Pointer, count int) (goArray []sdkstruct.CFaceInfo) {
|
p := uintptr(cArray)
|
|
for i := 0; i < count; i++ {
|
j := *(*sdkstruct.CFaceInfo)(unsafe.Pointer(p))
|
goArray = append(goArray, j)
|
p += unsafe.Sizeof(j)
|
}
|
return
|
}
|
|
// Track track
|
func (s *SDKFace) Track(data []byte, w, h, c int, ch int) []sdkstruct.CFaceInfo {
|
if !s.tracker {
|
return nil
|
}
|
|
//img, const int chan, void **fInfo, int *fcnt);
|
|
var fCount C.int
|
var finfos unsafe.Pointer
|
ret := C.track(s.handle, unsafe.Pointer(&data[0]), C.int(w), C.int(h), C.int(c), C.int(ch), &finfos, &fCount)
|
|
if ret > 0 {
|
faces := CFaceInfoArrayToGoArray(finfos, int(fCount))
|
//if len(faces) > 0{
|
// fmt.Println("faces detected:", len(faces))
|
//}
|
return faces
|
}
|
return nil
|
}
|
|
// FaceInfo2FacePos info -> pos
|
func FaceInfo2FacePos(face sdkstruct.CFaceInfo) (fPos sdkstruct.CFacePos) {
|
fPos.RcFace = face.RcFace
|
fPos.PtLeftEye = face.PtLeftEye
|
fPos.PtRightEye = face.PtRightEye
|
fPos.PtNose = face.PtNose
|
fPos.PtMouth = face.PtMouth
|
fPos.FAngle.Yaw = face.FAngle.Yaw
|
fPos.FAngle.Pitch = face.FAngle.Pitch
|
fPos.FAngle.Roll = face.FAngle.Roll
|
fPos.FAngle.Confidence = face.FAngle.Confidence
|
|
copy(fPos.PFacialData[:], face.PFacialData[:512])
|
|
return fPos
|
}
|
|
// TrackerResize init face tracker
|
func (s *SDKFace) TrackerResize(w, h, ch int) bool {
|
|
if !s.tracker {
|
s.printLog("->face--> TrackerResize Failed, No Tracker Init")
|
return false
|
}
|
ret := C.track_resize(s.handle, C.int(w), C.int(h), C.int(ch))
|
if ret == 1 {
|
return true
|
}
|
s.printLog("->face--> TrackerResize Failed, Ret: ", ret, " SDK Channel: ", ch, " Size: ", w, "x", h)
|
return false
|
}
|
|
// Run run
|
func (s *SDKFace) Run(data []byte, w, h, c, dchan int) (int, []byte, error) {
|
if data == nil || w <= 0 || h <= 0 {
|
return 0, nil, errors.New("->face--> Face Input Image Error")
|
}
|
|
if !s.tracker || !s.extractor || !s.propertizer {
|
return 0, nil, errors.New("->face--> Face SDK No Init Correctly")
|
}
|
|
channel := c
|
if channel == 0 {
|
channel = 3
|
}
|
|
var fInfo []sdkstruct.CFaceInfo
|
|
fInfo = s.Track(data, w, h, c, dchan)
|
if len(fInfo) == 0 {
|
return 0, nil, errors.New("->face--> Face Track No One")
|
}
|
|
var faces []*protomsg.ResultFaceDetect
|
|
for _, d := range fInfo {
|
|
//运行sd
|
dec := FaceInfo2FacePos(d)
|
p := s.Propertize(dec, data, w, h, c, dchan)
|
feat := s.Extract(dec, data, w, h, c, dchan)
|
|
/// filter rules
|
// sdkid := rMsg.Msg.Tasklab.Sdkinfos[rMsg.Msg.Tasklab.Index].Ipcid
|
// size := (d.RcFace.Right - d.RcFace.Left) * (d.RcFace.Bottom - d.RcFace.Top)
|
// angle := d.FAngle
|
// if !filter(rMsg.Msg.Tasklab.Taskid, sdkid, angle.Confidence, float32(angle.Yaw), int(size)) {
|
// continue
|
// }
|
/// filter rules
|
|
prop := (*protomsg.ThftResult)(unsafe.Pointer(&p))
|
fpos := tconvert2ProtoFacePos(d)
|
|
//组成结果并序列化
|
res := &protomsg.ResultFaceDetect{Pos: fpos, Result: prop, Feats: feat}
|
faces = append(faces, res)
|
|
}
|
facePos := protomsg.ParamFacePos{Faces: faces}
|
d, e := proto.Marshal(&facePos)
|
|
return len(faces), d, e
|
}
|
|
func tconvert2ProtoFacePos(dec sdkstruct.CFaceInfo) *protomsg.FacePos {
|
|
crect := dec.RcFace
|
rect := protomsg.Rect{Left: crect.Left, Top: crect.Top, Right: crect.Right, Bottom: crect.Bottom}
|
leftEye := (*protomsg.Point)(unsafe.Pointer(&dec.PtLeftEye))
|
rightEye := (*protomsg.Point)(unsafe.Pointer(&dec.PtRightEye))
|
mouth := (*protomsg.Point)(unsafe.Pointer(&dec.PtMouth))
|
nose := (*protomsg.Point)(unsafe.Pointer(&dec.PtNose))
|
angle := (*protomsg.FaceAngle)(unsafe.Pointer(&dec.FAngle))
|
faceID := uint64(dec.NFaceID)
|
|
facialData := dec.PFacialData[:512]
|
|
// facialData := make([]byte, 512)
|
// copy(facialData[:], dec.PFacialData[:512])
|
|
return &protomsg.FacePos{
|
RcFace: &rect,
|
PtLeftEye: leftEye,
|
PtRightEye: rightEye,
|
PtMouth: mouth,
|
PtNose: nose,
|
FAngle: angle,
|
Quality: dec.NQuality,
|
FacialData: facialData,
|
FaceID: faceID,
|
}
|
}
|