• 6240阅读
  • 17回复

Qt编写百度离线版人脸识别+比对+活体检测 [复制链接]

上一主题 下一主题
离线liudianwu
 

图酷模式  只看楼主 倒序阅读 楼主  发表于: 2018-09-15
在AI技术发展迅猛的今天,很多设备都希望加上人脸识别功能,好像不加上点人脸识别功能感觉不够高大上,都往人脸识别这边靠,手机刷脸解锁,刷脸支付,刷脸开门,刷脸金融,刷脸安防,是不是以后还可以刷脸匹配男女交友?
很多人认为人脸识别直接用opencv做,其实那只是极其基础的识别个人脸,然并卵,好比学C++写了个hello类似。拿到人脸区域图片只是万里长征的第一步,真正能够起作用的是人脸特征值的提取,然后用于搜索和查找人脸,比如两张图片比较相似度,从一堆人脸库中找到最相似的人脸,对当前人脸识别是否是活体等。
对于可以接入外网的设备,可以直接通过在线api的http请求方式获得结果,但是有很多应用场景是离线的,或者说不通外网,只能局域网使用,为了安全性考虑,这个时候就要求所有的人脸处理在本地完成,本篇文章采用的百度离线SDK作为解决方案。可以去官网申请,默认有6个免费的密钥使用三个月,需要与本地设备的指纹信息匹配,感兴趣的同学可以自行去官网下载SDK。
百度离线人脸识别SDK文件比较大,光模型文件就645MB,估计这也许是识别率比较高的一方面原因吧,不断训练得出的模型库,本篇文章只放出Qt封装部分源码。官网对应的使用说明还是非常详细的,只要是学过编程的人就可以看懂。
第一步:初始化SDK
第二步:执行动作,比如查找人脸、图片比对、特征值比对等


