Video Analysis底层库拆分,sdk的go封装
chenshijun
2019-10-22 1b388e4b0207003630c326ba1e71af8c8746070f
Merge branch 'master' of ssh://192.168.5.5:29418/valib/gosdk

# Conflicts:
# csdk.cpp
# csrc/buz/face/face.cpp
# csrc/buz/face/face.h
4个文件已删除
10个文件已添加
5个文件已修改
1047 ■■■■ 已修改文件
c2go.go 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
csdk.cpp 80 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
csdk.h 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
csrc/all.hpp 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
csrc/buz/face/detector.cpp 60 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
csrc/buz/face/detector.h 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
csrc/buz/face/extractor.cpp 54 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
csrc/buz/face/extractor.h 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
csrc/buz/face/face.cpp 219 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
csrc/buz/face/face.h 43 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
csrc/buz/face/property.cpp 34 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
csrc/buz/face/property.h 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
csrc/buz/face/tracker.cpp 89 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
csrc/buz/face/tracker.h 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
csrc/buz/yolo/detector.cpp 139 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
csrc/buz/yolo/detector.h 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
csrc/buz/yolo/yolo.cpp 152 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
csrc/buz/yolo/yolo.h 41 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
gosdk.go 35 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
c2go.go
@@ -3,6 +3,10 @@
/*
#include <string.h>
#include "csdk_struct.h"
int get_facepos_size(){
    return sizeof(cFacePos);
}
*/
import "C"
import (
csdk.cpp
@@ -12,29 +12,24 @@
using namespace csdk_wrap;
static sdkface *face = NULL;
static sdkyolo *yolo = NULL;
static VecFunc dtors_;
int c_api_face_detector_init(const int tm, const int gi, const int minFaces, const int rollAngle){
    if (!face) face = new sdkface();
    return face->init_face_detector(tm, gi, minFaces, rollAngle);
    return init_face_detector(tm, gi, minFaces, rollAngle, dtors_);
}
int c_api_face_property_init(const int tm){
    if (!face) face = new sdkface();
    return face->init_face_property(tm);
    return init_face_property(tm, dtors_);
}
int c_api_face_extractor_init(const int tm, const int gi){
    if (!face) face = new sdkface();
    return face->init_face_extractor(tm, gi);
    return init_face_extractor(tm, gi, dtors_);
}
int c_api_face_tracker_init(const int tm, const int gi, const int wid, const int hei,
                            const int maxFaces, const int detinterval, const int sampleSize){
    if (!face) face = new sdkface();
    if (face) printf("create sdk face success\n");
    return face->init_face_tracker(tm, gi, wid, hei, maxFaces, detinterval, sampleSize);
                              const int maxFaces, const int detinterval, const int sampleSize){
   return init_face_tracker(tm, gi, wid, hei, maxFaces, detinterval, sampleSize, dtors_);
}
int c_api_face_track_resize(const int chan, const int wid, const int hei){
@@ -44,76 +39,65 @@
YoloHandle c_api_yolo_init(
    const char *fcfg, const char *fweights, const char *fname,
    const int gpu_index){
    if (!yolo) yolo = new sdkyolo;
    return yolo->init_yolo(fcfg, fweights, fname, gpu_index);
    return init_yolo_detector(fcfg, fweights, fname, gpu_index, dtors_);
}
void c_api_release(){
    if (face) delete face;
    if (yolo) delete yolo;
    for(auto &i : dtors_){
        i();
    }
    dtors_.clear();
}
////////////////////////////////////////////////
cFacePos* c_api_face_detect(int *faceCount, uchar*data, const int w, const int h, const int channel){
    if (!face) return NULL;
    const cIMAGE img{data, w, h, 3};
    cFacePos *fpos = NULL;
    int ret = face->face_detect(&img, channel, (void**)&fpos, faceCount);
    if (ret <= 0) return NULL;
    return fpos;
    return face_detect(faceCount, &img, channel);
}
cThftResult c_api_face_property(const cFacePos* pos, uchar*data, const int w, const int h, const int channel){
    if (!face) return cThftResult{-1,-1,-1,-1,-1};
    const cIMAGE img{data, w, h, 3};
    return face->face_property(*pos, &img, channel);
    return face_property(*pos, &img, channel);
}
uchar* c_api_face_extract(int *featLen, const cFacePos* pos, uchar*data, const int w, const int h, const int channel){
    if (!face) return NULL;
    const cIMAGE img{data, w, h, 3};
    uchar *feat = NULL;
    int ret = face->face_extract_feature(*pos, &img, channel, (void**)&feat, featLen);
    if (ret <= 0) return NULL;
    return feat;
    return face_extract_feature(featLen, *pos, &img, channel);
}
float c_api_face_compare(uchar *feat1, uchar *feat2){
    if (!face) return NULL;
    return face->face_compare(feat1, feat2);
    return face_compare(feat1, feat2);
}
cRECT* c_api_face_track_only(int *fCount, uchar *data, const int wid, const int hei, const int channel){
    const cIMAGE img{data, wid, hei, 3};
    return face_track_only(fCount, &img, channel);
}
cFaceInfo* c_api_face_track_detect(int *fCount, uchar *data, const int wid, const int hei, const int channel){
    const cIMAGE img{data, wid, hei, 3};
    return face_track_detect(fCount, &img, channel);
}
cFaceInfo* c_api_face_track(int *fCount, uchar *data, const int wid, const int hei, const int channel){
    if (!face) return NULL;
    const cIMAGE img{data, wid, hei, 3};
    cFaceInfo *info = NULL;
    int ret = face->face_track(&img, channel, (void**)&info, fCount);
    if (ret <= 0) return NULL;
    return info;
    return face_track(fCount, &img, channel);
}
/// yolo api
cObjInfo* c_api_yolo_detect(YoloHandle handle, int *objCount, uchar*data, const int w, const int h, const float thrsh, const int use_means){
    if (!yolo) return NULL;
    const cIMAGE img{data, w, h, 3};
    cObjInfo *info = NULL;
    int ret = yolo->yolo_detect(handle, &img, thrsh, use_means, (void**)&info, objCount);
    if (ret <= 0) return NULL;
    return info;
    return yolo_detect(handle, objCount, &img, thrsh, use_means);
}
const char* c_api_yolo_obj_name(const int typ){
    if (!yolo) return NULL;
    return yolo->yolo_obj_name_by_type(typ);
    return yolo_obj_name_by_type(typ);
}
csdk.h
@@ -32,6 +32,8 @@
uchar* c_api_face_extract(int *featLen, const cFacePos* pos, uchar*data, const int w, const int h, const int channel);
float c_api_face_compare(uchar *feat1, uchar *feat2);
cRECT* c_api_face_track_only(int *fCount, uchar *data, const int wid, const int hei, const int channel);
cFaceInfo* c_api_face_track_detect(int *fCount, uchar *data, const int wid, const int hei, const int channel);
cFaceInfo* c_api_face_track(int *fCount, uchar *data, const int wid, const int hei, const int channel);
/// yolo api
csrc/all.hpp
@@ -1,7 +1,11 @@
#ifndef _c_wrapper_face_detector_all_hpp_
#define _c_wrapper_face_detector_all_hpp_
#include "buz/face/face.cpp"
#include "buz/yolo/yolo.cpp"
#include "buz/face/detector.cpp"
#include "buz/face/extractor.cpp"
#include "buz/face/property.cpp"
#include "buz/face/tracker.cpp"
#include "buz/yolo/detector.cpp"
#endif
csrc/buz/face/detector.cpp
New file
@@ -0,0 +1,60 @@
#include "detector.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "THFaceImage_i.h"
#include "csdk_struct.h"
namespace csdk_wrap
{
    int init_face_detector(const int tm, const int gi, const int minFaces, const int rollAngle,
                            VecFunc &vec){
        int ret = 0;
          if (gi < 0) {
              THFI_Param *param = new THFI_Param[tm];
              ret = THFI_Create(tm, param);
              delete[] param;
          } else {
              THFI_Param_Ex *param = new THFI_Param_Ex[tm];
              THFI_Param detParam;
              detParam.nMinFaceSize = minFaces;
              detParam.nRollAngle = rollAngle;
              for (int i = 0; i < tm; i++) {
                  param[i].tp = detParam;
                  param[i].nDeviceID = gi;
              }
              ret = THFI_Create_Ex(tm, param);
              delete[] param;
          }
          if(ret != tm){
              printf("create face detector failed!\n");
          }else{
              vec.emplace_back([]{THFI_Release();});
          }
          return ret;
    }
    cFacePos* face_detect(int *faceCount, const cIMAGE *img, const int channel){
        if(channel < 0 || !img){
            return NULL;
        }
        cFacePos *fpos = NULL;
        ::THFI_FacePos facesPos[30];
        int faceNum = THFI_DetectFace(channel, (BYTE*)(img->data), 24, img->width, img->height, facesPos, 30);
        if (faceNum > 0) {
            fpos =  (cFacePos*)malloc(faceNum * sizeof(cFacePos));
            *faceCount = faceNum;
            memcpy(fpos, facesPos, sizeof(THFI_FacePos) * faceNum);
        }else{
            // printf ("no face detected\n");
        }
        return fpos;
    }
} // csdk_wrap
csrc/buz/face/detector.h
New file
@@ -0,0 +1,18 @@
#ifndef _c_wrapper_face_detector_hpp_
#define _c_wrapper_face_detector_hpp_
#include "../base.hpp"
struct _cFacePos;
struct _cIMAGE;
namespace csdk_wrap{
    int init_face_detector(const int tm, const int gi, const int minFaces, const int rollAngle,
                            VecFunc &vec);
    cFacePos* face_detect(int *faceCount, const cIMAGE *img, const int channel);
}
#endif
csrc/buz/face/extractor.cpp
New file
@@ -0,0 +1,54 @@
#include "extractor.h"
#include "THFeature_i.h"
#include "csdk_struct.h"
namespace csdk_wrap{
    int init_face_extractor(const int tm, const int gi, VecFunc &vec){
        int ret = 0;
        if (gi < 0) {
            ret = EF_Init(tm);
        } else {
            EF_Param *param = new EF_Param[tm];
            for (int i = 0; i < tm; i++) {
                param[i].nDeviceID = gi;
            }
            ret = EF_Init_Ex(tm, param);
            delete[] param;
        }
        if(ret != tm){
            printf("create face extractor failed!\n");;
        }else{
            vec.emplace_back([]{EF_Release();});
        }
        return ret;
    }
    unsigned char* face_extract_feature(int *featLen, const cFacePos &pos, const cIMAGE *img, const int chan){
        if(!img){
            printf("face extract error, image or pos null\n");
            return NULL;
        }
        *featLen = EF_Size();
        unsigned char *feat = (unsigned char*)malloc(*featLen);
        auto ret = EF_Extract(chan, (BYTE*)(img->data), img->width, img->height, 3, (THFI_FacePos*)(&pos), feat);
        if(ret != 1){
            printf("face extract error %d\n", ret);
            free(feat);
            *featLen = 0;
            return NULL;
        }
        return feat;
    }
    float face_compare(unsigned char *feat1, unsigned char *feat2){
        if (!feat1 || !feat2){
            return 0.0f;
        }
        return EF_Compare(feat1, feat2);
    }
}
csrc/buz/face/extractor.h
New file
@@ -0,0 +1,14 @@
#ifndef _c_wrapper_face_extractor_h_
#define _c_wrapper_face_extractor_h_
#include "../base.hpp"
struct _cFacePos;
struct _cIMAGE;
namespace csdk_wrap{
    int init_face_extractor(const int tm, const int gi, VecFunc &vec);
    unsigned char* face_extract_feature(int *featLen, const cFacePos &pos, const cIMAGE *img, const int chan);
    float face_compare(unsigned char *feat1, unsigned char *feat2);
}
#endif
csrc/buz/face/face.cpp
File was deleted
csrc/buz/face/face.h
File was deleted
csrc/buz/face/property.cpp
New file
@@ -0,0 +1,34 @@
#include "property.h"
#include "THFaceProperty_i.h"
#include "csdk_struct.h"
namespace csdk_wrap{
    int init_face_property(const int tm, VecFunc &vec){
        auto ret = THFP_Create(tm);
        if(ret != tm){
            printf("create face property error\n");
        }else{
            vec.emplace_back([]{THFP_Release();});
        }
        return ret;
    }
    cThftResult face_property(const cFacePos &pos, const cIMAGE *img, const int chan){
        cThftResult result;
        result.gender = result.age = result.race = -1;
        auto ret = THFP_Execute_V2(chan, (BYTE*)(img->data), img->width, img->height,
            (THFI_FacePos*)(&pos), (THFP_Result_V2*)(&result));
        if(ret == 0){
            // printf("property face gender %s, age %d, race %s, beauty level %d, smile_level %d\n",
            // result.gender ?"male":"female",
            // result.age,
            // result.race==2?"yello":"other",
            // result.beauty_level, result.smile_level);
        }
        return result;
    }
}
csrc/buz/face/property.h
New file
@@ -0,0 +1,14 @@
#ifndef _c_wrapper_face_property_h_
#define _c_wrapper_face_property_h_
#include "../base.hpp"
struct _cThftResult;
struct _cFacePos;
struct _cIMAGE;
namespace csdk_wrap{
    int init_face_property(const int tm, VecFunc &vec);
    cThftResult face_property(const cFacePos &pos, const cIMAGE *img, const int chan);
}
#endif
csrc/buz/face/tracker.cpp
New file
@@ -0,0 +1,89 @@
#include "tracker.h"
#include <stdio.h>
#include "THFaceTracking_i.h"
namespace csdk_wrap{
    static THFT_Param param;
    int init_face_tracker(const int tm, const int gi,const int w, const int h,
                    const int maxFaces, const int detinterval,  const int sampleSize,
                              VecFunc &vec){
        param.nDeviceID = gi;
        param.nImageWidth = w;
        param.nImageHeight = h;
        param.nMaxFaceNum = maxFaces;
        param.nSampleSize = sampleSize > 0 ? sampleSize : w/2;
        param.nDetectionIntervalFrame = detinterval;
printf("threads %d gi: %d size: %dx%d maxface: %d, sample: %d, interval: %d\n",
            tm, gi, w, h, maxFaces, sampleSize, detinterval);
        auto nNum = THFT_Create(tm, &param);
        if(nNum != tm){
            printf("create face detector failed!\n");
        }else{
            vec.emplace_back([]{THFT_Release();});
        }
        return nNum;
    }
    cRECT* face_track_only(int *faceCount, const cIMAGE *img, const int chan){
        *faceCount = 0;
        cRECT *pFaces = (cRECT*)malloc(param.nMaxFaceNum * sizeof(cRECT));
        auto nNum = THFT_FaceOnly(chan, img->data, img->width, img->height, (tagRECT*)pFaces, param.nMaxFaceNum, param.nSampleSize);
        if (nNum > 0)
        {
            *faceCount = nNum;
        }else{
            free(pFaces);
            pFaces = NULL;
        }
        return pFaces;
    }
    cFaceInfo* face_track_detect(int *faceCount, const cIMAGE *img, const int chan){
        *faceCount = 0;
        cFaceInfo* pFaceInfos = (cFaceInfo*)malloc(param.nMaxFaceNum * sizeof(cFaceInfo));
        auto nNum = THFT_FaceDetect(chan, img->data, img->width, img->height, (THFT_FaceInfo*)pFaceInfos, param.nMaxFaceNum, param.nSampleSize);
        if (nNum > 0){
            *faceCount = nNum;
        }else{
            free(pFaceInfos);
            pFaceInfos = NULL;
        }
        return pFaceInfos;
    }
    cFaceInfo* face_track(int *faceCount, const cIMAGE *img, const int chan){
        *faceCount = 0;
        cFaceInfo* pFaceInfos = (cFaceInfo*)malloc(param.nMaxFaceNum * sizeof(cFaceInfo));
        auto nNum = THFT_FaceTracking(chan, img->data, (THFT_FaceInfo*)pFaceInfos);
        if (nNum > 0){
            *faceCount = nNum;
        }else{
            free(pFaceInfos);
            pFaceInfos = NULL;
        }
        return pFaceInfos;
    }
    //THFACETRACKING_API int    THFT_Reset(short nChannelID, THFT_Param* pParam);
    int sdkface::face_track_resize(const int chan, const int w, const int h){
        THFT_Param tmpParam;
        tmpParam.nDeviceID = param.nDeviceID;
        tmpParam.nImageWidth = w;
        tmpParam.nImageHeight = h;
        tmpParam.nMaxFaceNum = param.nMaxFaceNum;
        tmpParam.nSampleSize = param.nSampleSize;
        tmpParam.nDetectionIntervalFrame = param.nDetectionIntervalFrame;
        printf("chan %d size: %dx%d", chan, w, h);
        auto flag = THFT_Reset(chan, &tmpParam);
        return flag;
    }
}
csrc/buz/face/tracker.h
New file
@@ -0,0 +1,20 @@
#ifndef _c_wrapper_face_tracker_h_
#define _c_wrapper_face_tracker_h_
#include "../base.hpp"
struct _cRECT;
struct _cFaceInfo;
struct _cIMAGE;
namespace csdk_wrap{
    int init_face_tracker(const int tm, const int gi,const int w, const int h,
                              const int maxFaces, const int detinterval, const int sampleSize,
                              VecFunc &vec);
    cRECT* face_track_only(int *faceCount, const cIMAGE *img, const int chan);
    cFaceInfo* face_track_detect(int *faceCount, const cIMAGE *img, const int chan);
    cFaceInfo* face_track(int *faceCount, const cIMAGE *img, const int chan);
    int face_track_resize(const int chan, const int w, const int h);
}
#endif
csrc/buz/yolo/detector.cpp
New file
@@ -0,0 +1,139 @@
#include "detector.h"
#include <stdlib.h>
#include <fstream>
#include <sys/time.h>
#include "csdk_struct.h"
#include "yolo.hpp"
namespace csdk_wrap{
    static std::vector<std::string> names;
    static void objects_names_from_file(std::string filename) {
        std::ifstream file(filename);
        if (!file.is_open()){
            printf("open %s file error\n", filename.c_str());
            return;
        }
        for(std::string line; getline(file, line);) names.push_back(line);
        printf("names count %d\n", names.size());
    }
    void* init_yolo_detector(const char *cfg, const char *weights, const char *name,
                    const int gpu_index, VecFunc &vec){
        if(!cfg || !weights || !name){
            printf("init Detector error\n");
            return NULL;
        }
        if(names.empty())
            objects_names_from_file(name);
        auto det = new Detector(cfg, weights, gpu_index);
        vec.emplace_back([det]{delete det;});
        return det;
    }
    image_t* buffer_to_image(const unsigned char *data, const int w, const int h, const int color_channel){
        int size = w*h;
        int size2 = size*2;
        int c = color_channel;
        image_t *img = new image_t;
        img->h = h;
        img->w = w;
        img->c = color_channel;
        img->data = (float*)calloc(h*w*c, sizeof(float));
        // image im = make_image(w, h, c);
        const unsigned char *srcData = data;
        int count = 0;
        switch(c){
            case 1:{
                for (; count < size; ){
                    img->data[count] =
                    img->data[w*h + count] =
                    img->data[w*h*2 + count] =
                    (float)(srcData[count])/255.0;
                    ++count;
                }
                break;
            }
            case 3:{
                float* desData = img->data;
                for(int i = 0;i<size;i++){
                    *(desData) = *(srcData + 2) /255.0f;
                    *(desData+size) = *(srcData + 1) /255.0f;
                    *(desData+size2) = *(srcData) /255.0f;
                    desData++;
                    srcData+=3;
                }
                break;
            }
            default:
                printf("Channel number not supported.\n");
                break;
        }
        return img;
    }
    cObjInfo* yolo_detect(void *handle,int *objCount, const cIMAGE *img,  const float thrsh, const bool use_mean){
        Detector *det = (Detector*)handle;
        const int color_channel = img->channel;
        image_t* im = buffer_to_image(img->data, img->width, img->height, color_channel);
        // struct timeval b,e;
        // gettimeofday(&b, NULL);
        std::vector<bbox_t> result_vec = det->detect(*im, thrsh, use_mean);
        det->free_image(*im);
        delete im;
        // gettimeofday(&e,NULL);
        // double t = e.tv_sec*1000.0 + e.tv_usec/1000.0 - b.tv_sec*1000.0-b.tv_usec/1000.0;
        // printf("lib yolo detect use time %f ms\n", t);
        cObjInfo *infos = NULL;
        if(!result_vec.empty()){
            infos = (cObjInfo*)malloc(result_vec.size() * sizeof(cObjInfo));
        }
        int count = 0;
        for(auto &i : result_vec){
            cObjInfo info;
            info.typ = i.obj_id;
            info.prob = i.prob;
            info.rcObj.left = i.x;
            info.rcObj.top = i.y;
            info.rcObj.right = i.x+i.w;
            info.rcObj.bottom = i.y+i.h;
            infos[count++] = info;
        }
        *objCount = count;
        return infos;
    }
    const char* yolo_obj_name_by_type(const int typ){
        if(names.empty() || typ < 0 || typ >= names.size()) return NULL;
        return names.at(typ).c_str();
    }
}
csrc/buz/yolo/detector.h
New file
@@ -0,0 +1,21 @@
#ifndef _c_wrapper_yolo_detector_h_
#define _c_wrapper_yolo_detector_h_
#include "../base.hpp"
struct _cObjInfo;
struct _cIMAGE;
struct image_t;
namespace csdk_wrap{
    void* init_yolo_detector(const char *cfg, const char *weights, const char *name,
                    const int gpu_index, VecFunc &vec);
    image_t* buffer_to_image(const unsigned char *data, const int w, const int h, const int color_channel);
    cObjInfo* yolo_detect(void *handle,int *objCount, const cIMAGE *img, const float thrsh, const bool use_mean);
    const char* yolo_obj_name_by_type(const int typ);
}
#endif
csrc/buz/yolo/yolo.cpp
File was deleted
csrc/buz/yolo/yolo.h
File was deleted
gosdk.go
@@ -90,6 +90,7 @@
    var count C.int
    cfpos := C.c_api_face_detect(&count, (*C.uchar)(unsafe.Pointer(&data[0])), C.int(w), C.int(h), C.int(ch))
    if cfpos != nil {
        defer C.free(unsafe.Pointer(cfpos))
        return CFacePosArrayToGoArray(unsafe.Pointer(cfpos), int(count))
    }
    return nil
@@ -118,6 +119,7 @@
    pos := (*C.cFacePos)(unsafe.Pointer(&fpos))
    p := C.c_api_face_extract(&featLen, pos, (*C.uchar)(unsafe.Pointer(&data[0])), C.int(w), C.int(h), C.int(ch))
    defer C.free(unsafe.Pointer(p))
    b := C.GoBytes(unsafe.Pointer(p), featLen)
    return b
}
@@ -126,6 +128,36 @@
func FaceCompare(feat1 []byte, feat2 []byte) float32 {
    res := C.c_api_face_compare((*C.uchar)(unsafe.Pointer(&feat1[0])), (*C.uchar)(unsafe.Pointer(&feat2[0])))
    return float32(res)
}
// FaceTrackOnly face tracker face only
func FaceTrackOnly(img SDKImage, ch int) []CRECT {
    data := img.Data
    w := img.Width
    h := img.Height
    var fCount C.int
    rect := C.c_api_face_track_only(&fCount, (*C.uchar)(unsafe.Pointer(&data[0])), C.int(w), C.int(h), C.int(ch))
    if rect != nil {
        defer C.free(unsafe.Pointer(rect))
        return CRECTArrayToGoArray(unsafe.Pointer(rect), int(fCount))
    }
    return nil
}
// FaceTrackDetect face tracker face detect
func FaceTrackDetect(img SDKImage, ch int) []CFaceInfo {
    data := img.Data
    w := img.Width
    h := img.Height
    var fCount C.int
    finfo := C.c_api_face_track_only(&fCount, (*C.uchar)(unsafe.Pointer(&data[0])), C.int(w), C.int(h), C.int(ch))
    if finfo != nil {
        defer C.free(unsafe.Pointer(finfo))
        return CFaceInfoArrayToGoArray(unsafe.Pointer(finfo), int(fCount))
    }
    return nil
}
// FaceTrackingInfo face track info
@@ -145,6 +177,7 @@
    if cFinfo == nil {
        return
    }
    defer C.free(unsafe.Pointer(cFinfo))
    goFinfo := CFaceInfoArrayToGoArray(unsafe.Pointer(cFinfo), int(fCount))
    // 空,添加
@@ -217,6 +250,7 @@
    if cFinfo == nil {
        return faces
    }
    defer C.free(unsafe.Pointer(cFinfo))
    faces = CFaceInfoArrayToGoArray(unsafe.Pointer(cFinfo), int(fCount))
    //if len(faces) > 0{
    // fmt.Println("faces detected:", len(faces))
@@ -237,6 +271,7 @@
    cobjinfo := C.c_api_yolo_detect(handle.handle, &count, (*C.uchar)(unsafe.Pointer(&data[0])), C.int(w), C.int(h), C.float(thrsh), C.int(umns))
    if cobjinfo != nil {
        defer C.free(unsafe.Pointer(cobjinfo))
        return CYoloObjInfoArrayToGoArray(unsafe.Pointer(cobjinfo), int(count))
    }
    return nil