为了以后做多任务多标签对人脸属性进行分析,发现目前还没有已经实现好的工具或者我还没找到^_^o(╥﹏╥)o,如果哪位朋友知道望告知。故自己动手制作了个符合自己定义的属性,先给出标注界面图。
一、QT开发
下面界面开发用的是QT,C/C++开发工具,优点:跨平台轻巧。共设计有18种属性,每个QButtonGroup集成3--5个QRadioButton里面,一个QButtonGroup中有且仅有一个QRadioButton被选中。
这18种属性定义标注好后,会写入txt文件里面,格式为"***.jpg 属性1标志位 属性2标志位 属性3标志位 ... 属性18标志位",每种属性标志位用唯一的数字表示。顺序为:
眼睛睁闭情况,睁(1),闭(0),不确定(255)。
眼神,专注(1),视线方向无心(0),不确定(255)。
哈欠,打(1),不打(0),不确定(255)。
姿态角度,左(1),中(2),右(3),不确定(255)。
种族,黄种人(1),白种人(2),黑种人(3),不确定(255)。
性别,男(1),女(2),不确定(255)。
年龄,年轻人(1),中年人(2),老年人(3),不确定(255)。
发型,遮挡耳朵(1),刘海(2),无遮挡无刘海(3),不确定(255)。
脸型,窄(1),宽(2),不确定(255)。
戴眼镜,戴墨镜(1),不戴(0),不确定(255)。
戴帽子,戴(1),不戴(0),不确定(255)。
手,上(1),下(2),左(3),右(4),不确定(255)。
打电话,打(1),不打(0),不确定(255)。
吸烟,吸(1),不吸(0),不确定(255)。
吃东西喝水,吃(1),不吃(0),不确定(255)。
戴口罩,戴(1),不戴(0),不确定(255)。
围巾高衣领,有(1),没有(0),不确定(255)。
抓痒,抓(1),不抓(0),不确定(255)。
由于在VS里面方便调试、智能提示等功能,QT有自带的QT creator工具,很多组件可供选择,最后我选用了QT VS插件工具,结合两者的优点进行开发,所用版本为QT5.4。***.ui文件可以方便的对各种组件进行拖动,定义信号和槽,弄好后可以自动生成代码,一般开头都会以头文件ui_***.h和实现文件qrc_***.cpp,还可以方便的把信号和槽函数连接起来,这点非常棒,不用手工写大量的代码。
部分代码如下:
#pragma once #include <opencv2/opencv.hpp> #include <QtWidgets/QDialog> #include "ui_helloworld_QT.h" #include "CmFile/CmFile.h" class helloworld_QT : public QDialog { Q_OBJECT public: helloworld_QT(QWidget *parent = Q_NULLPTR); ~helloworld_QT(); void resetRadioBtn(); private: Ui::helloworld_QTClass ui; QButtonGroup *btnGroup1; QButtonGroup *btnGroup2; QButtonGroup *btnGroup3; QButtonGroup *btnGroup4; QButtonGroup *btnGroup5; QButtonGroup *btnGroup6; QButtonGroup *btnGroup7; QButtonGroup *btnGroup8; QButtonGroup *btnGroup9; QButtonGroup *btnGroup10; QButtonGroup *btnGroup11; QButtonGroup *btnGroup12; QButtonGroup *btnGroup13; QButtonGroup *btnGroup14; QButtonGroup *btnGroup15; QButtonGroup *btnGroup16; QButtonGroup *btnGroup17; QButtonGroup *btnGroup18; cv::Mat image; QImage qimage; string imagePath; vector<string> imagenames; vector<int> currentState; int m_index; int m_allNums; ofstream fid; //QStandardItemModel *standardItemModel; public slots: void onRadioClickEyes(); void onRadioClick2(); void onRadioClick3(); void onRadioClick4(); void onRadioClick5(); void onRadioClick6(); void onRadioClick7(); void onRadioClick8(); void onRadioClick9(); void onRadioClick10(); void onRadioClick11(); void onRadioClick12(); void onRadioClick13(); void onRadioClick14(); void onRadioClick15(); void onRadioClick16(); void onRadioClick17(); void onRadioClick18(); void on_signal_next(); void on_signal_prev(); void pictureShow(const QModelIndex &index); void itemClicked(QModelIndex index); };
由于比较多的重复内容,给出部分实现代码:
// imagesList图像列表 QFileSystemModel* model = new QFileSystemModel(); model->setNameFilterDisables(false); model->setFilter(QDir::Dirs | QDir::Drives | QDir::Files | QDir::NoDotAndDotDot); QStringList list_name; list_name << "*.jpg" ; model->setNameFilters(list_name); ui.imagesList->setMovement(QListView::Static); ui.imagesList->setViewMode(QListView::IconMode); ui.imagesList->setGridSize(QSize(100, 100)); ui.imagesList->setModel(model); ui.imagesList->setRootIndex(model->setRootPath(QString::fromStdString(imagePath))); connect(ui.imagesList, SIGNAL(clicked(QModelIndex)), this, SLOT(pictureShow(QModelIndex))); // 眼睛睁闭 btnGroup1 = new QButtonGroup(this); btnGroup1->addButton(ui.radioButton_01, 0); btnGroup1->addButton(ui.radioButton_02, 1); btnGroup1->addButton(ui.radioButton_03, 2); connect(ui.radioButton_01, SIGNAL(clicked()), this, SLOT(onRadioClickEyes())); connect(ui.radioButton_02, SIGNAL(clicked()), this, SLOT(onRadioClickEyes())); connect(ui.radioButton_03, SIGNAL(clicked()), this, SLOT(onRadioClickEyes())); ui.radioButton_01->setChecked(true);睁闭眼槽函数:
void helloworld_QT::onRadioClickEyes() { switch (btnGroup1->checkedId()) { case 0: //睁眼 currentState[0] = 1; break; case 1: // 闭眼 currentState[0] = 0; break; case 2: currentState[0] = 255; break; default: currentState[0] = 255; break; } }
上面currentState是存储当前图像标注状态位的变量,类型为vector<int>,size为18,在构造函数初始化下。只有用户点击“上一张”或“下一张”就会把currentState的值写到对应的txt中。“下一张”槽函数为:
void helloworld_QT::on_signal_next() { int currentIndex = m_index++; if ((currentIndex > m_allNums) || (currentIndex<0)) { QMessageBox msgBox; msgBox.setText(tr("Exceeds image retrieval range!")); msgBox.exec(); return; } fid << imagenames[currentIndex] << " "; for (size_t i = 0; i < currentState.size(); i++)//注意,共18种属性 { fid << currentState[i] << " "; } fid << endl; resetRadioBtn();//重新置所有单选为默认 image = cv::imread(imagenames[currentIndex]); if (!image.data) { QMessageBox msgBox; msgBox.setText(tr("image data is null")); msgBox.exec(); } else { cv::cvtColor(image, image, CV_BGR2RGB); qimage = QImage((const unsigned char*)(image.data), image.cols, image.rows, image.cols*image.channels(), QImage::Format_RGB888); ui.labelImage->clear(); qimage.scaled(ui.labelImage->size(), Qt::KeepAspectRatio); ui.labelImage->setPixmap(QPixmap::fromImage(qimage)); } // string imageNoname = CmFile::GetName(imagenames[currentIndex]); QStandardItem *item = new QStandardItem(QString::fromStdString(imageNoname)); QLinearGradient linearGrad(QPointF(0, 0), QPointF(200, 200)); linearGrad.setColorAt(0, Qt::darkGreen); linearGrad.setColorAt(1, Qt::yellow); QBrush brush(linearGrad); item->setBackground(brush); QStandardItemModel standardItemModel; standardItemModel.appendRow(item); ui.imagesList->setModel(&standardItemModel); //ui.imagesList->setFixedSize(200, 300); //connect(ui.imagesList, SIGNAL(clicked(QModelIndex)), this, SLOT(itemClicked(QModelIndex))); delete item; }
其他控件类似操作。
二、Matlab APP designer设计
不同于GUIDE,优点:面向对象设计,代码清晰简洁,只有一个文件,后缀为***.mlapp。有很多比较好的控件,控件属性,回调函数等等非常人性化设计,便于直接拖动到到画布上。跟QT类似,也会自动生成控件外观布局的代码,用户仅仅只需要根据自己的要求改写功能函数的代码,可用于大型程序开发。灰色部分代码是不可更改的生成代码,下面给出人脸属性标注设计开发界面。
有设计视图和代码视图2个部分,可交替进行操作。
最终开发跟QT一模一样的界面如下:
同样由于很多重复代码,这里给出部分代码,如“下一张”的回调函数:
% Button pushed function: Button_67 function Button_nextPushed(app, event) if (app.index>=1)&&(app.index<=length(app.imds.Files)) [path,name,ext] = fileparts(app.imds.Files{app.index}); txtFullName = fullfile(path,[name,'.txt']); fid_w = fopen(txtFullName,'w'); formatP = repmat('%d ',1,18); fprintf(fid_w,['%s',' ',formatP,'\r\n'],[name,ext],app.vectorState); fclose(fid_w); end app.index = app.index+1; if app.index<1||app.index>length(app.imds.Files) app.index = length(app.imds.Files)+1; return; end %下一张时候使得列表图像被选中 [nextPath,nextName,ext] = fileparts(app.imds.Files{app.index}); itemSelect = [nextName,ext]; app.ListBox.Value = itemSelect; % 显示状态 txtNextFullName = fullfile(nextPath,[nextName,'.txt']); if ~exist(txtNextFullName,'file') % 只是显示下一张图片的状态而已 resetState(app); else fid = fopen(txtNextFullName,'r'); A = textscan(fid,['%*s',repmat('%f',[1,18])],'Delimiter',' '); B = cell2mat(A);% 1*18的矩阵 fclose(fid); setRadioButtonValue(app,B); end % 选中的图像显示 imageFrame = readimage(app.imds,app.index); imshow(imageFrame,'Parent',app.UIAxes); app.UIAxes.XLabel.String = ''; app.UIAxes.YLabel.String = ''; end
图像列表选中的图像显示:如果遇到原来没有标注好的图像,就重置状态,否则读取原来标注状态并显示radiobutton上来。
% Value changed function: ListBox function ListBoxValueChanged(app, event) % resetState(app); value = app.ListBox.Value; imagefullname = fullfile(app.rootFolder,value); app.index = find(string(app.imds.Files)==string(imagefullname));%index更新 % 显示状态 txtFullName = fullfile(app.rootFolder,[value(1:end-4),'.txt']); if ~exist(txtFullName,'file') % 只是显示下一张图片的状态而已 resetState(app); else fid = fopen(txtFullName,'r'); A = textscan(fid,['%*s',repmat('%f',[1,18])],'Delimiter',' '); B = cell2mat(A);% 1*18的矩阵 fclose(fid); setRadioButtonValue(app,B); end %显示图像 imageFrame = imread(imagefullname); imshow(imageFrame,'Parent',app.UIAxes); app.UIAxes.XLabel.String = ''; app.UIAxes.YLabel.String = ''; end
睁闭眼回调函数:
% Selection changed function: ButtonGroup function ButtonGroupSelection_1Changed(app, event) selectedButton = app.ButtonGroup.SelectedObject; switch selectedButton.Text %睁闭眼 case app.Button_1.Text app.vectorState(1) = 1; case app.Button_2.Text app.vectorState(1) = 0; case app.Button_6.Text app.vectorState(1) = 255; otherwise app.vectorState(1) = 255; end end
app.vectorState就是跟上面QT中currentState变量一样的作用,先在构造函数完成初始化,然后在各个ButtonGroup中根据选中情况赋值。
最后,标注生成的结果如下图,注意顺序,对应我上面列出来的18种属性标志位。
以上工具在R2017b环境下开发,理论上版本要大于等于这个版本。我已打包,下载链接https://download.csdn.net/download/cuixing001/10307733,只需简单安装即可使用。
QT工具:链接:https://pan.baidu.com/s/1_wkrl4yy3M8q3OIMz-PYkQ 密码:8nmu
图像如有侵权,请告知。