完成头文件代码:
  1. #ifndef FACEBAIDULOCAL_H
  2. #define FACEBAIDULOCAL_H
  3. /**
  4. * 百度离线版人脸识别+人脸比对等功能类 作者:feiyangqingyun(QQ:517216493) 2018-8-30
  5. * 1:支持活体检测
  6. * 2:可设置最大队列中的图片数量
  7. * 3:多线程处理,通过type控制当前处理类型
  8. * 4:支持单张图片检索相似度最高的图片
  9. * 5:支持指定目录图片生成特征文件
  10. * 6:支持两张图片比对方式
  11. * 7:可设置是否快速查找
  12. * 8:可设置是否统计用时
  13. */
  14. #include <QtCore>
  15. #include <QtGui>
  16. #if (QT_VERSION > QT_VERSION_CHECK(5,0,0))
  17. #include <QtWidgets>
  18. #endif
  19. #include "baidu_face_api.h"
  20. class FaceBaiDuLocal : public QThread
  21. {
  22.     Q_OBJECT
  23. public:
  24.     static FaceBaiDuLocal *Instance();
  25.     explicit FaceBaiDuLocal(QObject *parent = 0);
  26.     ~FaceBaiDuLocal();
  27. protected:
  28.     void run();
  29. private:
  30.     static QScopedPointer<FaceBaiDuLocal> self;
  31.     BaiduFaceApi *api;
  32.     std::vector<TrackFaceInfo> *faces;
  33.     QMutex mutex;                   //锁对象
  34.     bool stopped;                   //线程停止标志位
  35.     int maxCount;                   //最大图片张数
  36.     int type;                       //当前处理类型
  37.     int percent;                    //最小人脸百分比
  38.     int delayms;                    //减去毫秒数,用于造假
  39.     bool findFast;                  //是否快速模式
  40.     bool countTime;                 //统计用时
  41.     bool busy;                      //是否正忙
  42.     QList<QString> flags;           //等待处理的图像队列的名称
  43.     QList<QImage> imgs;             //等待处理的图像队列
  44.     QList<QImage> imgs2;            //等待处理的比对图像队列
  45.     QString sdkPath;                //SDK目录
  46.     QString imgDir;                 //图片目录
  47.     QImage oneImg;                  //单张图片比对找出最大特征图像
  48.     QList<QString> imgNames;        //图像队列
  49.     QList<QList<float> > features;  //特征队列
  50. signals:
  51.     //人脸区域坐标返回
  52.     void receiveFaceRect(const QString &flag, const QRect &rect, int msec);
  53.     //获取人脸区域坐标失败
  54.     void receiveFaceRectFail(const QString &flag);
  55.     //人脸特征返回
  56.     void receiveFaceFeature(const QString &flag, const QList<float> &feature, int msec);
  57.     //获取人脸特征失败
  58.     void receiveFaceFeatureFail(const QString &flag);
  59.     //人脸比对结果返回
  60.     void receiveFaceCompare(const QString &flag, float result, int msec);
  61.     //人脸比对失败
  62.     void receiveFaceCompareFail(const QString &flag);
  63.     //单张图片检索最大相似度结果返回
  64.     void receiveFaceCompareOne(const QString &flag, const QImage &srcImg, const QString &targetName, float result);
  65.     //所有人脸特征提取完毕
  66.     void receiveFaceFeatureFinsh();
  67.     //活体检测返回
  68.     void receiveFaceLive(const QString &flag, float result, int msec);
  69.     //活体检测失败
  70.     void receiveFaceLiveFail(const QString &flag);
  71. public slots:
  72.     //初始化SDK
  73.     void init();
  74.     //停止处理线程
  75.     void stop();
  76.     //获取当前是否忙
  77.     bool getBusy();
  78.     //设置图片队列最大张数
  79.     void setMaxCount(int maxCount);
  80.     //设置当前处理类型
  81.     void setType(int type);
  82.     //设置最小人脸百分比
  83.     void setPercent(int percent);
  84.     //设置减去毫秒数
  85.     void setDelayms(int delayms);
  86.     //设置是否快速模式
  87.     void setFindFast(bool findFast);
  88.     //设置是否统计用时
  89.     void setCountTime(bool countTime);
  90.     //设置是否忙
  91.     void setBusy(bool busy);
  92.     //设置SDK目录
  93.     void setSDKPath(const QString &sdkPath);
  94.     //设置要将图片提取出特征的目录
  95.     void setImgDir(const QString &imgDir);
  96.     //设置单张需要检索的图片
  97.     void setOneImg(const QString &flag, const QImage &oneImg);
  98.     //往队列中追加单张图片等待处理
  99.     void append(const QString &flag, const QImage &img);
  100.     //往队列中追加两张图片等待比对
  101.     void append(const QString &flag, const QImage &img, const QImage &img2);
  102.     //自动加载目录下的所有图片的特征
  103.     void getFaceFeatures(const QString &imgDir);
  104.     //获取人脸区域
  105.     bool getFaceRect(const QString &flag, const QImage &img, QRect &rect, int &msec);
  106.     //活体检测
  107.     bool getFaceLive(const QString &flag, const QImage &img, float &result, int &msec);
  108.     //获取人脸特征
  109.     bool getFaceFeature(const QString &flag, const QImage &img, QList<float> &feature, int &msec);
  110.     //人脸比对,传入两张照片特征
  111.     float getFaceCompare(const QString &flag, const QList<float> &feature1, const QList<float> &feature2);
  112.     //人脸比对,传入两张照片
  113.     bool getFaceCompare(const QString &flag, const QImage &img1, const QImage &img2, float &result, int &msec);
  114.     //从一堆图片中找到最像的一张图片
  115.     void getFaceOne(const QString &flag, const QImage &img, QString &targetName, float &result);
  116.     //指定特征找到照片
  117.     void getFaceOne(const QString &flag, const QList<float> &feature, QString &targetName, float &result);
  118.     //添加人脸
  119.     void appendFace(const QString &flag, const QImage &img, const QString &txtFile);
  120.     //删除人脸
  121.     void deleteFace(const QString &flag);
  122. };
  123. #endif // FACEBAIDULOCAL_H
