人脸属性标注工具

     为了以后做多任务多标签对人脸属性进行分析,发现目前还没有已经实现好的工具或者我还没找到^_^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

图像如有侵权,请告知。

猜你喜欢

转载自blog.csdn.net/cuixing001/article/details/79652468