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");

 

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

* 简介:

* 界面:ui->lineEdit,LabelLineEdit 类型,绑定了 ui->listWidget(详见 LabelLineEdit 的声明)

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

*/

/* 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
												

posted @ 2021-01-20 19:20  tmjDD  阅读(745)  评论(0编辑  收藏  举报