完整实现文件代码:
  1. #include "facebaidulocal.h"
  2. #define TIMEMS qPrintable(QTime::currentTime().toString("HH:mm:ss zzz"))
  3. QByteArray getImageData(const QImage &image)
  4. {
  5.     QByteArray imageData;
  6.     QBuffer buffer(&imageData);
  7.     image.save(&buffer, "JPG");
  8.     imageData = imageData.toBase64();
  9.     return imageData;
  10. }
  11. QScopedPointer<FaceBaiDuLocal> FaceBaiDuLocal::self;
  12. FaceBaiDuLocal *FaceBaiDuLocal::Instance()
  13. {
  14.     if (self.isNull()) {
  15.         QMutex mutex;
  16.         QMutexLocker locker(&mutex);
  17.         if (self.isNull()) {
  18.             self.reset(new FaceBaiDuLocal);
  19.         }
  20.     }
  21.     return self.data();
  22. }
  23. FaceBaiDuLocal::FaceBaiDuLocal(QObject *parent) : QThread(parent)
  24. {
  25.     //注册信号中未知的数据类型
  26.     qRegisterMetaType<QList<float> >("QList<float>");
  27.     stopped = false;
  28.     maxCount = 100;
  29.     type = 1;
  30.     percent = 8;
  31.     delayms = 0;
  32.     findFast = false;
  33.     countTime = true;
  34.     busy = false;
  35.     sdkPath = qApp->applicationDirPath() + "/facesdk";
  36.     imgDir = "";
  37.     oneImg = QImage();
  38.     api = new BaiduFaceApi;
  39.     faces = new std::vector<TrackFaceInfo>();
  40. }
  41. FaceBaiDuLocal::~FaceBaiDuLocal()
  42. {
  43.     delete api;
  44.     this->stop();
  45.     this->wait(1000);
  46. }
  47. void FaceBaiDuLocal::run()
  48. {
  49.     this->init();
  50.     while(!stopped) {
  51.         int count = flags.count();
  52.         if (count > 0) {
  53.             QMutexLocker lock(&mutex);
  54.             busy = true;
  55.             if (type == 0) {
  56.                 QString flag = flags.takeFirst();
  57.                 QImage img = imgs.takeFirst();
  58.                 QRect rect;
  59.                 int msec;
  60.                 if (getFaceRect(flag, img, rect, msec)) {
  61.                     emit receiveFaceRect(flag, rect, msec);
  62.                 } else {
  63.                     emit receiveFaceRectFail(flag);
  64.                 }
  65.             } else if (type == 1) {
  66.                 QString flag = flags.takeFirst();
  67.                 QImage img = imgs.takeFirst();
  68.                 QList<float> feature;
  69.                 int msec;
  70.                 if (getFaceFeature(flag, img, feature, msec)) {
  71.                     emit receiveFaceFeature(flag, feature, msec);
  72.                 } else {
  73.                     emit receiveFaceFeatureFail(flag);
  74.                 }
  75.             } else if (type == 2) {
  76.                 QString flag = flags.takeFirst();
  77.                 QImage img1 = imgs.takeFirst();
  78.                 QImage img2 = imgs2.takeFirst();
  79.                 float result;
  80.                 int msec;
  81.                 if (getFaceCompare(flag, img1, img2, result, msec)) {
  82.                     emit receiveFaceCompare(flag, result, msec);
  83.                 } else {
  84.                     emit receiveFaceCompareFail(flag);
  85.                 }
  86.             } else if (type == 3) {
  87.                 flags.takeFirst();
  88.                 getFaceFeatures(imgDir);
  89.             } else if (type == 4) {
  90.                 QString flag = flags.takeFirst();
  91.                 QString targetName;
  92.                 float result;
  93.                 getFaceOne(flag, oneImg, targetName, result);
  94.                 if (!targetName.isEmpty()) {
  95.                     emit receiveFaceCompareOne(flag, oneImg, targetName, result);
  96.                 }
  97.             } else if (type == 5) {
  98.                 QString flag = flags.takeFirst();
  99.                 QImage img = imgs.takeFirst();
  100.                 float result;
  101.                 int msec;
  102.                 if (getFaceLive(flag, img, result, msec)) {
  103.                     emit receiveFaceLive(flag, result, msec);
  104.                 } else {
  105.                     emit receiveFaceLiveFail(flag);
  106.                 }
  107.             }
  108.         }
  109.         msleep(100);
  110.         busy = false;
  111.     }
  112.     stopped = false;
  113. }
  114. void FaceBaiDuLocal::init()
  115. {
  116.     int res = api->sdk_init();
  117.     res = api->is_auth();
  118.     if(res != 1) {
  119.         qDebug() << TIMEMS << QString("init sdk error: %1").arg(res);
  120.         return;
  121.     } else {
  122.         //设置最小人脸,默认30
  123.         api->set_min_face_size(percent);
  124.         //设置光照阈值,默认40
  125.         api->set_illum_thr(20);
  126.         //设置角度阈值,默认15
  127.         //api->set_eulur_angle_thr(30, 30, 30);
  128.         qDebug() << TIMEMS << "init sdk ok";
  129.     }
  130. }
  131. void FaceBaiDuLocal::stop()
  132. {
  133.     stopped = true;
  134. }
  135. bool FaceBaiDuLocal::getBusy()
  136. {
  137.     return this->busy;
  138. }
  139. void FaceBaiDuLocal::setMaxCount(int maxCount)
  140. {
  141.     if (maxCount <= 1000) {
  142.         this->maxCount = maxCount;
  143.     }
  144. }
  145. void FaceBaiDuLocal::setType(int type)
  146. {
  147.     if (this->type != type) {
  148.         this->type = type;
  149.         this->flags.clear();
  150.         this->imgs.clear();
  151.         this->imgs2.clear();
  152.     }
  153. }
  154. void FaceBaiDuLocal::setPercent(int percent)
  155. {
  156.     this->percent = percent;
  157. }
  158. void FaceBaiDuLocal::setDelayms(int delayms)
  159. {
  160.     this->delayms = delayms;
  161. }
  162. void FaceBaiDuLocal::setFindFast(bool findFast)
  163. {
  164.     this->findFast = findFast;
  165. }
  166. void FaceBaiDuLocal::setCountTime(bool countTime)
  167. {
  168.     this->countTime = countTime;
  169. }
  170. void FaceBaiDuLocal::setBusy(bool busy)
  171. {
  172.     this->busy = busy;
  173. }
  174. void FaceBaiDuLocal::setSDKPath(const QString &sdkPath)
  175. {
  176.     this->sdkPath = sdkPath;
  177. }
  178. void FaceBaiDuLocal::setImgDir(const QString &imgDir)
  179. {
  180.     this->imgDir = imgDir;
  181.     this->flags.clear();
  182.     this->flags.append("imgDir");
  183.     this->type = 3;
  184. }
  185. void FaceBaiDuLocal::setOneImg(const QString &flag, const QImage &oneImg)
  186. {
  187.     setType(4);
  188.     //需要将图片重新拷贝一个,否则当原图像改变之后也会改变
  189.     this->oneImg = oneImg.copy();
  190.     this->flags.append(flag);
  191. }
  192. void FaceBaiDuLocal::append(const QString &flag, const QImage &img)
  193. {
  194.     QMutexLocker lock(&mutex);
  195.     int count = flags.count();
  196.     if (count < maxCount) {
  197.         flags.append(flag);
  198.         imgs.append(img);
  199.     }
  200. }
  201. void FaceBaiDuLocal::append(const QString &flag, const QImage &img, const QImage &img2)
  202. {
  203.     QMutexLocker lock(&mutex);
  204.     int count = flags.count();
  205.     if (count < maxCount) {
  206.         flags.append(flag);
  207.         imgs.append(img);
  208.         imgs2.append(img2);
  209.     }
  210. }
  211. void FaceBaiDuLocal::getFaceFeatures(const QString &imgDir)
  212. {
  213.     imgNames.clear();
  214.     features.clear();
  215.     //载入指定目录图像处理特征
  216.     QDir imagePath(imgDir);
  217.     QStringList filter;
  218.     filter << "*.jpg" << "*.bmp" << "*.png" << "*.jpeg" << "*.gif";
  219.     imgNames.append(imagePath.entryList(filter));
  220.     qDebug() << TIMEMS << "getFaceFeatures" << imgNames;
  221.     //从目录下读取同名的txt文件(存储的特征)
  222.     //如果存在则从文件读取特征,如果不存在则转码解析出特征
  223.     //转码完成后将得到的特征存储到同名txt文件
  224.     int count = imgNames.count();
  225.     for (int i = 0; i < count; i++) {
  226.         QList<float> feature;
  227.         int msec;
  228.         QString imgName = imgNames.at(i);
  229.         QStringList list = imgName.split(".");
  230.         QString txtName = imgDir + "/" + list.at(0) + ".txt";
  231.         QFile file(txtName);
  232.         if (file.exists()) {
  233.             if (file.open(QFile::ReadOnly)) {
  234.                 QString data = file.readAll();
  235.                 file.close();
  236.                 qDebug() << TIMEMS << "readFaceFeature" << txtName;
  237.                 QStringList list = data.split(",");
  238.                 foreach (QString str, list) {
  239.                     if (!str.isEmpty()) {
  240.                         feature.append(str.toFloat());
  241.                     }
  242.                 }
  243.             }
  244.         } else {
  245.             QImage img(imgDir + "/" + imgName);
  246.             bool ok = getFaceFeature(imgName, img, feature, msec);
  247.             if (ok) {
  248.                 emit receiveFaceFeature(imgName, feature, msec);
  249.                 if (file.open(QFile::WriteOnly)) {
  250.                     QStringList list;
  251.                     foreach (float fea, feature) {
  252.                         list.append(QString::number(fea));
  253.                     }
  254.                     qDebug() << TIMEMS << "writeFaceFeature" << txtName;
  255.                     file.write(list.join(",").toLatin1());
  256.                     file.close();
  257.                 }
  258.             }
  259.         }
  260.         features.append(feature);
  261.         msleep(1);
  262.     }
  263.     qDebug() << TIMEMS << "getFaceFeatures finsh";
  264.     emit receiveFaceFeatureFinsh();
  265. }
  266. bool FaceBaiDuLocal::getFaceRect(const QString &flag, const QImage &img, QRect &rect, int &msec)
  267. {
  268.     //qDebug() << TIMEMS << flag << "getFaceRect";
  269.     QTime time;
  270.     if (countTime) {
  271.         time.start();
  272.     }
  273.     faces->clear();
  274.     QByteArray imageData = getImageData(img);
  275.     int result = api->track_max_face(faces, imageData.constData(), 1);
  276.     if (result == 1) {
  277.         TrackFaceInfo info = faces->at(0);
  278.         FaceInfo ibox = info.box;
  279.         float width = ibox.mWidth;
  280.         float x = ibox.mCenter_x;
  281.         float y = ibox.mCenter_y;
  282.         rect = QRect(x - width / 2, y - width / 2, width, width);
  283.         if (countTime) {
  284.             msec = time.elapsed() - delayms;
  285.         } else {
  286.             msec = delayms;
  287.         }
  288.         msec = msec < 0 ? 0 : msec;
  289.         return true;
  290.     } else {
  291.         return false;
  292.     }
  293.     return false;
  294. }
  295. bool FaceBaiDuLocal::getFaceLive(const QString &flag, const QImage &img, float &result, int &msec)
  296. {
  297.     //qDebug() << TIMEMS << flag << "getFaceLive";
  298.     QTime time;
  299.     if (countTime) {
  300.         time.start();
  301.     }
  302.     result = 0;
  303.     QByteArray imageData = getImageData(img);
  304.     std::string value = api->rgb_liveness_check(imageData.constData(), 1);
  305.     QString data = value.c_str();
  306.     data = data.replace("\t", "");
  307.     data = data.replace("\"", "");
  308.     data = data.replace(" ", "");
  309.     int index = -1;
  310.     QStringList list = data.split("\n");
  311.     foreach (QString str, list) {
  312.         index = str.indexOf("score:");
  313.         if (index >= 0) {
  314.             result = str.mid(6, 4).toFloat();
  315.             break;
  316.         }
  317.     }
  318.     if (index >= 0) {
  319.         if (countTime) {
  320.             msec = time.elapsed() - delayms;
  321.         } else {
  322.             msec = delayms;
  323.         }
  324.         msec = msec < 0 ? 0 : msec;
  325.         return true;
  326.     } else {
  327.         return false;
  328.     }
  329.     return false;
  330. }
  331. bool FaceBaiDuLocal::getFaceFeature(const QString &flag, const QImage &img, QList<float> &feature, int &msec)
  332. {
  333.     //qDebug() << TIMEMS << flag << "getFaceFeature" << img.width() << img.height() << img.size();
  334.     QTime time;
  335.     if (countTime) {
  336.         time.start();
  337.     }
  338.     const float *fea = nullptr;
  339.     QByteArray imageData = getImageData(img);
  340.     int result = api->get_face_feature(imageData.constData(), 1, fea);
  341.     if (result == 512) {
  342.         feature.clear();
  343.         for (int i = 0; i < 512; i++) {
  344.             feature.append(fea[i]);
  345.         }
  346.         if (countTime) {
  347.             msec = time.elapsed() - delayms;
  348.         } else {
  349.             msec = delayms;
  350.         }
  351.         msec = msec < 0 ? 0 : msec;
  352.         return true;
  353.     } else {
  354.         return false;
  355.     }
  356.     return false;
  357. }
  358. float FaceBaiDuLocal::getFaceCompare(const QString &flag, const QList<float> &feature1, const QList<float> &feature2)
  359. {
  360.     //qDebug() << TIMEMS << flag << "getFaceCompareXXX";
  361.     std::vector<float> fea1, fea2;
  362.     for (int i = 0; i < 512; i++) {
  363.         fea1.push_back(feature1.at(i));
  364.         fea2.push_back(feature2.at(i));
  365.     }
  366.     float result = api->compare_feature(fea1, fea2);
  367.     //过滤非法的值
  368.     result = result > 100 ? 0 : result;
  369.     return result;
  370. }
  371. bool FaceBaiDuLocal::getFaceCompare(const QString &flag, const QImage &img1, const QImage &img2, float &result, int &msec)
  372. {
  373.     //qDebug() << TIMEMS << flag << "getFaceCompare";
  374.     result = 0;
  375.     bool ok1, ok2;
  376.     QList<float> feature1, feature2;
  377.     int msec1, msec2;
  378.     QString flag1, flag2;
  379.     if (flag.contains("|")) {
  380.         QStringList list = flag.split("|");
  381.         flag1 = list.at(0);
  382.         flag2 = list.at(1);
  383.     } else {
  384.         flag1 = flag;
  385.         flag2 = flag;
  386.     }
  387.     QTime time;
  388.     if (countTime) {
  389.         time.start();
  390.     }
  391.     ok1 = getFaceFeature(flag1, img1, feature1, msec1);
  392.     if (ok1) {
  393.         emit receiveFaceFeature(flag1, feature1, msec1);
  394.     }
  395.     ok2 = getFaceFeature(flag2, img2, feature2, msec2);
  396.     if (ok2) {
  397.         emit receiveFaceFeature(flag2, feature2, msec2);
  398.     }
  399.     if (ok1 && ok2) {
  400.         result = getFaceCompare(flag, feature1, feature2);
  401.         if (countTime) {
  402.             msec = time.elapsed() - delayms;
  403.         } else {
  404.             msec = delayms;
  405.         }
  406.         msec = msec < 0 ? 0 : msec;
  407.         return true;
  408.     } else {
  409.         return false;
  410.     }
  411.     return false;
  412. }
  413. void FaceBaiDuLocal::getFaceOne(const QString &flag, const QImage &img, QString &targetName, float &result)
  414. {
  415.     QList<float> feature;
  416.     int msec;
  417.     bool ok = getFaceFeature(flag, img, feature, msec);
  418.     if (ok) {
  419.         emit receiveFaceFeature(flag, feature, msec);
  420.         getFaceOne(flag, feature, targetName, result);
  421.     }
  422. }
  423. void FaceBaiDuLocal::getFaceOne(const QString &flag, const QList<float> &feature, QString &targetName, float &result)
  424. {
  425.     //用当前图片的特征与特征数据库比对
  426.     result = 0;
  427.     int count = imgNames.count();
  428.     for (int i = 0; i < count; i++) {
  429.         QString imgName = imgNames.at(i);
  430.         float currentResult = getFaceCompare(flag, feature, features.at(i));
  431.         //qDebug() << TIMEMS << "getFaceOne" << imgName << currentResult;
  432.         if (currentResult > result) {
  433.             result = currentResult;
  434.             targetName = imgName;
  435.         }
  436.     }
  437.     qDebug() << TIMEMS << "getFaceOne result" << targetName << result;
  438. }
  439. void FaceBaiDuLocal::appendFace(const QString &flag, const QImage &img, const QString &txtFile)
  440. {
  441.     QList<float> feature;
  442.     int msec;
  443.     QImage image = img;
  444.     bool ok = getFaceFeature(flag, image, feature, msec);
  445.     msleep(100);
  446.     qDebug() << TIMEMS << "getFaceFeature result" << ok << "appendFace" << txtFile;
  447.     if (ok) {
  448.         emit receiveFaceFeature(flag, feature, msec);
  449.         //保存txt文件
  450.         QFile file(txtFile);
  451.         if (file.open(QFile::WriteOnly)) {
  452.             QStringList list;
  453.             foreach (float fea, feature) {
  454.                 list.append(QString::number(fea));
  455.             }
  456.             file.write(list.join(",").toLatin1());
  457.             file.close();
  458.         }
  459.         //保存图片文件
  460.         QString imgName = txtFile;
  461.         imgName = imgName.replace("txt", "jpg");
  462.         image.save(imgName, "jpg");
  463.         imgNames.append(QFileInfo(imgName).fileName());
  464.         features.append(feature);
  465.     }
  466. }
  467. void FaceBaiDuLocal::deleteFace(const QString &flag)
  468. {
  469.     //从图片名称中找到标识符
  470.     int index = imgNames.indexOf(flag);
  471.     if (index >= 0) {
  472.         imgNames.removeAt(index);
  473.         features.removeAt(index);
  474.         //删除图片文件
  475.         QString imgFileName = QString("%1/face/%2.jpg").arg(qApp->applicationDirPath()).arg(flag);
  476.         QFile imgFile(imgFileName);
  477.         imgFile.remove();
  478.         qDebug() << TIMEMS << "delete faceImage" << imgFileName;
  479.         //删除特征文件
  480.         QString txtFileName = QString("%1/face/%2.txt").arg(qApp->applicationDirPath()).arg(flag);
  481.         QFile txtFile(txtFileName);
  482.         txtFile.remove();
  483.         qDebug() << TIMEMS << "delete faceTxt" << txtFileName;
  484.     }
  485. }



