From 084788b79aeae8515bbe96486f738cf895e3fdff Mon Sep 17 00:00:00 2001 From: diegohce Date: Tue, 21 Jan 2025 08:48:43 -0300 Subject: [PATCH] added contrib.xobjdetect --- ROADMAP.md | 2 +- contrib/xobjdetect.cpp | 51 ++++++++++++++++++ contrib/xobjdetect.go | 108 +++++++++++++++++++++++++++++++++++++ contrib/xobjdetect.h | 35 ++++++++++++ contrib/xobjdetect_test.go | 36 +++++++++++++ persistence_filenode.go | 4 ++ persistence_filestorage.go | 4 ++ 7 files changed, 239 insertions(+), 1 deletion(-) create mode 100644 contrib/xobjdetect.cpp create mode 100644 contrib/xobjdetect.go create mode 100644 contrib/xobjdetect.h create mode 100644 contrib/xobjdetect_test.go diff --git a/ROADMAP.md b/ROADMAP.md index 9f091f06..0f6022ee 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -350,5 +350,5 @@ Your pull requests will be greatly appreciated! - [X] **wechat_qrcode. WeChat QR code detector for detecting and parsing QR code** - [ ] **xfeatures2d. Extra 2D Features Framework - WORK STARTED** - [ ] **ximgproc. Extended Image Processing - WORK STARTED** -- [ ] xobjdetect. Extended object detection +- [X] xobjdetect. Extended object detection - [X] **xphoto. Additional photo processing algorithms** diff --git a/contrib/xobjdetect.cpp b/contrib/xobjdetect.cpp new file mode 100644 index 00000000..b9561448 --- /dev/null +++ b/contrib/xobjdetect.cpp @@ -0,0 +1,51 @@ +#include "xobjdetect.h" + +WBDetector WBDetector_Create(){ + return new cv::Ptr(cv::xobjdetect::WBDetector::create()); +} + +void WBDetector_Close(WBDetector det){ + delete det; +} + +WBDetector_detect_result WBDetector_Detect(WBDetector det, Mat img){ + std::vector bb; + std::vector conf; + WBDetector_detect_result result; + + (*det)->detect(*img, bb, conf); + + Rect* result_rects = (Rect*)malloc(bb.size()*sizeof(Rect)); + float* result_confs = (float*)malloc(conf.size()*sizeof(float)); + + for(int i = 0; i < bb.size(); i ++) { + result_rects[i].x = bb[i].x; + result_rects[i].y = bb[i].y; + result_rects[i].width = bb[i].width; + result_rects[i].height = bb[i].height; + } + + for(int i = 0; i < conf.size(); i ++){ + result_confs[i] = conf[i]; + } + + result.bboxes.rects = result_rects; + result.bboxes.length = bb.size(); + + result.confidences.val = result_confs; + result.confidences.length = conf.size(); + + return result; +} + +void WBDetector_Read(WBDetector det, FileNode node){ + (*det)->read(*node); +} + +void WBDetector_Train(WBDetector det, const char* pos_samples, const char* neg_imgs){ + (*det)->train(pos_samples, neg_imgs); +} + +void WBDetector_Write(WBDetector det, FileStorage fs){ + (*det)->write(*fs); +} diff --git a/contrib/xobjdetect.go b/contrib/xobjdetect.go new file mode 100644 index 00000000..3aa5b3d9 --- /dev/null +++ b/contrib/xobjdetect.go @@ -0,0 +1,108 @@ +package contrib + +/* +#include +#include "xobjdetect.h" +*/ +import "C" +import ( + "image" + "unsafe" + + "gocv.io/x/gocv" +) + +// WBDetector is a wrapper around the cv::xobjdetect::WBDetector. +type WBDetector struct { + p C.WBDetector +} + +// NewWBDetector Creates a new WBDetector +// +// For further details, please see: +// https://docs.opencv.org/4.x/de/d0e/classcv_1_1xobjdetect_1_1WBDetector.html#a58377ae61694aac08ad842ac830972d9 +func NewWBDetector() WBDetector { + p := C.WBDetector_Create() + return WBDetector{p: p} +} + +// Close Releases WBDetector allocated resources. +func (det *WBDetector) Close() { + C.WBDetector_Close(det.p) +} + +// Detect Detect objects on image using WaldBoost detector +// +// img: Input image for detection +// +// Returns: +// +// Bounding boxes coordinates and +// Confidence values for bounding boxes output vector +// +// For further details, please see: +// https://docs.opencv.org/4.x/de/d0e/classcv_1_1xobjdetect_1_1WBDetector.html#ad19680e6545f49a9ca42dfc3457319e2 +func (det *WBDetector) Detect(img gocv.Mat) ([]image.Rectangle, []float32) { + + result := C.WBDetector_Detect(det.p, C.Mat(img.Ptr())) + + defer C.free(unsafe.Pointer(result.bboxes.rects)) + defer C.free(unsafe.Pointer(result.confidences.val)) + + cRects := unsafe.Slice(result.bboxes.rects, result.bboxes.length) + cConfs := unsafe.Slice(result.confidences.val, result.confidences.length) + + goRects := make([]image.Rectangle, int(result.bboxes.length)) + goConfs := make([]float32, int(result.confidences.length)) + + for i := 0; i < int(result.bboxes.length); i++ { + r := image.Rect(int(cRects[i].x), + int(cRects[i].y), + int(cRects[i].width), + int(cRects[i].height)) + + goRects = append(goRects, r) + } + + for i := 0; i < int(result.confidences.length); i++ { + goConfs[i] = float32(cConfs[i]) + } + + return goRects, goConfs +} + +// Read Read detector from gocv.FileNode +// +// For further details, please see: +// https://docs.opencv.org/4.x/de/d0e/classcv_1_1xobjdetect_1_1WBDetector.html#aef2df760f45d25aade518196986e139f +func (det *WBDetector) Read(node *gocv.FileNode) { + C.WBDetector_Read(det.p, C.FileNode(node.Ptr())) +} + +// Train WaldBoost detector. +// +// Parameters: +// +// posSamples: Path to directory with cropped positive samples +// +// negImgs: Path to directory with negative (background) images +// +// For further details, please see: +// https://docs.opencv.org/4.x/de/d0e/classcv_1_1xobjdetect_1_1WBDetector.html#a3720fb425a2d16f6cd0625a2d8bc563e +func (det *WBDetector) Train(posSamples string, negImgs string) { + + pos_samples := C.CString(posSamples) + neg_imgs := C.CString(negImgs) + defer C.free(unsafe.Pointer(pos_samples)) + defer C.free(unsafe.Pointer(neg_imgs)) + + C.WBDetector_Train(det.p, pos_samples, neg_imgs) +} + +// Write detector to gocv.FileStorage. +// +// For further details, please see: +// https://docs.opencv.org/4.x/de/d0e/classcv_1_1xobjdetect_1_1WBDetector.html#a7d85338895707904ae1ddb4374ec8dac +func (det *WBDetector) Write(fs *gocv.FileStorage) { + C.WBDetector_Write(det.p, C.FileStorage(fs.Ptr())) +} diff --git a/contrib/xobjdetect.h b/contrib/xobjdetect.h new file mode 100644 index 00000000..897980f3 --- /dev/null +++ b/contrib/xobjdetect.h @@ -0,0 +1,35 @@ +#ifndef _OPENCV3_XOBJDETECT_H_ +#define _OPENCV3_XOBJDETECT_H_ + +#ifdef __cplusplus +#include +#include +extern "C" { +#endif + +#include "../core.h" +#include "../persistence.h" + +typedef struct WBDetector_detect_result_t { + Rects bboxes; + FloatVector confidences; +} WBDetector_detect_result; + +#ifdef __cplusplus +typedef cv::Ptr* WBDetector; +#else +typedef void* WBDetector; +#endif + +WBDetector WBDetector_Create(); +void WBDetector_Close(WBDetector det); +WBDetector_detect_result WBDetector_Detect(WBDetector det, Mat img); +void WBDetector_Read(WBDetector det, FileNode node); +void WBDetector_Train(WBDetector det, const char* pos_samples, const char* neg_imgs); +void WBDetector_Write(WBDetector det, FileStorage fs); + +#ifdef __cplusplus +} +#endif + +#endif //_OPENCV3_XOBJDETECT_H_ diff --git a/contrib/xobjdetect_test.go b/contrib/xobjdetect_test.go new file mode 100644 index 00000000..f38af857 --- /dev/null +++ b/contrib/xobjdetect_test.go @@ -0,0 +1,36 @@ +package contrib + +import ( + "testing" + + "gocv.io/x/gocv" +) + +func TestWBDetector(t *testing.T) { + + img := gocv.IMRead("../images/face.jpg", gocv.IMReadAnyColor) + if img.Empty() { + t.Error("xobjdetect: cannot read image") + } + + det := NewWBDetector() + defer det.Close() + + det.Train("../images/gocvlogo.jpg", "../images/gocvlogo.png") + + det.Detect(img) + + fs := gocv.NewFileStorageWithParams("../testdata/WBDetector.json", gocv.FileStorageModeWrite, "utf-8") + defer fs.Close() + + fs.StartWriteStruct("gocv", gocv.FileNodeTypeSeq, "model") + + det.Write(fs) + + fs.EndWriteStruct() + + node := fs.GetFirstTopLevelNode() + + det.Read(node) + +} diff --git a/persistence_filenode.go b/persistence_filenode.go index c907cd58..a9f0e909 100644 --- a/persistence_filenode.go +++ b/persistence_filenode.go @@ -32,6 +32,10 @@ type FileNode struct { p C.FileNode } +func (fn *FileNode) Ptr() C.FileNode { + return fn.p +} + func (fn *FileNode) Empty() bool { return bool(C.FileNode_Empty(fn.p)) } diff --git a/persistence_filestorage.go b/persistence_filestorage.go index c0d08aee..fc94979f 100644 --- a/persistence_filestorage.go +++ b/persistence_filestorage.go @@ -200,3 +200,7 @@ func (fs *FileStorage) Root(streamIdx int) *FileNode { node_p := C.FileStorage_Root(fs.p, C.int(streamIdx)) return &FileNode{p: node_p} } + +func (fs *FileStorage) Ptr() C.FileStorage { + return fs.p +}