天天看点

ImageLabeler-master(图片标注工具)

可以参考配套的文档(作者的文档)

技术要点:类的继承,友元函数,自动补全,按键事件,listwidget,json数据解析,qlist,qmap,使用const宏定义,

类的继承:class

CubeAnnotationItem

:

public

AnnotationItem

友元函数:friend

AnnotationContainer;

宏定义:

//

for

image

result

const

QString

SUFFIX_SEG_COLOR("_segment_color.png");

const

QString

SUFFIX_SEG_LABELID("_segment_labelId.png");

const

QString

SUFFIX_SEG3D_COLOR("_segment3d_color.png");

const

QString

SUFFIX_SEG3D_LABELID("_segment3d_labelId.png");

ImageLabeler-master(图片标注工具)
ImageLabeler-master(图片标注工具)

/*

LabelDialog(选择label的一个对话框)

*

简介:

*

界面:ui->lineEdit,LabelLineEdit

类型,绑定了

ui->listWidget(详见

LabelLineEdit

的声明)

*

ui->listWidget,用于显示label,且有一个纯色的icon代表label对应的颜色

*/

ImageLabeler-master(图片标注工具)

/*

LabelLineEdit (自动补全,绑定QlistWidget,按键上下选中)

*
							简介:用作LabelDialog的一个部件,可绑定一个QListWidget实现一些联动的效果
						           
*
							功能:a.
									输入时根据
											labelListWidget
													的
															items
																	的
																			text
																					提供自动补全
																				           
*
							b.
									当
											labelListWidget
													的选中项改变时,相应的改变输入框中的text
												           
*
							c.
									按下
											up和down
													键,可改变
															labelListWidget
																	的选中项。
																           
*/
				           

class

LabelLineEdit:

public

QLineEdit{

friend

LabelDialog;

public:

//

labelListWidget

构造时默认为

nullptr

explicit

LabelLineEdit(QWidget

*parent =

nullptr);

//

设置

labelListWidget,并实现功能

a

b

(自动补全

相应list的选中)

void

setLabelListWidget(QListWidget*

listWidget);

protected:

//

处理

up

down

键的按下事件,实现功能

c

(改变list的选中项)

void

keyPressEvent(QKeyEvent

*event);

private:

QListWidget*

labelListWidget;

};

LabelLineEdit::LabelLineEdit(QWidget

*parent):QLineEdit(parent),

labelListWidget(nullptr)

{

}

void

LabelLineEdit::setLabelListWidget(QListWidget

*listWidget){

labelListWidget

=

listWidget;

//

功能

a:

输入时根据

labelListWidget

items

text

提供自动补全

QCompleter*

completer

=

new

QCompleter(this);

completer->setCompletionMode(QCompleter::InlineCompletion);

completer->setModel(labelListWidget->model());

this->setCompleter(completer);

//

功能

b:

labelListWidget

的选中项改变时,相应的改变输入框中的text

connect(labelListWidget,

&QListWidget::currentItemChanged,

[this](QListWidgetItem

*currentItem){

this->setText(currentItem->text());

});

}

void

LabelLineEdit::keyPressEvent(QKeyEvent

*event){

//

功能

c:

按下

up和down

键,可改变

labelListWidget

的选中项

if

(event->key()

==

Qt::Key_Up){

if

(labelListWidget!=nullptr

&&

labelListWidget->count()>0){

int

newRow

=

std::max(0,

std::min(labelListWidget->currentRow()

-

1,

labelListWidget->count()-1));

labelListWidget->setCurrentRow(newRow);

this->setText(labelListWidget->currentItem()->text());

}

}else

if

(event->key()

==

Qt::Key_Down){

if

(labelListWidget!=nullptr

&&

labelListWidget->count()>0){

int

newRow

=

std::max(0,

std::min(labelListWidget->currentRow()

+

1,

labelListWidget->count()-1));

labelListWidget->setCurrentRow(newRow);

this->setText(labelListWidget->currentItem()->text());

}

}else{

QLineEdit::keyPressEvent(event);

}

}

