用於分割任務的多邊形標註
在 Potato 中搭建一個多邊形分割任務:配置怎麼寫、標註者繪製時按哪些鍵、匯出座標是什麼格式,以及什麼時候畫筆蒙版才是更合適的工具。
本教程在 Potato 中搭建一個影像分割任務,標註者用多邊形描出目標邊界,內容涵蓋配置、標註者要按的鍵,以及匯出座標的形狀。
多邊形是一個頂點一個頂點地描出邊界,這正是語義分割和實例分割需要的東西,也是邊界框給不了的。代價是標註者的時間,所以第一個要做的判斷是:你的任務到底需不需要邊界。
在多邊形、邊界框和蒙版之間做選擇
Potato 裡有三種工具能產生區域標註,它們的區別在於能表達什麼,而不只是快慢。
| 邊界框 | 多邊形 | 畫筆蒙版 | |
|---|---|---|---|
| 邊界精度 | 只能是矩形 | 逐頂點 | 逐畫素 |
| 孔洞與不相連的部分 | 不行 | 彆扭 | 自然 |
| 細長結構(電線、頭髮) | 不行 | 差 | 好 |
| 格式支援 | 最廣 | 廣:COCO、YOLO、CVAT、LabelMe | 較窄:COCO RLE、蒙版 PNG |
| 編輯方式 | 拖角 | 拖頂點 | 重塗一片區域 |
當粗略定位就夠用時就用邊界框,多數檢測訓練都屬於這一類。當模型要消費邊界、而且目標是實心的,多邊形才值得多花標註者的時間。一旦目標帶有孔洞或細絲,畫筆蒙版才是老實的工具,因為硬給腳踏車輪圈一個多邊形,畫出來的形狀誰也沒法核對。Potato 還有一個開放路徑的 polyline 工具,用於車道線、血管這類線形特徵,它匯出時面積為零,而不是憑空編一個內部區域。
任務配置
多邊形工具是按 scheme 通過 tools 啟用的,labels 就是標註者可以賦給某個形狀的類別。item_properties.text_key 指明資料檔案裡存放影像 URL 的欄位,Potato 靠它知道要顯示什麼:
annotation_task_name: "Urban Scene Segmentation"
task_description: "Trace every object in the street scene."
data_files:
- data/images.json
item_properties:
id_key: id
text_key: image_url
annotation_schemes:
- annotation_type: image_annotation
name: segments
description: "Draw a polygon around each object."
tools: [polygon]
labels:
- name: road
color: "#808080"
key_value: "1"
- name: sidewalk
color: "#F5F5DC"
key_value: "2"
- name: building
color: "#8B4513"
key_value: "3"
- name: vegetation
color: "#228B22"
key_value: "4"
- name: sky
color: "#87CEEB"
key_value: "5"
- name: vehicle
color: "#FF6B6B"
key_value: "6"
- name: person
color: "#4ECDC4"
key_value: "7"
output_annotation_dir: annotation_outputkey_value 這些條目的作用比看上去大。一個七類任務,如果標註者每畫一個形狀都要去點工具欄,速度只有把類別放在數字鍵上的零頭,而這個代價全部落在標註者身上。顏色必須是十六進位制,因為蒙版是逐畫素畫的,而 CSS 顏色名會退化成紅色。
資料檔案裡每個條目給出一個 id 和影像:
{"id": "img_001", "image_url": "https://example.com/street_001.jpg"}
{"id": "img_002", "image_url": "/static/images/street_002.jpg"}用這份配置啟動服務:
potato start config.yaml -p 8000畫一個多邊形,以及要按的鍵
畫多邊形就是沿著目標每個頂點點一下,然後雙擊閉合。一個多邊形至少要三個點才能圍出東西,而雙擊留下的重複頂點,Potato 會丟掉而不是存起來。
按鍵來自一套鍵位方案。預設的 v7 沿用 V7 Darwin 和 CVAT 的習慣,所以從這兩個工具過來的標註者可以保留肌肉記憶:
| 按鍵 | 動作 |
|---|---|
p | 選擇多邊形工具 |
v | 選擇與移動模式,不啟用任何繪製工具 |
| 雙擊 | 閉合當前多邊形 |
Delete 或 Backspace | 刪除選中的標註 |
1 到 9 | 按 key_value 選擇標籤 |
+ 或 = / - | 放大與縮小 |
0 | 讓影像適配檢視 |
按住 Space 或 Alt 並拖動 | 平移 |
Ctrl/Cmd+Z、Ctrl/Cmd+Shift+Z | 撤銷與重做 |
h、Shift+H | 隱藏當前標籤,或只顯示該標籤 |
如果你的研究已經在收資料,就在 scheme 上設 keybinding_profile: legacy,因為 v7 重新綁定了 b、f 和 l,研究中途換鍵位會損失準確率。在平板上,單指繪製,雙指平移,雙指捏合縮放。
要改一個已經畫好的多邊形,切到選擇模式再拖它的頂點。處理曲線邊界的標註者,先鋪一條粗略輪廓再細化,比第一遍就想把每個頂點放準效果更好,而且描細碎部分之前就該先放大,而不是描完再放大。
重疊的目標與實例
兩個目標重疊時,標註者就畫兩個相互重疊的多邊形。Potato 把每個形狀作為獨立條目儲存,所以一個有四輛車的場景就是四個多邊形,無論它們是否互相遮擋,而且實例身份會一直保留到匯出結果裡。畫多邊形時,語義任務和實例任務在配置上不需要有任何區別;區別在於你怎麼消費輸出,因為語義模型要的是同類形狀合併,實例模型要的是把它們分開。
對畫筆蒙版來說,這個區別是一個設定項而不是消費方的決定,因為 mask_mode: semantic 會把同一類的每一筆合併成一個區域,而 mask_mode: instance 會把目標分開。
輸出格式
標註以一個扁平的 JSON 陣列返回,所有形狀都在同一個列表裡。座標相對影像歸一化到 0 到 1 的範圍,所以即使之後影像以不同尺寸提供,標註依然正確:
[
{
"type": "polygon",
"label": "vehicle",
"color": "#FF6B6B",
"coordinates": [
{"x": 0.02, "y": 0.04}, {"x": 0.20, "y": 0.04},
{"x": 0.20, "y": 0.21}, {"x": 0.02, "y": 0.21}
]
},
{
"type": "bbox",
"label": "person",
"color": "#4ECDC4",
"coordinates": {"x": 0.125, "y": 0.104, "width": 0.3125, "height": 0.625}
}
]每種形狀類型對自己幾何結構的命名各不相同。多邊形的 coordinates 是一串點,邊界框是一個頻寬高的物件,畫筆蒙版則帶一個行優先遊程編碼的 rle 和一個 [height, width] 形式的 size。要在 Python 裡轉換這些形狀,請用 potato.export.cv_utils 裡的 normalize_annotation_object() 和 to_client_object(),不要直接讀欄位,這樣儲存方式變了你的程式碼還能用。
多邊形邊界上的一致性
標註者之間的平均 IoU 是多數工具會報的數字,而它回答的是錯的問題。如果一個語料裡每張圖都是一個居中的大目標,那麼無論誰來畫,分數都在 0.95 左右,包括一個根本沒看圖的人,因為 IoU 裡沒有任何機遇校正。
Potato 在 /admin/iaa 為幾何 schema 報告經過機遇校正的指標,並把問題拆成三部分:detection_alpha 看標註者是否找到了同樣的目標,classification_alpha 看他們對每個目標是什麼是否一致,sigma 和 ks 看他們是否畫在了同一個位置。這樣拆開是有診斷價值的。檢測高而定位低,說明繪製規範太鬆;反過來則說明目標的定義不清楚。測量邊界框上的一致性講了適用於所有形狀類型的方法。
另一件值得看的事情是頂點數。一個曲線目標只用四個頂點的多邊形其實是偽裝的邊界框,繪製遙測裡的 vertices_median 能把它暴露出來。