YOLO關(guān)鍵點(diǎn)格式:避坑指南與腳本實(shí)現(xiàn))
關(guān)鍵點(diǎn)檢測(cè)這個(gè)方向做的人不少但真正踩過(guò)數(shù)據(jù)格式轉(zhuǎn)換坑的人才知道從標(biāo)注工具到訓(xùn)練框架之間那條“最后一公里”有多磨人。我最近在做一個(gè)自用的姿態(tài)估計(jì)小項(xiàng)目標(biāo)注環(huán)節(jié)用的是labelme訓(xùn)練端用的是YOLO系列的關(guān)鍵點(diǎn)檢測(cè)分支中間卡在格式轉(zhuǎn)換上整整折騰了兩天。標(biāo)題里說(shuō)的“l(fā)abelme2yolo格式改變”說(shuō)白了就是把labelme那套JSON標(biāo)注轉(zhuǎn)成YOLO關(guān)鍵點(diǎn)檢測(cè)能直接吃的TXT格式同時(shí)把關(guān)鍵點(diǎn)順序、可見(jiàn)性標(biāo)志、歸一化坐標(biāo)這些細(xì)節(jié)全部對(duì)齊。這件事看起來(lái)只是寫(xiě)個(gè)腳本的事但實(shí)際動(dòng)手你會(huì)發(fā)現(xiàn)坑集中在坐標(biāo)系差異、關(guān)鍵點(diǎn)索引映射、以及YOLO對(duì)關(guān)鍵點(diǎn)可見(jiàn)性的特殊約定上。這篇內(nèi)容適合正在做關(guān)鍵點(diǎn)檢測(cè)、手里已經(jīng)有一批labelme標(biāo)注、又不想重新標(biāo)一遍的同行參考也適合剛接觸YOLO關(guān)鍵點(diǎn)任務(wù)、想搞清楚標(biāo)注格式底層邏輯的朋友。下面我把自己從踩坑到跑通的全過(guò)程拆開(kāi)講包括腳本怎么寫(xiě)、參數(shù)怎么定、哪里最容易出錯(cuò)。1. 整體思路與方案選型拆解1.1 為什么不用現(xiàn)成轉(zhuǎn)換工具而選擇自己寫(xiě)腳本市面上確實(shí)有一些開(kāi)源的labelme轉(zhuǎn)YOLO工具我一開(kāi)始也試了兩個(gè)但很快就放棄了。原因很直接絕大多數(shù)現(xiàn)成工具是針對(duì)目標(biāo)檢測(cè)寫(xiě)的輸出的是類(lèi)別加邊界框根本不處理關(guān)鍵點(diǎn)。少數(shù)支持關(guān)鍵點(diǎn)的腳本關(guān)鍵點(diǎn)順序是寫(xiě)死的而我的項(xiàng)目里關(guān)鍵點(diǎn)定義有自己的順序要求比如先鼻尖再左右眼再左右耳跟腳本內(nèi)置的順序?qū)Σ簧限D(zhuǎn)出來(lái)之后訓(xùn)練時(shí)關(guān)鍵點(diǎn)全串位了。還有一個(gè)更隱蔽的問(wèn)題現(xiàn)成工具對(duì)“可見(jiàn)性”的處理往往是二值的要么可見(jiàn)要么不可見(jiàn)但YOLO關(guān)鍵點(diǎn)格式實(shí)際上支持三種狀態(tài)可見(jiàn)、遮擋、未標(biāo)注。這個(gè)差異在訓(xùn)練時(shí)影響很大遮擋點(diǎn)如果被當(dāng)成可見(jiàn)點(diǎn)參與損失計(jì)算模型會(huì)學(xué)到錯(cuò)誤的定位。自己寫(xiě)腳本的好處是每個(gè)環(huán)節(jié)都可控。坐標(biāo)系怎么轉(zhuǎn)、關(guān)鍵點(diǎn)怎么排序、可見(jiàn)性怎么映射、歸一化用哪張圖的寬高全部由自己決定。而且腳本一旦跑通后續(xù)換數(shù)據(jù)集、加關(guān)鍵點(diǎn)、改類(lèi)別只需要?jiǎng)訋仔信渲貌挥迷偃プx別人的源碼猜邏輯。我最終的方案是用Python寫(xiě)一個(gè)單文件腳本讀labelme的JSON目錄輸出YOLO需要的images和labels兩個(gè)目錄關(guān)鍵點(diǎn)順序和可見(jiàn)性映射通過(guò)一個(gè)配置字典管理。1.2 labelme與YOLO關(guān)鍵點(diǎn)格式的核心差異要把這件事講清楚得先把兩邊的格式擺出來(lái)對(duì)比。labelme標(biāo)注關(guān)鍵點(diǎn)時(shí)每個(gè)點(diǎn)是一個(gè)shape類(lèi)型是point坐標(biāo)是圖像像素坐標(biāo)附帶一個(gè)label名字。一張圖的所有點(diǎn)放在同一個(gè)JSON的shapes數(shù)組里順序就是你點(diǎn)擊的順序。而YOLO關(guān)鍵點(diǎn)檢測(cè)的標(biāo)注格式一行代表一個(gè)目標(biāo)結(jié)構(gòu)是類(lèi)別索引、邊界框中心x、邊界框中心y、邊界框?qū)?、邊界框高后面緊跟每個(gè)關(guān)鍵點(diǎn)的x、y、可見(jiàn)性全部歸一化到0到1之間。這里第一個(gè)大坑就出現(xiàn)了labelme只給你點(diǎn)不給你邊界框。YOLO格式卻強(qiáng)制要求前面有框。這個(gè)框怎么來(lái)有兩種做法一種是用所有關(guān)鍵點(diǎn)的外接矩形當(dāng)框另一種是人工再標(biāo)一遍框。我選的是外接矩形因?yàn)殛P(guān)鍵點(diǎn)檢測(cè)任務(wù)里框本身不是重點(diǎn)它更多是給模型一個(gè)目標(biāo)級(jí)的定位先驗(yàn)用關(guān)鍵點(diǎn)外接矩形完全夠用。但外接矩形要留一點(diǎn)余量否則框貼得太緊訓(xùn)練時(shí)數(shù)據(jù)增強(qiáng)裁剪容易把邊緣關(guān)鍵點(diǎn)裁掉。第二個(gè)差異是歸一化基準(zhǔn)。labelme的坐標(biāo)是絕對(duì)像素YOLO要的是相對(duì)圖像寬高的比例。這里必須用每張圖自己的寬高去歸一化不能用數(shù)據(jù)集統(tǒng)一的尺寸否則不同分辨率的圖會(huì)全部錯(cuò)位。我在腳本里是逐圖讀取JSON里的imageWidth和imageHeight字段來(lái)做歸一化的。第三個(gè)差異是可見(jiàn)性。labelme的point沒(méi)有可見(jiàn)性字段所有點(diǎn)默認(rèn)都是標(biāo)了的。但實(shí)際標(biāo)注時(shí)有些點(diǎn)是被遮擋的標(biāo)注者可能憑經(jīng)驗(yàn)點(diǎn)了個(gè)大概位置。YOLO的可見(jiàn)性約定是0表示未標(biāo)注、1表示遮擋、2表示可見(jiàn)。我的處理策略是在labelme的label名字里加后綴來(lái)區(qū)分比如nose_vis表示可見(jiàn)nose_occ表示遮擋腳本解析時(shí)根據(jù)后綴映射到1或2沒(méi)有后綴的默認(rèn)按可見(jiàn)處理。這樣既不用改標(biāo)注工具又能把可見(jiàn)性信息帶過(guò)去。1.3 關(guān)鍵點(diǎn)順序映射的設(shè)計(jì)考量關(guān)鍵點(diǎn)順序是整件事里最容易被忽視、又最致命的一環(huán)。YOLO訓(xùn)練時(shí)輸出層的每個(gè)關(guān)鍵點(diǎn)通道是固定順序的你的標(biāo)注順序必須和模型定義的關(guān)鍵點(diǎn)順序完全一致錯(cuò)一個(gè)位置整條數(shù)據(jù)就廢了。labelme里點(diǎn)的順序取決于標(biāo)注者點(diǎn)擊的先后不同人標(biāo)、甚至同一個(gè)人不同時(shí)間標(biāo)順序都可能不一樣。所以腳本里必須有一個(gè)顯式的映射表把label名字映射到固定的索引。我的做法是維護(hù)一個(gè)有序列表比如[nose, left_eye, right_eye, left_ear, right_ear, ...]腳本讀到一個(gè)點(diǎn)就去列表里查它的名字對(duì)應(yīng)的索引然后按索引把坐標(biāo)填到對(duì)應(yīng)位置。如果某個(gè)關(guān)鍵點(diǎn)在圖上沒(méi)標(biāo)就填0、0、0可見(jiàn)性填0表示未標(biāo)注。這個(gè)設(shè)計(jì)的好處是無(wú)論標(biāo)注者點(diǎn)擊順序如何輸出永遠(yuǎn)是固定順序。這里有個(gè)細(xì)節(jié)要注意映射表里的名字必須和labelme里寫(xiě)的label完全一致大小寫(xiě)、下劃線(xiàn)都不能差我一開(kāi)始就是因?yàn)閘eft_eye寫(xiě)成了leftEye排查了半天才發(fā)現(xiàn)是名字對(duì)不上導(dǎo)致整批點(diǎn)被當(dāng)成未標(biāo)注。2. 核心細(xì)節(jié)解析與實(shí)操要點(diǎn)2.1 坐標(biāo)歸一化的計(jì)算過(guò)程與邊界處理歸一化的公式本身很簡(jiǎn)單x_norm等于x_pixel除以image_widthy_norm等于y_pixel除以image_height。但實(shí)操中有幾個(gè)邊界情況必須處理。第一坐標(biāo)可能因?yàn)闃?biāo)注時(shí)的像素取整出現(xiàn)等于image_width的情況歸一化后變成1.0而YOLO要求坐標(biāo)嚴(yán)格在0到1之間等于1在某些實(shí)現(xiàn)里會(huì)越界。我的處理是統(tǒng)一做一次截?cái)喟褮w一化后的值限制在0.0001到0.9999之間避免邊界值引發(fā)訓(xùn)練時(shí)的索引異常。第二外接矩形的計(jì)算要基于歸一化前的像素坐標(biāo)算完再歸一化??虻闹行狞c(diǎn)是(x_min x_max) / 2寬是x_max - x_min。這里要注意如果某個(gè)目標(biāo)只有一個(gè)關(guān)鍵點(diǎn)被標(biāo)了外接矩形會(huì)退化成一個(gè)點(diǎn)寬高為0這種樣本在訓(xùn)練時(shí)會(huì)出問(wèn)題。我的策略是給框加一個(gè)最小尺寸比如寬高都不小于圖像尺寸的百分之一保證框有實(shí)際面積。這個(gè)最小尺寸不是拍腦袋定的太小起不到作用太大又會(huì)把框撐得離譜百分之一是我在幾百?gòu)垐D上試出來(lái)的經(jīng)驗(yàn)值既能兜住退化情況又不會(huì)明顯改變正常樣本的框。第三關(guān)于框的余量。前面提到外接矩形要留余量我的做法是把框的寬高各放大百分之十中心點(diǎn)不變。這個(gè)比例的依據(jù)是關(guān)鍵點(diǎn)檢測(cè)常用的數(shù)據(jù)增強(qiáng)里隨機(jī)裁剪的幅度一般在百分之十以?xún)?nèi)留百分之十的余量能保證裁剪后關(guān)鍵點(diǎn)仍在框內(nèi)。放大太多會(huì)讓框失去定位意義放大太少又防不住裁剪百分之十是個(gè)平衡點(diǎn)。2.2 可見(jiàn)性映射的實(shí)操約定可見(jiàn)性這塊我在標(biāo)注階段就做了約定而不是等到轉(zhuǎn)換時(shí)再猜。具體做法是在labelme的label命名上加后綴正常可見(jiàn)的點(diǎn)直接寫(xiě)名字比如nose被遮擋但位置可推斷的點(diǎn)寫(xiě)nose_occ完全看不清、只是占位的點(diǎn)寫(xiě)nose_inv。腳本解析時(shí)按后綴映射無(wú)后綴映射為2_occ映射為1_inv映射為0。這個(gè)約定的價(jià)值在于它把可見(jiàn)性判斷留在了標(biāo)注現(xiàn)場(chǎng)而不是轉(zhuǎn)換時(shí)憑坐標(biāo)猜。標(biāo)注的人最清楚那個(gè)點(diǎn)當(dāng)時(shí)能不能看見(jiàn)轉(zhuǎn)換腳本沒(méi)有這個(gè)信息。我試過(guò)用坐標(biāo)是否在圖像內(nèi)來(lái)推斷可見(jiàn)性結(jié)果完全不可靠因?yàn)檎趽觞c(diǎn)的坐標(biāo)也在圖像內(nèi)只是被別的物體擋住了。所以可見(jiàn)性必須由標(biāo)注環(huán)節(jié)帶出來(lái)這是我在返工一批數(shù)據(jù)之后得出的教訓(xùn)。注意可見(jiàn)性為0的點(diǎn)YOLO在計(jì)算關(guān)鍵點(diǎn)損失時(shí)會(huì)跳過(guò)但坐標(biāo)仍然要填。我一開(kāi)始圖省事未標(biāo)注的點(diǎn)直接不寫(xiě)導(dǎo)致那一行的關(guān)鍵點(diǎn)數(shù)量對(duì)不上訓(xùn)練直接報(bào)維度錯(cuò)誤。正確做法是每個(gè)關(guān)鍵點(diǎn)位置都要占位坐標(biāo)填0、0可見(jiàn)性填0。2.3 類(lèi)別索引與多目標(biāo)場(chǎng)景的處理labelme的JSON里一張圖可能有多個(gè)目標(biāo)每個(gè)目標(biāo)的點(diǎn)混在同一個(gè)shapes數(shù)組里。YOLO格式要求每個(gè)目標(biāo)單獨(dú)一行所以腳本必須能把點(diǎn)按目標(biāo)分組。labelme本身沒(méi)有目標(biāo)分組的概念我的做法是在label命名里再加一層前綴來(lái)標(biāo)識(shí)目標(biāo)比如person1_nose、person1_left_eye、person2_nose。腳本按前綴分組每組生成一行。這個(gè)設(shè)計(jì)對(duì)單目標(biāo)場(chǎng)景是冗余的但對(duì)多目標(biāo)場(chǎng)景是必需的。我做的項(xiàng)目里大部分圖是單人但有一批是雙人交互的如果不分組兩個(gè)人的點(diǎn)會(huì)被混成一行外接矩形也會(huì)把兩個(gè)人框在一起完全沒(méi)法用。加前綴的代價(jià)是標(biāo)注時(shí)多打幾個(gè)字但換來(lái)的是轉(zhuǎn)換邏輯的清晰。如果你的項(xiàng)目確定只有單目標(biāo)可以省掉這層前綴腳本里把整張圖的點(diǎn)當(dāng)成一個(gè)目標(biāo)處理即可。類(lèi)別索引方面我的項(xiàng)目只有一類(lèi)就是人索引填0。如果有多類(lèi)需要在配置里維護(hù)一個(gè)類(lèi)別名到索引的映射腳本按前綴里的類(lèi)別名去查。這里要注意YOLO的類(lèi)別索引是從0開(kāi)始的連續(xù)整數(shù)不能跳號(hào)否則訓(xùn)練時(shí)會(huì)報(bào)類(lèi)別數(shù)不匹配。3. 實(shí)操過(guò)程與核心環(huán)節(jié)實(shí)現(xiàn)3.1 環(huán)境準(zhǔn)備與目錄結(jié)構(gòu)約定腳本運(yùn)行環(huán)境很簡(jiǎn)單Python 3.8以上只依賴(lài)標(biāo)準(zhǔn)庫(kù)的json、os、glob不需要裝額外的包。這一點(diǎn)是我刻意保持的轉(zhuǎn)換腳本越輕越好不引入依賴(lài)就少一層環(huán)境問(wèn)題。目錄結(jié)構(gòu)上我約定labelme的JSON文件全部放在raw_json目錄下每張圖對(duì)應(yīng)一個(gè)同名JSON。腳本輸出到dataset目錄下面分images和labels兩個(gè)子目錄圖片從JSON同目錄復(fù)制過(guò)去標(biāo)注寫(xiě)成同名TXT。這里有個(gè)細(xì)節(jié)labelme保存的JSON里圖片路徑字段可能是絕對(duì)路徑也可能是相對(duì)路徑取決于標(biāo)注時(shí)的打開(kāi)方式。腳本不要依賴(lài)這個(gè)字段去讀圖而是直接用JSON文件名去掉后綴去raw_json同級(jí)或指定的圖片目錄里找同名圖片。我一開(kāi)始用JSON里的imagePath字段拼路徑結(jié)果換了一臺(tái)機(jī)器路徑全失效后來(lái)改成按文件名匹配就穩(wěn)了。3.2 轉(zhuǎn)換腳本的核心邏輯拆解腳本主體分四步。第一步遍歷JSON目錄逐個(gè)加載。第二步解析shapes按目標(biāo)前綴分組每組內(nèi)按label名字查關(guān)鍵點(diǎn)索引填坐標(biāo)和可見(jiàn)性。第三步計(jì)算每組的外接矩形加余量歸一化。第四步拼成YOLO行格式寫(xiě)入TXT同時(shí)復(fù)制圖片。關(guān)鍵代碼邏輯我用偽代碼說(shuō)明方便你對(duì)照自己的實(shí)現(xiàn)。解析部分的核心是維護(hù)一個(gè)keypoints數(shù)組長(zhǎng)度等于關(guān)鍵點(diǎn)總數(shù)初始全填[0, 0, 0]然后遍歷shapes對(duì)每個(gè)point類(lèi)型的shape解析出目標(biāo)前綴和關(guān)鍵點(diǎn)名查索引把歸一化坐標(biāo)和可見(jiàn)性填到對(duì)應(yīng)位置。這里歸一化需要imageWidth和imageHeight從JSON頂層字段讀。外接矩形計(jì)算時(shí)只統(tǒng)計(jì)可見(jiàn)性大于0的點(diǎn)因?yàn)槲礃?biāo)注的點(diǎn)坐標(biāo)是0、0如果參與計(jì)算會(huì)把框拉到圖像左上角。這是個(gè)很容易忽略的點(diǎn)我第一版就是所有點(diǎn)都參與結(jié)果有未標(biāo)注點(diǎn)的樣本框全部偏到左上角訓(xùn)練時(shí)模型完全學(xué)不到東西。改成只統(tǒng)計(jì)可見(jiàn)點(diǎn)之后框的位置就正常了。寫(xiě)TXT時(shí)每行格式是類(lèi)別 框中心x 框中心y 框?qū)?框高 點(diǎn)1x 點(diǎn)1y 點(diǎn)1v 點(diǎn)2x 點(diǎn)2y 點(diǎn)2v ...所有數(shù)值保留六位小數(shù)。保留六位是精度和文件大小的平衡太少會(huì)損失定位精度太多文件會(huì)變大且沒(méi)必要。3.3 參數(shù)配置與驗(yàn)證方法腳本里我抽出了幾個(gè)可配置參數(shù)集中放在文件頭部。關(guān)鍵點(diǎn)順序列表、類(lèi)別映射、框余量比例、最小框尺寸比例、可見(jiàn)性后綴映射這五個(gè)是必須按項(xiàng)目改的。改完之后先拿三五張圖跑一遍人工檢查輸出的TXT。檢查方法是把TXT里的歸一化坐標(biāo)乘回圖像寬高看是否落在關(guān)鍵點(diǎn)的實(shí)際位置上。我一般會(huì)挑一張關(guān)鍵點(diǎn)齊全的、一張有遮擋的、一張多目標(biāo)的三種情況都對(duì)了再跑全量。全量跑完之后還要做一次統(tǒng)計(jì)校驗(yàn)。統(tǒng)計(jì)每個(gè)關(guān)鍵點(diǎn)被標(biāo)注的次數(shù)如果某個(gè)關(guān)鍵點(diǎn)的標(biāo)注次數(shù)明顯低于其他點(diǎn)說(shuō)明映射表里可能有名字對(duì)不上的情況。我第一次跑全量就是靠這個(gè)統(tǒng)計(jì)發(fā)現(xiàn)right_ear的標(biāo)注次數(shù)是0回頭查才發(fā)現(xiàn)labelme里寫(xiě)的是rightEar。這個(gè)校驗(yàn)步驟花不了幾分鐘但能省掉訓(xùn)練跑一半才發(fā)現(xiàn)數(shù)據(jù)有問(wèn)題的返工。4. 常見(jiàn)問(wèn)題與排查技巧實(shí)錄4.1 轉(zhuǎn)換后訓(xùn)練報(bào)維度錯(cuò)誤的排查路徑維度錯(cuò)誤是轉(zhuǎn)換后最常見(jiàn)的問(wèn)題表現(xiàn)是訓(xùn)練啟動(dòng)時(shí)報(bào)關(guān)鍵點(diǎn)數(shù)量不匹配。排查順序我總結(jié)成三步。第一步看TXT每行的字段數(shù)是否一致用腳本統(tǒng)計(jì)每行的空格分隔字段數(shù)正常應(yīng)該是5加3倍關(guān)鍵點(diǎn)數(shù)。如果某行字段數(shù)不對(duì)說(shuō)明那個(gè)目標(biāo)的關(guān)鍵點(diǎn)沒(méi)填滿(mǎn)多半是某個(gè)點(diǎn)的label名字沒(méi)在映射表里查到被跳過(guò)了。第二步看關(guān)鍵點(diǎn)總數(shù)配置和映射表長(zhǎng)度是否一致這兩個(gè)數(shù)必須相等。第三步看類(lèi)別索引是否從0開(kāi)始連續(xù)跳號(hào)會(huì)導(dǎo)致類(lèi)別數(shù)對(duì)不上。4.2 關(guān)鍵點(diǎn)位置整體偏移的定位方法如果訓(xùn)練能跑但關(guān)鍵點(diǎn)位置整體偏移問(wèn)題通常出在歸一化基準(zhǔn)上。最常見(jiàn)的是用了數(shù)據(jù)集統(tǒng)一的尺寸去歸一化而不是每張圖自己的寬高。排查方法是挑一張圖把TXT里的坐標(biāo)乘回該圖的實(shí)際寬高看是否和標(biāo)注位置吻合。如果不吻合檢查腳本里讀imageWidth和imageHeight的地方確認(rèn)讀的是當(dāng)前JSON的字段而不是全局變量。還有一種可能是坐標(biāo)順序?qū)懛戳藊和y對(duì)調(diào)這種偏移表現(xiàn)為關(guān)鍵點(diǎn)整體沿對(duì)角線(xiàn)翻轉(zhuǎn)比較容易識(shí)別。4.3 常見(jiàn)問(wèn)題速查表問(wèn)題現(xiàn)象可能原因排查與解決訓(xùn)練報(bào)關(guān)鍵點(diǎn)維度錯(cuò)誤某行字段數(shù)不足統(tǒng)計(jì)每行字段數(shù)檢查映射表名字是否與label完全一致關(guān)鍵點(diǎn)整體偏移歸一化用了統(tǒng)一尺寸改為逐圖讀取imageWidth和imageHeight框偏到圖像左上角未標(biāo)注點(diǎn)參與了外接矩形計(jì)算只統(tǒng)計(jì)可見(jiàn)性大于0的點(diǎn)多目標(biāo)混成一行缺少目標(biāo)前綴分組在label命名加目標(biāo)前綴腳本按前綴分組遮擋點(diǎn)被當(dāng)成可見(jiàn)點(diǎn)可見(jiàn)性未從標(biāo)注帶出在label加后綴約定可見(jiàn)性腳本按后綴映射坐標(biāo)等于1越界像素取整導(dǎo)致歸一化后截?cái)嗟?.0001至0.99994.4 獨(dú)家避坑心得第一個(gè)心得是關(guān)于標(biāo)注規(guī)范的。轉(zhuǎn)換腳本寫(xiě)得再好也救不了標(biāo)注不規(guī)范的數(shù)據(jù)。我在項(xiàng)目開(kāi)始前就定了一份標(biāo)注約定文檔明確關(guān)鍵點(diǎn)名字、后綴含義、目標(biāo)前綴格式所有參與標(biāo)注的人按同一份文檔來(lái)。這份文檔看起來(lái)是額外工作但它讓轉(zhuǎn)換腳本的映射表一次寫(xiě)對(duì)后面零返工。第二個(gè)心得是關(guān)于增量轉(zhuǎn)換的。項(xiàng)目進(jìn)行中會(huì)不斷有新標(biāo)注進(jìn)來(lái)我的腳本設(shè)計(jì)成可以重復(fù)跑每次全量覆蓋輸出。這樣不用擔(dān)心增量同步的問(wèn)題反正轉(zhuǎn)換很快幾百?gòu)垐D幾秒鐘的事。但要注意覆蓋前先備份上一次的輸出萬(wàn)一新標(biāo)注有問(wèn)題還能回退。第三個(gè)心得是關(guān)于小樣本驗(yàn)證的。每次改完腳本不要直接跑全量先挑三類(lèi)樣本跑關(guān)鍵點(diǎn)齊全的、有遮擋的、多目標(biāo)的。這三類(lèi)過(guò)了基本就沒(méi)大問(wèn)題了。我吃過(guò)直接跑全量、訓(xùn)練跑了兩天才發(fā)現(xiàn)遮擋點(diǎn)全錯(cuò)的虧小樣本驗(yàn)證花五分鐘能省兩天。最后再分享一個(gè)腳本里的小設(shè)計(jì)輸出TXT的同時(shí)生成一個(gè)轉(zhuǎn)換日志記錄每張圖的關(guān)鍵點(diǎn)數(shù)量、可見(jiàn)點(diǎn)數(shù)量、是否有多目標(biāo)。這個(gè)日志在排查問(wèn)題時(shí)特別有用比如某個(gè)關(guān)鍵點(diǎn)標(biāo)注次數(shù)異常翻日志就能定位到具體是哪幾張圖出的問(wèn)題不用再去翻原始JSON。這個(gè)日志我一開(kāi)始沒(méi)加后來(lái)排查問(wèn)題排煩了才補(bǔ)上補(bǔ)上之后排查效率提升明顯。