From 8fe0d8bc91779a55ff3be9ecd722deaf6deee180 Mon Sep 17 00:00:00 2001 From: diegohce Date: Sun, 1 Sep 2024 08:34:31 -0300 Subject: [PATCH] added face recognizer interface (#1211) contrib/face: added face recognizer interface. refactored LBPHFaceRecognizer --- contrib/face.cpp | 52 ++++++++---------- contrib/face.go | 78 ++++++++------------------ contrib/face.h | 24 +++++--- contrib/face_recognizer.go | 109 +++++++++++++++++++++++++++++++++++++ contrib/face_test.go | 3 + 5 files changed, 176 insertions(+), 90 deletions(-) create mode 100644 contrib/face_recognizer.go diff --git a/contrib/face.cpp b/contrib/face.cpp index d2a1f4dd..7d8f458d 100644 --- a/contrib/face.cpp +++ b/contrib/face.cpp @@ -1,10 +1,10 @@ #include "face.h" -LBPHFaceRecognizer CreateLBPHFaceRecognizer() { - return new cv::Ptr(cv::face::LBPHFaceRecognizer::create()); +bool FaceRecognizer_Empty(FaceRecognizer fr) { + return (*fr)->empty(); } -void LBPHFaceRecognizer_Train(LBPHFaceRecognizer fr, Mats mats, IntVector labels_in) { +void FaceRecognizer_Train(FaceRecognizer fr, Mats mats, IntVector labels_in) { std::vector labels; for (int i = 0, *v = labels_in.val; i < labels_in.length; ++v, ++i) { @@ -22,7 +22,7 @@ void LBPHFaceRecognizer_Train(LBPHFaceRecognizer fr, Mats mats, IntVector labels return; } -void LBPHFaceRecognizer_Update(LBPHFaceRecognizer fr, Mats mats, IntVector labels_in) { +void FaceRecognizer_Update(FaceRecognizer fr, Mats mats, IntVector labels_in) { std::vector labels; for (int i = 0, *v = labels_in.val; i < labels_in.length; ++v, ++i) { @@ -40,14 +40,14 @@ void LBPHFaceRecognizer_Update(LBPHFaceRecognizer fr, Mats mats, IntVector label return; } -int LBPHFaceRecognizer_Predict(LBPHFaceRecognizer fr, Mat sample) { +int FaceRecognizer_Predict(FaceRecognizer fr, Mat sample) { int label; label = (*fr)->predict(*sample); return label; } -struct PredictResponse LBPHFaceRecognizer_PredictExtended(LBPHFaceRecognizer fr, Mat sample) { +struct PredictResponse FaceRecognizer_PredictExtended(FaceRecognizer fr, Mat sample) { struct PredictResponse response; int label; double confidence; @@ -56,68 +56,61 @@ struct PredictResponse LBPHFaceRecognizer_PredictExtended(LBPHFaceRecognizer fr, response.label = label; response.confidence = confidence; - return response; } -double LBPHFaceRecognizer_GetThreshold(LBPHFaceRecognizer fr){ +double FaceRecognizer_GetThreshold(FaceRecognizer fr){ return (*fr)->getThreshold(); } -void LBPHFaceRecognizer_SetThreshold(LBPHFaceRecognizer fr, double threshold) { +void FaceRecognizer_SetThreshold(FaceRecognizer fr, double threshold) { (*fr)->setThreshold(threshold); + return; +} + +void FaceRecognizer_SaveFile(FaceRecognizer fr, const char* filename) { + (*fr)->write(filename); + return; +} +void FaceRecognizer_LoadFile(FaceRecognizer fr, const char* filename) { + (*fr)->read(filename); return; } +LBPHFaceRecognizer CreateLBPHFaceRecognizer() { + return new cv::Ptr(cv::face::LBPHFaceRecognizer::create()); +} + void LBPHFaceRecognizer_SetRadius(LBPHFaceRecognizer fr, int radius) { (*fr)->setRadius(radius); - return; } void LBPHFaceRecognizer_SetNeighbors(LBPHFaceRecognizer fr, int neighbors) { (*fr)->setNeighbors(neighbors); - return; } - int LBPHFaceRecognizer_GetNeighbors(LBPHFaceRecognizer fr) { int n; n = (*fr)->getNeighbors(); - return n; } -void LBPHFaceRecognizer_SaveFile(LBPHFaceRecognizer fr, const char* filename) { - (*fr)->write(filename); - - return; -} - -void LBPHFaceRecognizer_LoadFile(LBPHFaceRecognizer fr, const char* filename) { - (*fr)->read(filename); - - return; -} - void LBPHFaceRecognizer_SetGridX(LBPHFaceRecognizer fr, int x) { (*fr)->setGridX(x); - return; } void LBPHFaceRecognizer_SetGridY(LBPHFaceRecognizer fr, int y) { (*fr)->setGridY(y); - return; } int LBPHFaceRecognizer_GetGridX(LBPHFaceRecognizer fr) { int n = (*fr)->getGridX(); - return n; } @@ -126,3 +119,6 @@ int LBPHFaceRecognizer_GetGridY(LBPHFaceRecognizer fr) { return n; } +void LBPHFaceRecognizer_Close(LBPHFaceRecognizer fr) { + delete fr; +} \ No newline at end of file diff --git a/contrib/face.go b/contrib/face.go index 4a2f27fd..925cf40c 100644 --- a/contrib/face.go +++ b/contrib/face.go @@ -7,7 +7,6 @@ package contrib import "C" import ( "image" - "unsafe" "gocv.io/x/gocv" ) @@ -18,12 +17,18 @@ type PredictResponse struct { Confidence float32 `json:"confidence"` } +var _ FaceRecognizer = (*LBPHFaceRecognizer)(nil) + // LBPHFaceRecognizer is a wrapper for the OpenCV Local Binary Patterns // Histograms face recognizer. type LBPHFaceRecognizer struct { p C.LBPHFaceRecognizer } +func (fr *LBPHFaceRecognizer) Empty() bool { + return faceRecognizer_Empty(C.FaceRecognizer(fr.p)) +} + // NewLBPHFaceRecognizer creates a new LBPH Recognizer model. // // For further information, see: @@ -36,24 +41,7 @@ func NewLBPHFaceRecognizer() *LBPHFaceRecognizer { // // see https://docs.opencv.org/master/dd/d65/classcv_1_1face_1_1FaceRecognizer.html#ac8680c2aa9649ad3f55e27761165c0d6 func (fr *LBPHFaceRecognizer) Train(images []gocv.Mat, labels []int) { - cparams := []C.int{} - for _, v := range labels { - cparams = append(cparams, C.int(v)) - } - labelsVector := C.struct_IntVector{} - labelsVector.val = (*C.int)(&cparams[0]) - labelsVector.length = (C.int)(len(cparams)) - - cMatArray := make([]C.Mat, len(images)) - for i, r := range images { - cMatArray[i] = (C.Mat)(r.Ptr()) - } - matsVector := C.struct_Mats{ - mats: (*C.Mat)(&cMatArray[0]), - length: C.int(len(images)), - } - - C.LBPHFaceRecognizer_Train(fr.p, matsVector, labelsVector) + faceRecognizer_Train(C.FaceRecognizer(fr.p), images, labels) } // Update updates the existing trained model with new images and labels. @@ -61,24 +49,7 @@ func (fr *LBPHFaceRecognizer) Train(images []gocv.Mat, labels []int) { // For further information, see: // https://docs.opencv.org/master/dd/d65/classcv_1_1face_1_1FaceRecognizer.html#a8a4e73ea878dcd0c235d0487189d25f3 func (fr *LBPHFaceRecognizer) Update(newImages []gocv.Mat, newLabels []int) { - cparams := []C.int{} - for _, v := range newLabels { - cparams = append(cparams, C.int(v)) - } - labelsVector := C.struct_IntVector{} - labelsVector.val = (*C.int)(&cparams[0]) - labelsVector.length = (C.int)(len(cparams)) - - cMatArray := make([]C.Mat, len(newImages)) - for i, r := range newImages { - cMatArray[i] = (C.Mat)(r.Ptr()) - } - matsVector := C.struct_Mats{ - mats: (*C.Mat)(&cMatArray[0]), - length: C.int(len(newImages)), - } - - C.LBPHFaceRecognizer_Update(fr.p, matsVector, labelsVector) + faceRecognizer_Update(C.FaceRecognizer(fr.p), newImages, newLabels) } // Predict predicts a label for a given input image. It returns the label for @@ -87,7 +58,7 @@ func (fr *LBPHFaceRecognizer) Update(newImages []gocv.Mat, newLabels []int) { // For further information, see: // https://docs.opencv.org/master/dd/d65/classcv_1_1face_1_1FaceRecognizer.html#aa2d2f02faffab1bf01317ae6502fb631 func (fr *LBPHFaceRecognizer) Predict(sample gocv.Mat) int { - label := C.LBPHFaceRecognizer_Predict(fr.p, (C.Mat)(sample.Ptr())) + label := faceRecognizer_Predict(C.FaceRecognizer(fr.p), sample) return int(label) } @@ -99,11 +70,7 @@ func (fr *LBPHFaceRecognizer) Predict(sample gocv.Mat) int { // For further information, see: // https://docs.opencv.org/master/dd/d65/classcv_1_1face_1_1FaceRecognizer.html#ab0d593e53ebd9a0f350c989fcac7f251 func (fr *LBPHFaceRecognizer) PredictExtendedResponse(sample gocv.Mat) PredictResponse { - respp := C.LBPHFaceRecognizer_PredictExtended(fr.p, (C.Mat)(sample.Ptr())) - resp := PredictResponse{ - Label: int32(respp.label), - Confidence: float32(respp.confidence), - } + resp := faceRecognizer_PredictExtendedResponse(C.FaceRecognizer(fr.p), sample) return resp } @@ -114,7 +81,7 @@ func (fr *LBPHFaceRecognizer) PredictExtendedResponse(sample gocv.Mat) PredictRe // For further information, see: // https://docs.opencv.org/4.x/df/d25/classcv_1_1face_1_1LBPHFaceRecognizer.html#acf2a6993eb4347b3f89009da693a3f70 func (fr *LBPHFaceRecognizer) GetThreshold() float32 { - t := C.LBPHFaceRecognizer_GetThreshold(fr.p) + t := faceRecognizer_GetThreshold(C.FaceRecognizer(fr.p)) return float32(t) } @@ -124,7 +91,7 @@ func (fr *LBPHFaceRecognizer) GetThreshold() float32 { // For further information, see: // https://docs.opencv.org/master/dd/d65/classcv_1_1face_1_1FaceRecognizer.html#a3182081e5f8023e658ad8ab96656dd63 func (fr *LBPHFaceRecognizer) SetThreshold(threshold float32) { - C.LBPHFaceRecognizer_SetThreshold(fr.p, (C.double)(threshold)) + faceRecognizer_SetThreshold(C.FaceRecognizer(fr.p), threshold) } // SetNeighbors sets the neighbors value of the model, i.e. the number of @@ -134,7 +101,7 @@ func (fr *LBPHFaceRecognizer) SetThreshold(threshold float32) { // For further information, see: // https://docs.opencv.org/master/df/d25/classcv_1_1face_1_1LBPHFaceRecognizer.html#ab225f7bf353ce8697a506eda10124a92 func (fr *LBPHFaceRecognizer) SetNeighbors(neighbors int) { - C.LBPHFaceRecognizer_SetNeighbors(fr.p, (C.int)(neighbors)) + C.LBPHFaceRecognizer_SetNeighbors(fr.p, C.int(neighbors)) } // GetNeighbors returns the neighbors value of the model. @@ -142,6 +109,7 @@ func (fr *LBPHFaceRecognizer) SetNeighbors(neighbors int) { // For further information, see: // https://docs.opencv.org/master/df/d25/classcv_1_1face_1_1LBPHFaceRecognizer.html#a50a3e2ca6e8464166e153c9df84b0a77 func (fr *LBPHFaceRecognizer) GetNeighbors() int { + n := C.LBPHFaceRecognizer_GetNeighbors(fr.p) return int(n) @@ -153,7 +121,7 @@ func (fr *LBPHFaceRecognizer) GetNeighbors() int { // For further information, see: // https://docs.opencv.org/master/df/d25/classcv_1_1face_1_1LBPHFaceRecognizer.html#a62d94c75cade902fd3b487b1ef9883fc func (fr *LBPHFaceRecognizer) SetRadius(radius int) { - C.LBPHFaceRecognizer_SetRadius(fr.p, (C.int)(radius)) + C.LBPHFaceRecognizer_SetRadius(fr.p, C.int(radius)) } // SaveFile saves the trained model data to file. @@ -161,9 +129,7 @@ func (fr *LBPHFaceRecognizer) SetRadius(radius int) { // For further information, see: // https://docs.opencv.org/master/dd/d65/classcv_1_1face_1_1FaceRecognizer.html#a2adf2d555550194244b05c91fefcb4d6 func (fr *LBPHFaceRecognizer) SaveFile(fname string) { - cName := C.CString(fname) - defer C.free(unsafe.Pointer(cName)) - C.LBPHFaceRecognizer_SaveFile(fr.p, cName) + faceRecognizer_SaveFile(C.FaceRecognizer(fr.p), fname) } // LoadFile loads a trained model data from file. @@ -171,9 +137,7 @@ func (fr *LBPHFaceRecognizer) SaveFile(fname string) { // For further information, see: // https://docs.opencv.org/master/dd/d65/classcv_1_1face_1_1FaceRecognizer.html#acc42e5b04595dba71f0777c7179af8c3 func (fr *LBPHFaceRecognizer) LoadFile(fname string) { - cName := C.CString(fname) - defer C.free(unsafe.Pointer(cName)) - C.LBPHFaceRecognizer_LoadFile(fr.p, cName) + faceRecognizer_LoadFile(C.FaceRecognizer(fr.p), fname) } // SetGridX sets grid's X value @@ -218,5 +182,11 @@ func (fr *LBPHFaceRecognizer) SetGrid(p image.Point) { // GetGrid helper for GetGrid* functions func (fr *LBPHFaceRecognizer) GetGrid() image.Point { - return image.Point{X: fr.GetGridX(), Y: fr.GetGridY()} + return image.Pt(fr.GetGridX(), fr.GetGridY()) +} + +func (fr *LBPHFaceRecognizer) Close() error { + C.LBPHFaceRecognizer_Close(fr.p) + fr.p = nil + return nil } diff --git a/contrib/face.h b/contrib/face.h index 1888aa6b..96fa6e2c 100644 --- a/contrib/face.h +++ b/contrib/face.h @@ -12,8 +12,10 @@ extern "C" { #ifdef __cplusplus typedef cv::Ptr* LBPHFaceRecognizer; +typedef cv::Ptr* FaceRecognizer; #else typedef void* LBPHFaceRecognizer; +typedef void* FaceRecognizer; #endif struct PredictResponse { @@ -21,22 +23,28 @@ struct PredictResponse { double confidence; }; +bool FaceRecognizer_Empty(FaceRecognizer fr); +void FaceRecognizer_Train(FaceRecognizer fr, Mats images, IntVector labels); +void FaceRecognizer_Update(FaceRecognizer fr, Mats images, IntVector labels); +int FaceRecognizer_Predict(FaceRecognizer fr, Mat sample); +struct PredictResponse FaceRecognizer_PredictExtended(FaceRecognizer fr, Mat sample); +double FaceRecognizer_GetThreshold(FaceRecognizer fr); +void FaceRecognizer_SetThreshold(FaceRecognizer fr, double threshold); +void FaceRecognizer_SaveFile(FaceRecognizer fr, const char* filename); +void FaceRecognizer_LoadFile(FaceRecognizer fr, const char* filename); + + + LBPHFaceRecognizer CreateLBPHFaceRecognizer(); -void LBPHFaceRecognizer_Train(LBPHFaceRecognizer fr, Mats images, IntVector labels); -void LBPHFaceRecognizer_Update(LBPHFaceRecognizer fr, Mats images, IntVector labels); -int LBPHFaceRecognizer_Predict(LBPHFaceRecognizer fr, Mat sample); -struct PredictResponse LBPHFaceRecognizer_PredictExtended(LBPHFaceRecognizer fr, Mat sample); -double LBPHFaceRecognizer_GetThreshold(LBPHFaceRecognizer fr); -void LBPHFaceRecognizer_SetThreshold(LBPHFaceRecognizer fr, double threshold); void LBPHFaceRecognizer_SetRadius(LBPHFaceRecognizer fr, int radius); void LBPHFaceRecognizer_SetNeighbors(LBPHFaceRecognizer fr, int neighbors); -void LBPHFaceRecognizer_SaveFile(LBPHFaceRecognizer fr, const char* filename); -void LBPHFaceRecognizer_LoadFile(LBPHFaceRecognizer fr, const char* filename); int LBPHFaceRecognizer_GetNeighbors(LBPHFaceRecognizer fr); void LBPHFaceRecognizer_SetGridX(LBPHFaceRecognizer fr, int x); void LBPHFaceRecognizer_SetGridY(LBPHFaceRecognizer fr, int y); int LBPHFaceRecognizer_GetGridX(LBPHFaceRecognizer fr); int LBPHFaceRecognizer_GetGridY(LBPHFaceRecognizer fr); +void LBPHFaceRecognizer_Close(LBPHFaceRecognizer fr); + diff --git a/contrib/face_recognizer.go b/contrib/face_recognizer.go new file mode 100644 index 00000000..1b087e94 --- /dev/null +++ b/contrib/face_recognizer.go @@ -0,0 +1,109 @@ +package contrib + +/* +#include +#include "face.h" +*/ +import "C" +import ( + "unsafe" + + "gocv.io/x/gocv" +) + +type FaceRecognizer interface { + Empty() bool + Train(images []gocv.Mat, labels []int) + Update(newImages []gocv.Mat, newLabels []int) + Predict(sample gocv.Mat) int + PredictExtendedResponse(sample gocv.Mat) PredictResponse + GetThreshold() float32 + SetThreshold(threshold float32) + SaveFile(fname string) + LoadFile(fname string) + Close() error +} + +func faceRecognizer_Empty(fr C.FaceRecognizer) bool { + b := C.FaceRecognizer_Empty(fr) + return bool(b) +} + +func faceRecognizer_Train(fr C.FaceRecognizer, images []gocv.Mat, labels []int) { + cparams := []C.int{} + for _, v := range labels { + cparams = append(cparams, C.int(v)) + } + labelsVector := C.struct_IntVector{} + labelsVector.val = (*C.int)(&cparams[0]) + labelsVector.length = (C.int)(len(cparams)) + + cMatArray := make([]C.Mat, len(images)) + for i, r := range images { + cMatArray[i] = (C.Mat)(r.Ptr()) + } + matsVector := C.struct_Mats{ + mats: (*C.Mat)(&cMatArray[0]), + length: C.int(len(images)), + } + + C.FaceRecognizer_Train(fr, matsVector, labelsVector) +} + +func faceRecognizer_Update(fr C.FaceRecognizer, newImages []gocv.Mat, newLabels []int) { + cparams := []C.int{} + for _, v := range newLabels { + cparams = append(cparams, C.int(v)) + } + labelsVector := C.struct_IntVector{} + labelsVector.val = (*C.int)(&cparams[0]) + labelsVector.length = (C.int)(len(cparams)) + + cMatArray := make([]C.Mat, len(newImages)) + for i, r := range newImages { + cMatArray[i] = (C.Mat)(r.Ptr()) + } + matsVector := C.struct_Mats{ + mats: (*C.Mat)(&cMatArray[0]), + length: C.int(len(newImages)), + } + + C.FaceRecognizer_Update(fr, matsVector, labelsVector) +} + +func faceRecognizer_Predict(fr C.FaceRecognizer, sample gocv.Mat) int { + label := C.FaceRecognizer_Predict(fr, (C.Mat)(sample.Ptr())) + + return int(label) +} + +func faceRecognizer_PredictExtendedResponse(fr C.FaceRecognizer, sample gocv.Mat) PredictResponse { + respp := C.FaceRecognizer_PredictExtended(fr, (C.Mat)(sample.Ptr())) + resp := PredictResponse{ + Label: int32(respp.label), + Confidence: float32(respp.confidence), + } + + return resp +} + +func faceRecognizer_GetThreshold(fr C.FaceRecognizer) float32 { + t := C.FaceRecognizer_GetThreshold(fr) + return float32(t) +} + +func faceRecognizer_SetThreshold(fr C.FaceRecognizer, threshold float32) { + C.FaceRecognizer_SetThreshold(fr, (C.double)(threshold)) +} + +func faceRecognizer_SaveFile(fr C.FaceRecognizer, fname string) { + cName := C.CString(fname) + defer C.free(unsafe.Pointer(cName)) + C.FaceRecognizer_SaveFile(fr, cName) +} + +func faceRecognizer_LoadFile(fr C.FaceRecognizer, fname string) { + cName := C.CString(fname) + defer C.free(unsafe.Pointer(cName)) + C.FaceRecognizer_LoadFile(fr, cName) +} diff --git a/contrib/face_test.go b/contrib/face_test.go index 1b44bb8a..b01ac8de 100644 --- a/contrib/face_test.go +++ b/contrib/face_test.go @@ -14,6 +14,8 @@ func TestLBPHFaceRecognizer_Methods(t *testing.T) { t.Errorf("Invalid NewLBPHFaceRecognizer call %v", model) } + model.Empty() + labels := []int{1, 1, 1, 1, 2, 2, 2, 2} images := []gocv.Mat{ gocv.IMRead("./att_faces/s1/1.pgm", gocv.IMReadGrayScale), @@ -104,6 +106,7 @@ func TestLBPHFaceRecognizer_SetGridY_GetGridY(t *testing.T) { func TestLBPHFaceRecognizer_SetGrid_GetGrid(t *testing.T) { facer := NewLBPHFaceRecognizer() + defer facer.Close() facer.SetGrid(image.Point{7, 5}) if p := facer.GetGrid(); p.X != 7 || p.Y != 5 {