/*

LabelManager -LabelProperty(json格式存储label数据,解析json数据)

*

简介:用来存储label的相关性质的数据类型,存储了label、color、visible、id

*

Json:该类的数据与json相互转化时的格式为

*

{

*

"color":

[

*

Double,

//

R

*

Double,

//

G

*

Double

//

B

*

],

*

"id":

Double,

*

"label":

String,

*

"visible":

Bool

*

}

//---------------------------------LabelProperty-------------------------------------//

LabelProperty::LabelProperty(QString

label,

QColor

color,

bool

visible,

int

id)

:

label(label),

color(color),

visible(visible),id(id)

{

}

LabelProperty::LabelProperty():label(),

color(),

visible(true),

id(-1)

{

}

QJsonObject

LabelProperty::toJsonObject(){

QJsonArray

colorJson;

colorJson.append(color.red());

colorJson.append(color.green());

colorJson.append(color.blue());

QJsonObject

json;

json.insert("label",

label);

json.insert("id",

id);

json.insert("color",

colorJson);

json.insert("visible",

visible);

return

json;

}

void

LabelProperty::fromJsonObject(QJsonObject

json)

{

if

(json.contains("label")){

QJsonValue

value

=

json.value("label");

if

(value.isString()){

label

=

value.toString();

}else{

throw

JsonException("value

of

<label>

is

illegal");

}

}else{

throw

JsonException("no

data

<label>");

}

if

(json.contains("color")){

QJsonValue

value

=

json.value("color");

if

(value.isArray()){

QJsonArray

array

=

value.toArray();

if

(!array.at(0).isDouble()

||

!array.at(1).isDouble()

||

!array.at(2).isDouble()){

throw

JsonException("value

of

<color>

is

illegal");

}

int

r=static_cast<int>(array.at(0).toDouble());

int

g=static_cast<int>(array.at(1).toDouble());

int

b=static_cast<int>(array.at(2).toDouble());

color

=

QColor(r,g,b);

}else{

throw

JsonException("value

of

<color>

is

illegal");

}

}else{

throw

JsonException("no

data

<color>");

}

if

(json.contains("visible")){

QJsonValue

value

=

json.value("visible");

if

(value.isBool()){

visible

=

value.toBool();

}else{

throw

JsonException("value

of

<visible>

is

illegal");

}

}else{

throw

JsonException("no

data

<visible>");

}

if

(json.contains("id")){

QJsonValue

value

=

json.value("id");

if

(value.isDouble()){

id

=

static_cast<int>(value.toDouble());

}else{

throw

JsonException("value

of

<id>

is

illegal");

}

}else{

throw

JsonException("no

data

<id>");

}

}

/*

LabelManager(增加删除修改查询label,)

*

简介:管理label相关的数据的类,主要与ui->labelListWidget同步

*

注意:在addLabel前应检查是否已经存在该label

*

*

Json:该类的数据与json相互转化时的格式为

*

[

LabelProperty,

LabelProperty,

...

]

//

Array中的元素的格式为LabelProperty的格式

*/

使用map存储labels;

QMap<QString,

LabelProperty>

labels;

/*

CustomListWidget

*

简介:作为MainWindow中的部件,根据所需提供了更方便的函数接口以及事件响应

*

是MainWindow中

annoListWidget、labelListWidget、fileListWidget的类型

*

界面:每个

item

从左至右组成为:一个用于check的小框(可有可无),一个icon(可有可无),以及text

*

事件:鼠标若点击到listwidget空白区域,即没有item的区域,则清除list的所有选中状态

*

键盘按下

up/down

键改变list的选中项

*

注意:a.

该类应为单选模式(SingleSelection),且不应该被更改;

*

b.

该类不应出现不同的item的text相同的情况,

*

这种情况在该项目中是不会发生的,因为:

*

1.

annoList中每个label相同的标注被一个各不相同的instance

id标识

*

2.

fileList中不会出现文件重名的情况

*

3.

labelList在加入item时会根据labelManager检查label是否已经存在

*/

Listwidget中的右键菜单

void

MainWindow::provideLabelContextMenu(const

QPoint

&pos)

