ubuntu下用Qt实现人脸识别(四)

一、流程

首先是程序启动的时候,从指定的路径下获取图片,然后从图片中提取出人脸特征值,将这些人脸特征值存储到缓冲区中。接着不断从摄像头获取一帧图片,传到人脸识别的算法接口,然后通过判别人脸阈值的大小,来确定是否识别成功。如果成功,在人脸框上显示该人脸的姓名。又因为,提取特征值和识别人脸的任务用时可能比较大,所以需要创建一条线程来执行这些任务。

二、源码

在 ubuntu下用Qt实现人脸识别之检测人脸并绘制人脸框(三)的基础下增加以下修改

主线程

主线程头文件代码如下(示例):

#ifndef HRQTPRO_H
#define HRQTPRO_H#include <QWidget>
#include <QImage>
#include <QPainter>
#include <QObject>
#include <QTimer>
#include <QDebug>#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/core/core.hpp"
#include "opencv2/objdetect/objdetect.hpp"
#include "opencv2/highgui.hpp"#include "ASF/inc/merror.h"
#include "ASF/inc/asvloffscreen.h"
#include "ASF/inc/arcsoft_face_sdk.h"
#include "ASF/inc/amcomdef.h"#include "services/detect/workpthread.h"
//激活SDK的ID和KEY
#define APP_ID ""
#define SDK_KEY ""using namespace cv;namespace Ui {class hrQtPro;
}class hrQtPro : public QWidget
{Q_OBJECTpublic:explicit hrQtPro(QWidget *parent = 0);~hrQtPro();protected:void paintEvent(QPaintEvent* e);private slots:int slotReadFarme();void slotRegisterFace();void slotFaceCompareFinished(QString name, QString ID, float scores);private:Ui::hrQtPro *ui;VideoCapture capture;QTimer  *cvTimer;Mat cap;QImage qImg;ASF_MultiFaceInfo detectedFaces;MHandle handle;//识别线程workPthread *thread1;//人脸姓名QString faceName;QString faceID;int ASFInit();int OpenCamera();QImage Mat2QImage(Mat &src);
};#endif // HRQTPRO_H

主线程cpp代码如下(示例):

