为了以后做多任务多标签对人脸属性进行分析,发现目前还没有已经实现好的工具或者我还没找到^_^o(╥﹏╥)o,如果哪位朋友知道望告知。故自己动手制作了个符合自己定义的属性,先给出标注界面图。
下面界面开发用的是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
#include
#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 imagenames;
vector 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
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;
}
其他控件类似操作。
不同于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
图像如有侵权,请告知。