欢迎关注微信公众号:Qt实战/Qt入门和进阶(各种开源作品、经验整理、项目实战技巧,专注Qt/C++软件开发,视频监控、物联网、工业控制、嵌入式软件、国产化系统应用软件开发) QQ:517216493  WX:feiyangqingyun  QQ群:751439350
离线crazy

只看该作者 1楼 发表于: 2018-09-16
一级棒 不懂,帮顶
C/C++/Qt爱好者
邮箱: kevinlq0912@163.com
公众号: devstone
博客:http://kevinlq.com/
离线自强不吸

只看该作者 2楼 发表于: 2018-09-17
    
自强不吸!
离线305750665

只看该作者 3楼 发表于: 2018-09-17
     刘大师威武
雨田哥: 群号:853086607
QQ: 3246214072

刘典武-feiyangqingyun:专业各种自定义控件编写+UI定制+输入法定制+视频监控+工业控制+仪器仪表+嵌入式linux+各种串口网络通信,童叟无欺,量大从优,欢迎咨询购买定制!QQ:517216493
离线john_wang

只看该作者 4楼 发表于: 2018-09-17
    
离线windzq

只看该作者 5楼 发表于: 2018-09-17
    
离线nigoole

只看该作者 6楼 发表于: 2018-09-17
  