#include "hrqtpro.h"
#include "ui_hrqtpro.h"hrQtPro::hrQtPro(QWidget *parent) :QWidget(parent),handle(NULL),ui(new Ui::hrQtPro)
{ui->setupUi(this);thread1 = new workPthread();connect(thread1, SIGNAL(faceCompareFinished(QString , QString ,float)),this, SLOT(slotFaceCompareFinished(QString , QString ,float)));thread1->start();//注册人脸按钮connect(ui->registerBtn, SIGNAL(clicked()), this, SLOT(slotRegisterFace()));cvTimer = new QTimer(this);connect(cvTimer, SIGNAL(timeout()), this, SLOT(slotReadFarme()));ASFInit();OpenCamera();
}hrQtPro::~hrQtPro()
{if(thread1) {thread1->slotDestroy(true);thread1->quit();thread1->wait(300);delete thread1;thread1 = nullptr;}MRESULT res = ASFUninitEngine(handle);if (res != MOK)printf("ASFUninitEngine fail: %d\n", res);elseprintf("ASFUninitEngine sucess: %d\n", res);if(cvTimer) {cvTimer->stop();delete cvTimer;cvTimer = NULL;}delete ui;
}int hrQtPro::ASFInit()
{MRESULT res = MOK;
#if 0//激活SDK,只需要激活一次即可//这里的APP_ID和SDK_KEY是从官网中获取到的res = ASFOnlineActivation(APP_ID, SDK_KEY);if (MOK != res && MERR_ASF_ALREADY_ACTIVATED != res)qDebug("ASFOnlineActivation fail: %d\n", res);elseqDebug("ASFOnlineActivation sucess: %d\n", res);
#endif//只是检测人脸,如果需要人脸识别还需要或上ASF_FACERECOGNITION,例如:ASF_FACE_DETECT | ASF_FACERECOGNITIONMInt32 mask = ASF_FACE_DETECT;res = ASFInitEngine(ASF_DETECT_MODE_VIDEO, ASF_OP_0_ONLY, 16, 5, mask, &handle);if (res != MOK)qDebug("ASFInitEngine fail: %d\n", res);elseqDebug("ASFInitEngine sucess: %d\n", res);return res;
}int hrQtPro::OpenCamera()
{//打开摄像头if(capture.open(0)) {qDebug() << "open success";} else {qDebug() << "open error";return -1;}//设置格式capture.set(cv::CAP_PROP_FOURCC, cv::VideoWriter::fourcc('M','J','P','G'));cvTimer->start(20);return 0;
}//Mat转换为QImage
QImage hrQtPro::Mat2QImage(Mat &src) {if(src.type() == CV_8UC3) {const uchar *pSrc = (const uchar*)src.data;QImage qImage(pSrc,src.cols,src.rows,src.step,QImage::Format_RGB888);return qImage.rgbSwapped();}return QImage();
}int hrQtPro::slotReadFarme()
{//读取摄像头数据capture.read(cap);if(!cap.data || cap.empty()) {return -1;}//Mat转换为QImageqImg = Mat2QImage(cap);if(qImg.isNull())return -1;IplImage ipi(cap);IplImage* img = cvCreateImage(cvSize(ipi.width - ipi.width % 4,ipi.height), IPL_DEPTH_8U, ipi.nChannels);ASVLOFFSCREEN offscreen = { 0 };if (img) {CutIplImage(&ipi, img, 0, 0);offscreen.u32PixelArrayFormat = ASVL_PAF_RGB24_B8G8R8;offscreen.i32Width = img->width;offscreen.i32Height = img->height;offscreen.pi32Pitch[0] = img->widthStep;offscreen.ppu8Plane[0] = (MUInt8*)img->imageData;//人脸检测MRESULT res = ASFDetectFacesEx(handle, &offscreen, &detectedFaces);if (MOK != res) {qDebug("ASFDetectFacesEx failed: %d\n", res);} else {if(detectedFaces.faceNum > 0 ) {compareInfo t;t.faceImg = NULL;t.faceImg = cvCreateImage(cvSize(ipi.width - ipi.width % 4,ipi.height), IPL_DEPTH_8U, ipi.nChannels);CutIplImage(&ipi, t.faceImg, 0, 0);t.compareDetectedFaces.faceRect.left = detectedFaces.faceRect[0].left;t.compareDetectedFaces.faceRect.top = detectedFaces.faceRect[0].top;t.compareDetectedFaces.faceRect.right = detectedFaces.faceRect[0].right;t.compareDetectedFaces.faceRect.bottom = detectedFaces.faceRect[0].bottom;t.compareDetectedFaces.faceOrient = detectedFaces.faceOrient[0];t.faceID[0] = detectedFaces.faceID[0];t.faceNum = detectedFaces.faceNum;memcpy(&t.compareOffscreen, &offscreen, sizeof(offscreen));//将检测到的人脸数据添加到一个队列中,等待识别线程的处理thread1->addFrame(t);//设置faceIDthread1->setFaceID(*detectedFaces.faceID);cvReleaseImage(&t.faceImg);}}cvReleaseImage(&img);}}
//绘制人脸框
void hrQtPro::paintEvent(QPaintEvent* e)
{if(!qImg.isNull()) {QPainter painter(&qImg);if(detectedFaces.faceNum > 0) {// 设置人脸框颜色painter.setPen(QColor(255, 0, 0));//显示识别结果painter.drawText(QPoint(detectedFaces.faceRect[0].left, detectedFaces.faceRect[0].top - 20),faceName);//绘制人脸的框painter.drawRect(detectedFaces.faceRect[0].left,detectedFaces.faceRect[0].top,detectedFaces.faceRect[0].right - detectedFaces.faceRect[0].left,detectedFaces.faceRect[0].bottom - detectedFaces.faceRect[0].top);}ui->previewLabel->setPixmap(QPixmap::fromImage(qImg));}
}void hrQtPro::slotFaceCompareFinished(QString name, QString ID, float scores)
{//识别成功后,得到一个姓名和IDif(scores >= 0.8) {faceName = name;faceID = ID;} else {//识别失败,清除信息faceName = "";faceID = "";}
}
//手动点击按钮进行拍照注册
void hrQtPro::slotRegisterFace()
{QString name  = QString::number(thread1->getFaceNum());QString ID = QString::number(thread1->getFaceNum());QString imgStr = AUTOMATIC_REGISTERI_MAGE_PATH + name + ".jpg";if(qImg.isNull()) {qDebug("[%s:%d] img is NULL!!!", __func__, __LINE__);return;}//保存这张图片,以便下次程序启动的时候自动获取人脸特征值if(!qImg.save(imgStr)) {qDebug("[%s:%d] save %s failed!!!", __func__, __LINE__, imgStr.toUtf8().data());return;} else {qDebug("[%s:%d] save %s success!!!", __func__, __LINE__, imgStr.toUtf8().data());}ASF_FaceFeature feature = { 0 };if(exFeaturesOfFile(imgStr, &feature) == 0){persionInfo registerInfo;memset(&registerInfo, 0, sizeof(persionInfo));strcpy(registerInfo.name, name.toUtf8().data());strcpy(registerInfo.ID, ID.toUtf8().data());memcpy(registerInfo.feature, feature.feature, feature.featureSize);registerInfo.featureLen = feature.featureSize;//更新缓存区的人脸数据thread1->addFace(&registerInfo);qDebug("[%s:%d] register %s success!!!", __func__, __LINE__, name.toUtf8().data());} else {qDebug("[%s:%d] register %s failed!!!", __func__, __LINE__, name.toUtf8().data());}
}

识别线程

头文件代码如下:

#ifndef WORKPTHREAD_H
#define WORKPTHREAD_H#include <QThread>
#include <QDebug>
#include <QQueue>
#include <QObject>
#include <QDir>#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/core/core.hpp>
#include <opencv2/objdetect/objdetect.hpp>
#include "opencv2/highgui.hpp"#include "ASF/inc/merror.h"
#include "ASF/inc/asvloffscreen.h"
#include "ASF/inc/arcsoft_face_sdk.h"
#include "ASF/inc/amcomdef.h"#define NSCALE 16
#define FACE_NUM 5
#define FEATURE_LEN 2048
#define ID_LEN 64
#define NAME_LEN 64
//注册的图片路径
#define AUTOMATIC_REGISTERI_MAGE_PATH "image/automatic/"#define SafeFree(p) { if ((p)) free(p); (p) = NULL; }
#define SafeArrayDelete(p) { if ((p)) delete [] (p); (p) = NULL; }
#define SafeDelete(p) { if ((p)) delete (p); (p) = NULL; }typedef struct compare{IplImage* faceImg;ASF_SingleFaceInfo compareDetectedFaces;ASVLOFFSCREEN compareOffscreen;MInt32 faceID[5];MInt32 faceNum;
}compareInfo;
Q_DECLARE_METATYPE(compareInfo)struct persionInfo {char ID[ID_LEN];char name[NAME_LEN];char feature[FEATURE_LEN];unsigned int featureLen;
};Q_DECLARE_METATYPE(persionInfo)void CutIplImage(IplImage* src, IplImage* dst, int x, int y);
int exFeaturesOfFile(QString filePth, ASF_FaceFeature *OutFeature);class workPthread : public QThread
{Q_OBJECT
public:explicit workPthread(QObject *parent = nullptr);void run();void addFace(persionInfo *p);void addFrame(compareInfo frame);int getFaceNum();void slotDestroy(bool flag);void setFaceID(MInt32 id);signals:void faceCompareFinished(QString name, QString ID, float scores);private://待识别的人脸信息队列QQueue<compareInfo> compareInfoQeue;//线程结束的标志bool isDestroy;QVector<persionInfo> faces;compareInfo recvCompareInfo;ASF_FaceFeature copyfeature1;//上一个人脸IDMInt32 lastID;int ASFInit();int compareResult(compareInfo *info, ASF_FaceFeature *databaseFeature, float *scores);int exFeaturesOfImage(compareInfo* p, ASF_FaceFeature *feature);void getFeatures();
};#endif // WORKPTHREAD_H

cpp代码如下:

#include "workpthread.h"MHandle handle = NULL;#if 1
//图像四字节对齐
void CutIplImage(IplImage* src, IplImage* dst, int x, int y)
{CvSize size = cvSize(dst->width, dst->height);//区域大小cvSetImageROI(src, cvRect(x, y, size.width, size.height));//设置源图像ROIcvCopy(src, dst); //复制图像cvResetImageROI(src);//源图像用完后,清空ROI
}
//提取人脸特征值
int exFeaturesOfFile(QString filePth, ASF_FaceFeature *OutFeature)
{if(filePth.isEmpty() || filePth.isNull()) {qDebug("filePth is NULL");return -1;}MRESULT res = MOK;IplImage* pImg = cvLoadImage(filePth.toUtf8().data());IplImage* img = NULL;ASF_FaceFeature feature = { 0 };if(pImg) {img = cvCreateImage(cvSize(pImg->width - pImg->width % 4,pImg->height), IPL_DEPTH_8U, pImg->nChannels);} else {qDebug("pImg is null");return -1;}if(img) {CutIplImage(pImg, img, 0, 0);} else {qDebug("img is null");cvReleaseImage(&pImg);return -1;}if(img) {ASVLOFFSCREEN offscreen = { 0 };offscreen.u32PixelArrayFormat = ASVL_PAF_RGB24_B8G8R8;offscreen.i32Width = img->width;offscreen.i32Height = img->height;offscreen.pi32Pitch[0] = img->widthStep;offscreen.ppu8Plane[0] = (MUInt8*)img->imageData;ASF_MultiFaceInfo detectedFaces;res = ASFDetectFacesEx(handle, &offscreen, &detectedFaces);if (MOK != res) {qDebug("[%s]ASFDetectFacesEx failed: %d\n", __func__, res);} else {ASF_SingleFaceInfo SingleDetectedFaces = { 0 };SingleDetectedFaces.faceRect.left = detectedFaces.faceRect[0].left;SingleDetectedFaces.faceRect.top = detectedFaces.faceRect[0].top;SingleDetectedFaces.faceRect.right = detectedFaces.faceRect[0].right;SingleDetectedFaces.faceRect.bottom = detectedFaces.faceRect[0].bottom;SingleDetectedFaces.faceOrient = detectedFaces.faceOrient[0];res = ASFFaceFeatureExtractEx(handle, &offscreen, &SingleDetectedFaces, &feature);if (res != MOK) {qDebug("%s ASFFaceFeatureExtractEx fail: %d\n", filePth.toUtf8().data(), res);} else{OutFeature->featureSize = feature.featureSize;OutFeature->feature = (MByte *)malloc(feature.featureSize);memset(OutFeature->feature, 0, feature.featureSize);memcpy(OutFeature->feature, feature.feature, feature.featureSize);qDebug("%s ASFFaceFeatureExtractEx:%#x success: %d\n", filePth.toUtf8().data(), feature.feature,res);}}cvReleaseImage(&img);} else {qDebug("cutImg is null %s", filePth.toUtf8().data());}cvReleaseImage(&pImg);return res;
}
#endifworkPthread::workPthread(QObject *parent): QThread(parent)
{ASFInit();getFeatures();isDestroy = false;
}void workPthread::run()
{int ret = -1;int i = 0;int lastSize = 0;int delCnt = 10;bool isFind = false;MFloat confidenceLevel = 0.0;MRESULT res;MInt32 curID = 0;ASF_FaceFeature featureImage = { 0 };lastSize = faces.size();lastID = -1;while(1){if(isDestroy) {break;}//去重,如果识别到一直是同一个人,就不重复提取人脸特征去识别了if((lastID != curID || confidenceLevel < 0.8) && !compareInfoQeue.isEmpty()) {//10 * 100 * 1000us 内一直是同一个人,不重复识别if(delCnt-- <= 0) {delCnt = 10;curID = lastID;}recvCompareInfo = compareInfoQeue.back();memset(&featureImage, 0, sizeof(ASF_FaceFeature));//提取特征值ret = exFeaturesOfImage(&recvCompareInfo, &featureImage);lastSize = faces.size();copyfeature1.feature = (MByte *)malloc(FEATURE_LEN + 1);if(!isFind) {for(i=0; i<faces.size(); ++i) {copyfeature1.featureSize = faces[i].featureLen;memset(copyfeature1.feature, 0, FEATURE_LEN + 1);memcpy(copyfeature1.feature, faces[i].feature, FEATURE_LEN);//特征值比对res = ASFFaceFeatureCompare(handle, &featureImage, &copyfeature1, &confidenceLevel);//阈值大于0.8,则识别成功if(confidenceLevel >= 0.8) {if(copyfeature1.feature) {SafeFree(copyfeature1.feature);}break;}}}isFind = false;SafeFree(copyfeature1.feature);copyfeature1.featureSize = 0;compareInfoQeue.dequeue();if(confidenceLevel >= 0.8 && faces.size() > 0) {qDebug() << "confidenceLevel=" << confidenceLevel;//识别成功,向主线程发送该人脸的信息emit faceCompareFinished(faces[i].name,faces[i].ID,confidenceLevel);} else {//识别失败,清除主线程上一个人脸的信息emit faceCompareFinished("", "", 0.0);}}usleep(100*000);}res = ASFUninitEngine(handle);if (res != MOK)qDebug("ASFUninitEngine fail: %d\n", res);elseqDebug("ASFUninitEngine sucess: %d\n", res);compareInfoQeue.clear();
}void workPthread::slotDestroy(bool flag)
{isDestroy = flag;
}void workPthread::addFrame(compareInfo frame)
{compareInfoQeue.enqueue(frame);
}int workPthread::ASFInit()
{MRESULT res = MOK;handle = NULL;MInt32 mask = ASF_FACE_DETECT | ASF_FACERECOGNITION;res = ASFInitEngine(ASF_DETECT_MODE_VIDEO, ASF_OP_0_ONLY, NSCALE * 2, FACE_NUM, mask, &handle);if (res != MOK)qDebug("ASFInitEngine fail: %d\n", res);elseqDebug("ASFInitEngine sucess: %d\n", res);return res;
}int workPthread::exFeaturesOfImage(compareInfo* p, ASF_FaceFeature *feature)
{MRESULT res = MOK;if(p->faceNum <= 0) {qDebug() << "faceNum == 0";return -1;}res = ASFFaceFeatureExtractEx(handle, &p->compareOffscreen, &p->compareDetectedFaces, feature);if (res != MOK) {qDebug("%s ASFFaceFeatureExtractEx fail: %d\n", __func__, res);emit faceCompareFinished("", "", 0.0);}return res;
}
//添加人脸到缓冲区中
void workPthread::addFace(persionInfo *p) {persionInfo t;memset(&t, 0, sizeof(persionInfo));memcpy(t.feature, p->feature, p->featureLen);strcpy(t.ID, p->ID);strcpy(t.name, p->name);t.featureLen = p->featureLen;faces.push_back(t);
}
//获取人脸数量
int workPthread::getFaceNum() {return faces.size();
}
//设置人脸ID,起到去重的作用
void workPthread::setFaceID(MInt32 id) {lastID = id;
}
//程序启动的时候,加载指定路径下的人脸图片特征
void workPthread::getFeatures() {QDir dir;//如果该路径不存在,则创建if (!dir.exists(AUTOMATIC_REGISTERI_MAGE_PATH)) {dir.mkpath(AUTOMATIC_REGISTERI_MAGE_PATH);} else {qDebug() << AUTOMATIC_REGISTERI_MAGE_PATH << "is exists";}dir.setPath(AUTOMATIC_REGISTERI_MAGE_PATH);QStringList filename;QStringList results;quint32 ID = 0;QString path, name;persionInfo registerInfo;ASF_FaceFeature feature = { 0 };filename << "*.png" << "*.jpg"; //只选择png和jpg的图片results = dir.entryList(filename, QDir::Files | QDir::Readable, QDir::Name);for (int i=0; i<results.size(); ++i) {path = AUTOMATIC_REGISTERI_MAGE_PATH + results.at(i);//使用图片的名字来作为人脸的名字name = results.at(i).split(".").at(0);if(exFeaturesOfFile(path, &feature) == 0){ID++;memset(&registerInfo, 0, sizeof(persionInfo));strcpy(registerInfo.name, name.toUtf8().data());sprintf(registerInfo.ID, "%d", ID);memcpy(registerInfo.feature, feature.feature, feature.featureSize);registerInfo.featureLen = feature.featureSize;//更新缓存区的人脸数据faces.push_back(registerInfo);qDebug("obtain facial features success:%s", registerInfo.name);} else {qDebug("Num %d failed to obtain facial features. Name=%s", i, name.toUtf8().data());}}
}

最后结果


OK,完成。但是,在指定路径读取图片,并且提取人脸特征值的方法还需要改进,应该将注册好的特征值和人脸信息存储在一个数据库中,比如sqlite,下次程序启动只需要从数据库中进行读取即可;同时,还需要添加一些增删改查等功能。

ubuntu下用Qt实现人脸识别(四)相关推荐

  1. ubuntu下用Qt实现人脸识别之检测人脸并绘制人脸框(三)

    ubuntu下用Qt实现人脸识别之检测人脸并绘制人脸框(三) 要检测出人脸并且还要识别出这个人是谁,就得用到人脸算法,这个算法如果你足够牛X的话可以自己写出来,当然,如果像我一样是个小菜鸟的话就得领悟 ...

  2. Ubuntu 下使用 FDDB 测试人脸检测模型并生成 ROC 曲线,详细步骤

    原 Ubuntu 下使用 FDDB 测试人脸检测模型并生成 ROC 曲线 2018年08月01日 20:18:44 Xing_yb 阅读数:101 标签: FDDB 人脸检测 模型测试 ROC 曲线 ...

  3. 基于QT的人脸识别考勤管理系统【二】

    前言: 上一篇我们实现了考勤管理系统的用户考勤打卡系统https://blog.csdn.net/qq_42449351/article/details/99716413,这一篇我将为大家带来这个系统 ...

  4. Ubuntu下安装Qt全部过程

    Ubuntu下安装Qt全部过程 (2013-03-04 22:12:02) 转载▼   分类:ARM Linux 1.到官网http://qt-project.org/downloads或者ftp:/ ...

  5. Ubuntu下编译qt程序libQtWebEngineCore报错

    最近在Ubuntu下编译qt程序时, 报如下错误 libQt5WebEngineCore.so: .dynsym local symbol at index 3 (>= sh_info of 3 ...

  6. ubuntu下搭建android开发环境(四)核心篇安装AndroidStudio、sdk、jdk

    [置顶] ubuntu下搭建android开发环境(四)核心篇安装AndroidStudio.sdk.jdk(by 星空武哥) <div class="article_manage c ...

  7. 基于QT的人脸识别考勤管理系统【一】

    前言: 上篇我们已经用opencv实现了人脸识别https://blog.csdn.net/qq_42449351/article/details/99052241,现在我们就用人脸识别来做一个考勤管 ...

  8. Ubuntu下嵌入式Qt开发环境配置全攻略

    本文以友善之臂的Mini6410嵌入式开发板为目标板,介绍ubuntu 12.04系统下,配置嵌入式Qt开发工具的过程.本文中介绍的工具.大部分步骤和脚本来自开发板附带资料光盘,但其默认配置环境为老旧 ...

  9. 禁用人脸识别四个月后,旧金山人证明了他们的先见之明

    文 | 波波夫 01从便利到恐惧 一百年来,旧金山时刻保持着自由捍卫者的先锋姿态. 四个月前,2019年4月5日,当旧金山监事会(The San Francisco Board of Supervis ...

最新文章

  1. C语言第一次实验报告
  2. java中自然排序和比较器排序
  3. #4 什么是shell与bash的特性
  4. 【翻译】.NET 5 Preview2发布
  5. 使用react实现select_使用 Hooks 优化 React 组件
  6. mysql2018漏洞_MySQL多个远程安全漏洞CVE-2018-2562/91 大批版本受影响
  7. UEFI开发探索31–鼠标GUI构建
  8. SqlSugar-执行Sql语句查询实例
  9. java搭建rtmp服务器,利用docker搭建RTMP直播流服务器实现直播
  10. vue使用coreUI的CDataTable踩坑记录
  11. 计算机网络hdcp是什么意思,HDCP技术_百科..doc
  12. ODL中版本变化引起包位置的变化
  13. 元素的显示、隐藏、遮罩
  14. iOS之深入解析App Thinning的应用瘦身优化
  15. 坐标反算计算起始方位角_平面坐标反算(测量上常用于计算坐标方位角)
  16. 夏天来了,西瓜配橙汁,来点小清新风格
  17. Python学多久能接单赚钱?按照这套路线学习,30天内就可以!
  18. 谷歌浏览器调用打印机不预览
  19. 中国的数字化转型 China’s digital transformation
  20. 神经网络和pid有什么区别,基于神经网络的pid控制

热门文章

  1. PHP+MYSQL实现手机号码归属地查询…
  2. Illustrator 教程:如何在 Illustrator 中剪切及擦除图稿?
  3. php框架写博客,用PHP写框架用框架写应用程序
  4. springboot + poi 解析 excel
  5. 机器与人类视觉能力的差距(二)神经网络
  6. java mysql executequery_jdbc连接数据库 Statement的executeQuery方法报空指针异常?
  7. 在 Windows Vista 操作系统中手动删除驱动程序
  8. 字符串 - 二进制和文本字符串 - 探究
  9. 性价比之王——H3C Magic R300千兆版路由器
  10. Linux创建用户后,登录报错/usr/bin/xauth: file /home/user/.Xauthority does not exist