{

QPoint

globalPos

=

ui->labelListWidget->mapToGlobal(pos);

QModelIndex

modelIdx

=

ui->labelListWidget->indexAt(pos);

if

(!modelIdx.isValid())

return;

int

row

=

modelIdx.row();

auto

item

=

ui->labelListWidget->item(row);

QMenu

submenu;

submenu.addAction("Change

Color");

submenu.addAction("Delete");

QAction*

rightClickItem

=

submenu.exec(globalPos);

if

(rightClickItem){

if

(rightClickItem->text().contains("Delete")){

removeLabelRequest(item->text());

}else

if

(rightClickItem->text().contains("Change

Color")){

QColor

color

=

QColorDialog::getColor(Qt::white,

this,

"Choose

Color");

if

(color.isValid()){

labelManager.setColor(item->text(),color);

}

}

}

}

/*

FileManager(静态函数方便调用,)

*

简介:管理与文件相关的状态的类型

*

功能:a.

实现静态函数作为更便捷的处理文件名、读写文件的接口

*

b.

打开文件或文件夹,生成默认的输出文件名(包括label和annotation的输出文件)

*

c.

记录距离上次保存后是否有未保存的修改

*

d.

可选中文件,切换显示的图像,并与

ui->fileListWidget

同步

*/

/*

RectAnnotationItem(绘制矩形,标记用json格式存储)

*

简介:用于2D

Detection的标注类型,比父类多记录了一个像素坐标的矩形

rect

表示bounding

box

*

*

Json:该类的数据与json相互转化时的格式如下,points表示两个矩形的左上和右下顶点

*

{

*

"label":

String

*

"id":

Double

*

"points":

[

*

[

*

Double,

*

Double

*

],

*

[

*

Double,

*

Double

*

]

*

]

*

}

*/

/*

SegStroke(绘制多边形)

*

简介:用于表示2D分割标注中一

"笔画"

的数据类型

*

笔画:笔画共分为三种,分别为圆形画笔、方形画笔、轮廓

*

对应的type的值分别为

"circle_pen"

"square_pen"

"contour"

*

当type为画笔时,points中记录的是画笔中心经过路径,penWidth记录画笔的大小;

*

当type为轮廓时,points依次记录轮廓上的点,并用多边形连接它们,特别地,当点比较稠密时,可视为光滑闭合曲线

*

当type为轮廓时,penWidth无意义,一般记为1,也可不记

*

*

Json:该类的数据与json相互转化时的格式如下,

*

{

*

"type":

String

//

取值为

"circle_pen"

"square_pen"

"contour"

*

"penWidth":

Double

*

"points":

[

[Double,

Double],

[Double,

Double],

...

]

//Array类型,表示xy坐标

*

}

*/

/*

Basic_SegAnnotationItem

*

简介:用于表示Segmentation标注的类模板,该项目中认为一个分割标注可由多个笔画(stroke_type类型)合并组成

*

对应2D和3D分割标注,stroke_type分别为SegStroke和SegStroke3D

*

*

Json:该类的数据与json相互转化时的格式如下

*

{

*

"label":

String

*

"id":

Double

*

"strokes":

[

stroke_type,

stroke_type,

...

]

//

Array的元素的格式为SegStroke或SegStroke3D对应的格式

*

}

*/

Common(一些基本的功能,多个命名空间)

#ifndef
										COMMON_H
           
#define
											COMMON_H
										           
#include
											"canvasbase.h"
										           
#include
											"cubeannotationitem.h"
										           
#include
											<QList>
										           
#include
											<QColor>
										           
#include
											<QRect>
										           
#include
											<QPoint>
										           
#include
											<QString>
										           
#include
											<cmath>
										           
#include
											<map>
										           
namespace
											ColorUtils{
											           
extern
												QList<QColor>
																	randomColors(int
																			count);
																		           
extern
												QColor
														randomColor();
													           
extern
												QIcon
														iconFromColor(QColor
																color,
																		QSize
																			size
																				=
																						QSize(16,16));
																										           
}
								           
namespace
											CanvasUtils
													{
												           
const
												int
													DEFAULT_PEN_WIDTH=15;
														           
const
												int
													LABEL_PIXEL_SIZE
														=
																15;
																           
const
												int
													pixEps=5;
														           
enum
												EditingRectEdge{
												           
TOP,
													BOTTOM,
																LEFT,
																			RIGHT
																		           
};
									           
extern
												bool
														onRectTop(QPoint
																pos,
																		QRect
																			rect);
																		           
extern
												bool
														onRectBottom(QPoint
																pos,
																		QRect
																			rect);
																		           
extern
												bool
														onRectLeft(QPoint
																pos,
																		QRect
																			rect);
																		           
extern
												bool
														onRectRight(QPoint
																pos,
																		QRect
																			rect);
																		           
//
												z:
														min
																->
																		max
																				<=>
																						top
																								->
																										bottom
																									           
//
												x:
														min
																->
																		max
																				<=>
																						left
																								->
																										right
																									           
//
												y:
														min
																->
																		max
																				<=>
																						back
																								->
																										front
																									           
enum
												EditingCubeFace{
												           
TOPf,
													BOTTOMf,
																LEFTf,
																			RIGHTf,
																						FRONTf,
																									BACKf
																								           
};
									           
extern
												bool
														onCubeTop(Point3D
																pos,
																		Cuboid
																			cube);
																		           
extern
												bool
														onCubeBottom(Point3D
																pos,
																		Cuboid
																			cube);
																		           
extern
												bool
														onCubeLeft(Point3D
																pos,
																		Cuboid
																			cube);
																		           
extern
												bool
														onCubeRight(Point3D
																pos,
																		Cuboid
																			cube);
																		           
extern
												bool
														onCubeFront(Point3D
																pos,
																		Cuboid
																			cube);
																		           
extern
												bool
														onCubeBack(Point3D
																pos,
																		Cuboid
																			cube);
																		           
}
								           
namespace
											StringConstants{
											           
const
												std::map<TaskMode,QString>
																				taskText
																					={{DETECTION,
																									"Detection
																											"},
																											           
{SEGMENTATION,
														"Segmentation
																"},
																           
{DETECTION3D,
														"3D
																Detection
																		"},
																		           
{SEGMENTATION3D,
														"3D
																Segmentation
																		"}};
																		           
const
												std::map<DrawMode,QString>
																				drawModeText={{RECTANGLE,
																							"Rectangle
																									"},
																									           
{CIRCLEPEN,
														"Circle
																Pen
																		"},
																		           
{SQUAREPEN,
														"Square
																Pen
																		"},
																		           
{CONTOUR,
														"Contour
																"},
																           
{POLYGEN,
														"Polygonal
																Contour
																		"}};
																		           
extern
												inline
														bool
																is2dTask(const
																			QString
																					&text)
																							{
																						           
return
												text
														==
															taskText.at(DETECTION)
																		||
																				text
																						==
																							taskText.at(SEGMENTATION);
																							           
}
									           
extern
												inline
														bool
																is3dTask(const
																			QString
																					&text)
																							{
																						           
return
												text
														==
															taskText.at(DETECTION3D)
																		||
																				text
																						==
																							taskText.at(SEGMENTATION3D);
																							           
}
									           
extern
												inline
														bool
																isDetectTask(const
																			QString
																					&text)
																							{
																						           
return
												text
														==
															taskText.at(DETECTION)
																		||
																				text
																						==
																							taskText.at(DETECTION3D);
																							           
}
									           
extern
												inline
														bool
																isSegmentTask(const
																			QString
																					&text)
																							{
																						           
return
												text
														==
															taskText.at(SEGMENTATION)
																		||
																				text
																						==
																							taskText.at(SEGMENTATION3D);
																							           
}
									           
extern
												DrawMode
														getDrawModeFromText(const
																	QString
																			&text);
																		           
extern
												TaskMode
														getTaskFromText(const
																	QString
																			&text);
																		           
//
												for
														single
																image
															           
const
												QString
													SUFFIX_DET_LABEL_ANNO("_detect_labels_annotations.json");
														           
const
												QString
													SUFFIX_SEG_LABEL_ANNO("_segment_labels_annotations.json");
														           
//
												for
														multiple
																image
															           
const
												QString
													FILENAME_DIR_LABEL("labels.json");
														           
const
												QString
													SUFFIX_DET_ANNO("_detect_annotations.json");
														           
const
												QString
													SUFFIX_SEG_ANNO("_segment_annotations.json");
													           
//
												for
														3d
																image
															           
const
												QString
													FILENAME_DET3D_LABEL_ANNO("detect3d_labels_annotations.json");
														           
const
												QString
													FILENAME_SEG3D_LABEL_ANNO("segment3d_labels_annotations.json");
														           
//
												for
														image
																result
															           
const
												QString
													SUFFIX_SEG_COLOR("_segment_color.png");
														           
const
												QString
													SUFFIX_SEG_LABELID("_segment_labelId.png");
														           
const
												QString
													SUFFIX_SEG3D_COLOR("_segment3d_color.png");
														           
const
												QString
													SUFFIX_SEG3D_LABELID("_segment3d_labelId.png");
														           
}
								           
#endif
											//
													COMMON_H