有句话说得好:好好学习,天天向上。加油~~!有上船的朋友联系企鹅393320854
离线wmx菜鸟

只看该作者 7楼 发表于: 2018-09-17

只看该作者 8楼 发表于: 2018-09-17
还以为你发数据源的,没想到发这个离线版的,这个你不是老早就做好了
离线qibbs

只看该作者 9楼 发表于: 2018-09-17
     赞!
春梦觉来心自警,往事般般应。
离线liuchangyin

只看该作者 10楼 发表于: 2018-09-19
离线llwj0303

只看该作者 11楼 发表于: 2018-09-30
专注C++,专注Qt
离线greensky10

只看该作者 12楼 发表于: 2019-05-14
麻烦问下刘大师,图1中的红框是怎么画上去的呢?
离线hongbang518

只看该作者 13楼 发表于: 2019-05-15
    
机器视觉检测设备+成套软硬件解决方案+影像测量仪+三坐标测量机+Qt 软件开发定制+嵌入式linux+各种串口、网络通信,童叟无欺,量大从优,欢迎咨询购买定制!你正好需要,我正好专业!QQ:278792617 微信:hongbang106 Email:hongbang106@163.com
离线liudianwu

只看该作者 14楼 发表于: 2019-05-15
回 greensky10 的帖子
greensky10:麻烦问下刘大师,图1中的红框是怎么画上去的呢? (2019-05-14 18:17) 

