可以參考配套的文檔(作者的文檔)
技術要點:類的繼承,友元函數,自動補全,按鍵事件,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(一些基本的功能,多個命名空間)
|