painter到图片上的
欢迎关注微信公众号:Qt实战/Qt入门和进阶(各种开源作品、经验整理、项目实战技巧,专注Qt/C++软件开发,视频监控、物联网、工业控制、嵌入式软件、国产化系统应用软件开发) QQ:517216493  WX:feiyangqingyun  QQ群:751439350
离线greensky10

只看该作者 15楼 发表于: 2019-05-15
回 liudianwu 的帖子
liudianwu:painter到图片上的 (2019-05-15 10:18) 

那再请问下刘大师,如果这个窗口播放的是视频,如何在视频上叠加一个类似的红框呢?
离线liudianwu

只看该作者 16楼 发表于: 2019-05-15
回 greensky10 的帖子
greensky10:那再请问下刘大师,如果这个窗口播放的是视频,如何在视频上叠加一个类似的红框呢? (2019-05-15 16:33) 

视频也是图片,一样的,都是painter上去
欢迎关注微信公众号:Qt实战/Qt入门和进阶(各种开源作品、经验整理、项目实战技巧,专注Qt/C++软件开发,视频监控、物联网、工业控制、嵌入式软件、国产化系统应用软件开发) QQ:517216493  WX:feiyangqingyun  QQ群:751439350
离线greensky10

只看该作者 17楼 发表于: 2019-05-16
回 liudianwu 的帖子
liudianwu:视频也是图片,一样的,都是painter上去 (2019-05-15 16:52) 

感谢刘大神回复,我是用widget播放的视频,实际测试发现painter画的线都藏在视频下面了,不知道是不是我的打开方式有问题,还请刘大神再指点一二
快速回复
限100 字节
 
上一个 下一个