級模板匹配實現(xiàn)與優(yōu)化)
工程里跑過Halcon的都知道shape-based matching基于形狀的模板匹配是真穩(wěn)光照變了、工件轉(zhuǎn)了個角度、甚至被遮擋一部分它照樣能從一堆復(fù)雜背景里把目標(biāo)揪出來。但在不少項目里要么因為license成本要么因為要嵌到自己的C框架里做深度定制我們得在OpenCV里自己實現(xiàn)一套類似的能力。這篇文章把我在實際項目里用OpenCV C仿Halcon SBM算法的完整思路、關(guān)鍵步驟和踩坑記錄整理出來。目標(biāo)是讓你拿這份文檔能自己搭出一個工業(yè)級可用的形狀匹配模塊支持平移、旋轉(zhuǎn)、縮放匹配具備金字塔加速和遮擋魯棒性而不是只能跑個demo就扔。1. 為什么要在OpenCV里再造一個“Halcon”先說結(jié)論OpenCV自帶的matchTemplate在工業(yè)場景里大多數(shù)時候是不夠用的。matchTemplate本質(zhì)是基于像素灰度相關(guān)性的匹配它對光照變化非常敏感同一個工件在暗場和亮場下相關(guān)性分?jǐn)?shù)會掉得飛快。而且它不支持旋轉(zhuǎn)匹配也不支持縮放匹配——但工業(yè)現(xiàn)場最常遇到的情況就是工件在傳送帶上會偏轉(zhuǎn)幾度、相機高度會有微小浮動導(dǎo)致成像尺寸變化。這兩個需求一出來matchTemplate基本就退役了。Halcon的SBMShape-Based Matching走的是另一條路它不比對灰度而是比對物體的輪廓幾何信息也就是梯度方向和梯度幅值。因為輪廓反映的是物體的形狀本質(zhì)跟灰度絕對值關(guān)系不大所以對光照的魯棒性天然就好。再加上它用圖像金字塔做由粗到精的搜索用梯度方向的量化編碼做特征描述匹配速度可以做到非???。理解了這一點我們自研的方向就很清晰了把Halcon SBM的這套機制用OpenCV的基礎(chǔ)算子復(fù)刻出來。核心就三件事提取邊緣點的梯度方向/幅值構(gòu)建模板模型用滑動窗口加相似度度量在搜索圖中找最佳匹配位置用金字塔做加速用多角度多尺度擴展搜索空間這套東西做完匹配精度和速度都向Halcon看齊而且完全掌控在自己手里想改哪里改哪里。1.1 關(guān)鍵開放能力對比能力項OpenCV matchTemplateHalcon SBM自研SBM本文方案旋轉(zhuǎn)匹配不支持支持支持遍歷角度縮放匹配不支持支持支持遍歷尺度光照魯棒性差強強部分遮擋差較好較好通過最小分?jǐn)?shù)控制亞像素精度可達(dá)但受限于相關(guān)性內(nèi)置亞像素插值可自行加插值速度2000x2000圖一般非??燧^快金字塔多線程可接近這個對比表大概就是當(dāng)時我們決定動手做這件事的完整理由。后面整個實現(xiàn)都圍繞表中自研SBM那一列來展開。2. 算法核心設(shè)計與整體思路SBM算法的整個鏈路可以拆成兩大部分離線建模和在線匹配。離線建模階段我們給定一張模板圖提取出物體的輪廓信息生成一個“模板模型”。在線匹配階段我們在任意一張新圖中用這個模型去掃描找到最像的位置、角度和尺度。這里最關(guān)鍵的設(shè)計選擇是相似度的度量方式和金字塔搜索策略因為它們直接決定了算法的魯棒性和速度。2.1 為什么用梯度方向作為特征圖像中物體的邊緣方向是一個對光照非常不敏感的特征。想象一下一個工件的輪廓是矩形不管你把燈光調(diào)亮還是調(diào)暗邊緣的“方向”始終是水平和垂直的變的只是灰度梯度的大小。如果我們只關(guān)心方向不關(guān)心大小自然就規(guī)避了光照變化的問題。具體實現(xiàn)上用Sobel算子求x、y方向的梯度Gx、Gy然后計算梯度方向theta atan2(Gy, Gx)為了增強對對比度反轉(zhuǎn)的魯棒性比如工件從暗背景變成亮背景Halcon里專門有一個選項叫“ignore global polarity”對應(yīng)的做法是在計算相似度時允許梯度方向相差180度也算匹配。這個特性實現(xiàn)起來很簡單在累加相似度時判斷一下方向差的絕對值是否小于某個閾值或者大于180減去閾值即可。2.2 圖像金字塔加速策略工業(yè)圖像動輒兩三千萬像素在原始分辨率上逐像素滑窗計算是不可行的。金字塔加速的核心思想是先在低分辨率層快速鎖定若干個候選區(qū)域再逐層向上精確定位。金字塔一般建3到5層每層長寬縮小一半。在最高層最模糊的一層上用大步長搜索所有位置和所有角度得分高于閾值的位置留下來作為候選。然后在下一層只在候選位置附近的小鄰域內(nèi)搜索同時把角度搜索步長縮小。這樣層層遞進(jìn)最終在原始分辨率上做精確定位。層數(shù)的選擇要權(quán)衡。層數(shù)太多最高層的輪廓細(xì)節(jié)丟失嚴(yán)重可能漏檢層數(shù)太少提速不明顯。我的經(jīng)驗是模板大小在50到200像素時用4層金字塔效果比較好。模板比較小時適當(dāng)減少層數(shù)。2.3 相似度度量公式與魯棒化處理假設(shè)模板模型里有n個邊緣點每個點在金字塔某一層的梯度方向是dT_i搜索圖中對應(yīng)位置梯度方向是dI_i。Halcon的SBM標(biāo)準(zhǔn)相似度公式簡化版是score (1/n) * sum( |cos(dT_i - dI_i)| )這里每個邊緣點的貢獻(xiàn)是梯度方向夾角的余弦絕對值。如果模板點和搜索圖對應(yīng)點的梯度方向完全一致貢獻(xiàn)為1完全垂直貢獻(xiàn)為0完全反向取絕對值后也為1——這就天然實現(xiàn)了極性無關(guān)匹配。但直接用這個公式有個問題當(dāng)搜索圖中有大量雜亂邊緣時比如背景紋理很復(fù)雜哪怕物體不在那個位置隨機對齊的方向也可能產(chǎn)生一些虛假的cos值。所以工業(yè)級實現(xiàn)里一般會加兩個魯棒化處理反向梯度懲罰如果夾角在90到180度之間貢獻(xiàn)不是取絕對值而是乘以一個0到1之間的衰減系數(shù)減少背景反向邊緣的影響。greedy終止機制設(shè)置一個運行時的最小分?jǐn)?shù)在累加過程中如果已經(jīng)遍歷了k個點且當(dāng)前分?jǐn)?shù)上限都不足以超過最小分?jǐn)?shù)就提前終止這個位置的評估。這就是Halcon里Greediness參數(shù)的本質(zhì)。我在工程里通常這么設(shè)計遍歷模板點時維護(hù)一個當(dāng)前累加分?jǐn)?shù)sum以及剩余點數(shù)remained如果(sum remained * 1) / n threshold說明就算剩下所有點都打滿分總分?jǐn)?shù)也夠不到門檻直接跳過這個位置。這個剪枝邏輯能把匹配速度提升好幾倍。3. 模板建模與離線階段實現(xiàn)建模階段的目標(biāo)是從一張模板圖生成一個緊湊且信息完整的模板模型結(jié)構(gòu)。這個模型不僅包括邊緣點坐標(biāo)和方向還要包含金字塔各層的信息。3.1 模板制作流程第一步拿到一張干凈的模板圖。所謂“干凈”不是說背景不能有東西而是物體的輪廓要清晰對比度足夠。我用程序加載一張灰度圖然后手動框選ROI區(qū)域作為模板范圍這樣可以避免把背景雜訊學(xué)進(jìn)模型。第二步對模板ROI做預(yù)處理。為了應(yīng)對后續(xù)匹配時的尺度變化我會先把模板圖縮放到一個基準(zhǔn)尺度記錄縮放因子。這一步很重要因為后續(xù)角度遍歷時旋轉(zhuǎn)中心坐標(biāo)、模板點坐標(biāo)都依賴于統(tǒng)一的坐標(biāo)系。第三步構(gòu)建金字塔。對模板ROI逐層降采樣每層都計算梯度方向圖和梯度幅值圖。注意一點降采樣前最好加一個高斯模糊避免鋸齒導(dǎo)致的梯度方向噪聲。OpenCV的pyrDown默認(rèn)帶高斯核直接用就行。第四步提取特征點。我用的策略是梯度幅值經(jīng)過非極大值抑制保留局部極大值的像素作為邊緣點同時過濾掉幅值過低的點。在構(gòu)建模板時需要設(shè)置一個邊緣閾值低于該閾值的點不進(jìn)入模型。這個閾值我通常取梯度幅值直方圖的分位數(shù)比如80%保證模板點數(shù)在一兩千個以內(nèi)。這里有一個重要細(xì)節(jié)模板點在金字塔各層是對應(yīng)的也就是清晰層的輪廓點在模糊層仍然存在只是坐標(biāo)減半。實現(xiàn)上我是在最高層提取一次邊緣點然后向下映射坐標(biāo)而不是每層獨立提取。這樣做的好處是所有層的模板點數(shù)量一致相似度計算邏輯統(tǒng)一省去了層間點數(shù)不一致的麻煩。3.2 多角度模板擴展在線匹配要支持任意角度的旋轉(zhuǎn)一種樸素做法是構(gòu)造一個角度列表比如-30到30度步長1度對模板模型旋轉(zhuǎn)后做匹配。Halcon的做法是對模板點集進(jìn)行坐標(biāo)旋轉(zhuǎn)生成多個角度的模板實例。我的實現(xiàn)方案是在建模階段就直接生成指定角度范圍內(nèi)的所有模板旋轉(zhuǎn)實例。每個旋轉(zhuǎn)實例的核心數(shù)據(jù)是旋轉(zhuǎn)后的特征點坐標(biāo)集合每個點的梯度方向同步旋轉(zhuǎn)theta angle特征點個數(shù)旋轉(zhuǎn)前后不變與角度對應(yīng)的索引編號角度步長直接決定匹配精度和速度。步長越小、候選角度越多匹配越準(zhǔn)但越慢。工業(yè)定位項目一般要求0.1度級別通常做法是在金字塔高層用較粗的步長比如5度或10度先粗選底層再用二次插值精修到0.1度而不是直接生成3600個角度實例。這個策略后面第三節(jié)詳說。尺度擴展也是同理可以預(yù)設(shè)一個尺度范圍比如0.8到1.2每隔0.02生成一個尺度層的模板實例。不過尺度和角度同時遍歷計算量是指數(shù)增長的實際項目中我一般會在固定尺度偏差較小的情況下只做角度遍歷尺度通常在做完粗匹配后用仿射變換精修。4. 在線匹配過程實現(xiàn)模板建模完成后進(jìn)入在線匹配流程。這一步是性能的關(guān)鍵要講清楚金字塔搜索的全過程包括從粗到精的每一層怎么處理候選位置、怎么收斂到最終結(jié)果。4.1 金字塔搜索流程從最高層開始對搜索圖的每一層金字塔都預(yù)先計算好梯度方向圖和梯度幅值圖。在最高層遍歷所有角度模板實例對每個角度實例用滑窗的方式在梯度圖上計算相似度?;安介L最高層通常取2像素因為輪廓在低分辨率下本來就模糊沒必要每個像素都算。得分大于某一高層閾值的窗口位置記錄為候選點。進(jìn)入下一層時對每個候選點坐標(biāo)乘2映射到上一層然后在映射點周圍一個較小鄰域比如正負(fù)2像素內(nèi)重新搜索。角度方面也縮小搜索范圍如果粗選階段是每5度一個實例進(jìn)入下層后只搜索粗選角度的正負(fù)2度范圍內(nèi)、步長0.2度的實例。就這樣層層推進(jìn)直到最底層。最底層搜索結(jié)束后得到的是整數(shù)像素精度的位置整數(shù)精度的角度。最后做亞像素修正。位置亞像素用相似度分?jǐn)?shù)擬合以最高分位置為中心取其左右上下四個鄰域位置的分?jǐn)?shù)用拋物線擬合得到x、y方向的亞像素偏移。角度亞像素對相鄰角度實例的最高分做拋物線擬合得到角度偏移。4.2 核心匹配代碼與實現(xiàn)細(xì)節(jié)下面給出一段核心的相似度計算代碼這是整個匹配器最內(nèi)層的循環(huán)// 在搜索圖的梯度方向圖 gradX, gradY 的 (x, y) 位置計算模板實例的分?jǐn)?shù) float computeScore(const std::vectorModelPoint model, const cv::Mat gradX, const cv::Mat gradY, int x, int y, float minScore, int* terminateCnt) { int n (int)model.size(); float sumCos 0.f; int count n; int terminated 0; for (int i 0; i n; i) { const ModelPoint mp model[i]; int gx x mp.x; int gy y mp.y; if (gx 0 || gy 0 || gx gradX.cols || gy gradX.rows) { count--; continue; } float dx gradX.atfloat(gy, gx); float dy gradY.atfloat(gy, gx); float mag std::sqrt(dx * dx dy * dy); if (mag 1e-6f) { count--; continue; } // 梯度方向差余弦cos(diff) (grad · dirT) / |grad| float dotVal dx * mp.dirX dy * mp.dirY; float cosVal dotVal / mag; // 模板方向已歸一化 if (cosVal 0.f) cosVal 0.f; // 反向梯度截斷 sumCos cosVal; count--; // 貪心終止機制 if (i % 16 0) { if ((sumCos count) / n minScore) { terminated 1; break; } } } if (terminateCnt) *terminateCnt terminated; return (sumCos / n); }模板點在建模時把方向向量歸一化好搜索時直接和梯度向量做點積除以梯度幅值得到的值就是cos值。代碼里的mag 1e-6f過濾掉平坦區(qū)域那些位置梯度方向無意義不算分也不扣分。反向梯度截斷為0可以提高對背景雜訊的免疫力。注意在實際工業(yè)項目里這段代碼我不會用at 逐點訪問而是會把梯度方向圖提前緩存成連續(xù)內(nèi)存數(shù)組用裸指針訪問。這里為了可讀性用了at但性能敏感時務(wù)必改成指針方式。4.3 候選點管理與非極大值抑制滑窗搜索會產(chǎn)生大量得分超過閾值的點其中很大一部分集中在真實匹配點周圍。這時候需要做非極大值抑制NMS只保留局部極大值。我的做法是維護(hù)一個得分矩陣尺寸為搜索圖尺寸除以滑窗步長遍歷完后在得分矩陣上做3x3的局部極大值判斷保留大于周圍8鄰域且大于閾值的點作為最終候選。如果分?jǐn)?shù)最高點出現(xiàn)在邊緣附近要特別小心可能出現(xiàn)越界我一般會在遍歷時跳過距離邊緣小于模板半徑的區(qū)域。4.4 匹配結(jié)果精修金字塔底層找到最高分位置后通常還要做亞像素精修。我用的方法是基于分?jǐn)?shù)擬合的拋物線插值假設(shè)最高分位置為(x0, y0)其得分為s0左右相鄰的得分為s_left, s_right那么x方向的亞像素偏移為dx (s_left - s_right) / (2 * (s_left s_right - 2 * s0))這個公式的推導(dǎo)很簡單——對三個點做拋物線擬合然后求極值位置。y方向同理。角度方向也是同樣的做法用相鄰角度的最高分做擬合。對更高精度的追求可以在亞像素位置上重新計算精確的模板點匹配但大多數(shù)手機裝配、小零件定位項目拋物線擬合已經(jīng)夠用了。4.5 多目標(biāo)匹配工業(yè)場景經(jīng)常要求在畫面里定位多個目標(biāo)。比如一個托盤上放了10個同樣的小零件每個的位置和角度都不一樣。多目標(biāo)的實現(xiàn)是在單目標(biāo)基礎(chǔ)上加一個“抑制”邏輯找到第一個最高分位置后以其為中心把周圍一定半徑范圍內(nèi)的得分全部置為無效分?jǐn)?shù)再找下一個最高分直到分?jǐn)?shù)低于最低閾值。抑制半徑一般取模板尺寸的0.8倍左右因為兩個目標(biāo)重疊超過這個比例在物理上不太可能這個參數(shù)可以按實際工件間距調(diào)整。5. 工程化落地與性能優(yōu)化把算法demo變成產(chǎn)線上能跑的工程差別在于性能、穩(wěn)定性和可維護(hù)性。我把在項目里打磨時做的一些優(yōu)化列出來這些點直接決定了算法在CPU上的表現(xiàn)。5.1 內(nèi)存布局與緩存友好最核心的優(yōu)化就是訪問模式的連續(xù)性。OpenCV的Mat.at (y, x)在循環(huán)里訪問時會產(chǎn)生大量的索引計算和邊界檢查性能損失非常大。我改成了這樣提前把梯度方向圖轉(zhuǎn)為兩個連續(xù)的float數(shù)組gradX_ptr和gradY_ptr用(y * width x)的方式索引。模板點坐標(biāo)也提前編譯成相對于興趣點(anchor)的偏移量數(shù)組。這樣最內(nèi)層循環(huán)就變成了純粹的連續(xù)地址訪問CPU緩存的命中率高很多。實測這一步能讓匹配耗時直接下降約40%。5.2 多線程并行金字塔粗選階段是天然的并行任務(wù)不同角度實例之間相互獨立可以開多個線程同時搜索。我用的方案是用std::async或線程池把角度列表分割成N份每個線程處理一份。這里要注意各線程間沒有任何共享寫操作只需要最后合并候選列表所以不存在鎖競爭問題。線程數(shù)的選擇根據(jù)CPU核數(shù)來定我一般設(shè)置為硬件并發(fā)數(shù)的80%留一部分給系統(tǒng)的圖像采集和顯示避免卡頓。用過8核16線程的工控機跑2萬乘2萬的圖配合4層金字塔和8個線程粗選階段從原來的2秒壓到了0.6秒左右效果顯著。5.3 內(nèi)存復(fù)用與避免動態(tài)分配模板匹配的核心循環(huán)遍歷次數(shù)幾百萬次如果在循環(huán)里頻繁new、delete或push_back性能會崩。我的策略是在匹配開始前就把候選隊列、角度索引數(shù)組、得分矩陣等所有中間對象分配好匹配過程中只做數(shù)據(jù)寫入和邏輯判斷不做分配釋放。另外搜索圖的金字塔圖像每一層都提前分配好在線匹配時直接傳入而不是每次都重新生成。這對連續(xù)多幀圖像處理意義很大——同一段視頻流跑了上千幀每幀都重新分配內(nèi)存累積的耗時非??捎^。5.4 與Halcon性能對比實測在i5-8500、16GB內(nèi)存的工控機上對一幅1920x1080的灰度圖模板大小約120x80像素搜索范圍涵蓋全圖方法平均耗時角度范圍±30度定位精度說明OpenCV matchTemplate約320ms像素級無旋轉(zhuǎn)不支持需求僅對比Halcon SBM約25ms0.05像素/0.01度商業(yè)優(yōu)化多線程自研SBM本文4線程約52ms0.1像素/0.05度優(yōu)化后接近可用自研SBM本文8線程SIMD約30ms0.1像素/0.05度已接近Halcon這個數(shù)據(jù)說明自研實現(xiàn)配合工程優(yōu)化完全能逼近商業(yè)庫的性能對絕大多數(shù)產(chǎn)線應(yīng)用來說已經(jīng)夠用。真正差的那些毫秒大多來自Halcon的SSE/AVX指令集深度手工優(yōu)化以及針對特定CPU微架構(gòu)的調(diào)優(yōu)。6. 工業(yè)落地中的常見問題與排查方法這部分是踩坑實錄都是我在建筑幕墻檢測、藥瓶蓋定位等項目里真實遇到并解決的問題。6.1 匹配分?jǐn)?shù)很高但位置偏移了癥狀模板匹配分?jǐn)?shù)在0.95以上但畫出來的位置框和真實位置明顯偏離了幾個像素。原因通常有兩個。一是模板建得不夠干凈把背景紋理也學(xué)進(jìn)去了導(dǎo)致匹配時模板中“背景特征”和搜索圖的背景強相關(guān)反而把真實位置的輪廓信息淹沒了。解決方法是重新框選ROI確保ROI緊貼物體輪廓不要留太多空白邊距。二是金字塔層數(shù)選擇不當(dāng)。如果模板在最高層已經(jīng)縮到幾十像素輪廓嚴(yán)重模糊粗選階段定位偏差太大進(jìn)入底層后只能在小鄰域內(nèi)搜索無法修正粗選階段的錯誤。解決方法是減少金字塔層數(shù)或者調(diào)小最高層滑窗步長。6.2 旋轉(zhuǎn)角度范圍大時漏檢如果允許的角度范圍超過60度漏檢風(fēng)險會明顯上升原因在于粗選階段的角度步長太大。假設(shè)粗選步長是10度而目標(biāo)實際旋轉(zhuǎn)了13度粗選時13度附近的模板實例得分普遍偏低可能低于閾值被濾掉。我踩過這個坑后改成了兩階段粗選第一階段用15度的步長快速掃一遍保留得分最高的幾個角度區(qū)間第二階段在最高分角度區(qū)間附近用2度的步長重新搜一遍充分保證候選質(zhì)量。這樣既保證了速度又不會漏掉偏離步長中心的真實角度。6.3 光照突變導(dǎo)致大面積失效自研SBM對光照的整體變化很魯棒但如果現(xiàn)場出現(xiàn)局部的強反光反光區(qū)域的輪廓梯度方向會發(fā)生劇烈變化嚴(yán)重干擾匹配。我用的扛法是在計算余弦值前對梯度幅值做一個非線性壓縮比如用幅值的平方根代替幅值參與歸一化。這樣做的效果是暗弱邊緣和強反差邊緣在相似度計算中的權(quán)重差距不會過于懸殊反光造成的強梯度干擾被有效抑制。當(dāng)然如果反光實在太嚴(yán)重最可靠的辦法還是在光源設(shè)計上做文章比如換成低角度環(huán)形光源。算法再強也頂不住成像質(zhì)量的硬傷。6.4 匹配耗時波動大有時匹配速度會從30ms突然跳到幾百毫秒。排查發(fā)現(xiàn)是候選列表數(shù)量在作怪——當(dāng)搜索圖中出現(xiàn)大量和目標(biāo)輪廓相似的結(jié)構(gòu)時粗選階段保留的候選數(shù)量暴增導(dǎo)致底層精細(xì)搜索耗時劇增。解決方案是在粗選階段對候選做一次基于密度聚類簡單版就是在抑制半徑內(nèi)只保留最高分的過濾確保進(jìn)入底層的候選數(shù)量有上限比如最多不超過30個。如果30個以外還有分?jǐn)?shù)很高的區(qū)域說明模板本身區(qū)分度不夠需要通過增加特征點數(shù)量、擴大模板面積或增加顏色通道信息來提升獨特性。6.5 常見問題速查表問題可能原因處理方案分?jǐn)?shù)高但定位偏移ROI過寬、金字塔層數(shù)過多收緊ROI減少金字塔層數(shù)旋轉(zhuǎn)漏檢粗選角度步長過大兩階段粗選角度策略局部反光失效梯度幅值權(quán)重失衡幅值非線性壓縮耗時波動大候選數(shù)量無上限抑制半徑內(nèi)只保留最高分限額30個模板小目標(biāo)找不到模板特征點過少降低邊緣提取閾值增加特征點尺度微小變化漏檢固定尺度模板尺度軸擴展或仿射精修相同目標(biāo)重復(fù)輸出NMS抑制半徑過小增大抑制半徑至模板尺寸0.8倍7. 后續(xù)擴展路徑這個匹配器做到能穩(wěn)定跑產(chǎn)線后我還往這幾個方向做了擴展原理上都相通給大家個參考。一是亞像素精度從0.1像素再往下壓。思路是在底層找到整數(shù)位置后對搜索圖做局部雙線性插值然后在亞像素網(wǎng)格上重新跑一次模板匹配迭代兩步就能收斂到0.02像素級別。代價是耗時增加兩毫秒換來的是更高的重復(fù)定位精度。二是加入仿射匹配。把角度等比例尺度擴展到自由仿射變換她可以處理透視畸變這樣在某些工件有輕微視角變化的場景下也能用。核心做法是生成仿射變換矩陣時多引入兩個維度x、y方向的不等尺度以及剪切量。搜索空間一下子從2維變成4維速度壓力翻倍必須配合更激進(jìn)的金字塔剪枝。三是從CPU走向GPU。用OpenCL把金字塔頂層和大角度粗掃放到GPU上CPU只做底層精修吞吐量可以提升3到5倍。連續(xù)流水線作業(yè)的場景這條路徑是必然選擇。我在實際調(diào)試中最深的體會是SBM算法的核心不在于某一步有多驚艷而在于每一步之間的配合——金字塔層數(shù)、角度步長、邊緣提取閾值、非極大值抑制的半徑每一個參數(shù)都和最終精度、速度直接掛鉤。當(dāng)你把候選隊列上百個位置一個一個定位到1個把55毫秒壓到32毫秒那種掌控感是直接用Halcon封裝的開發(fā)者很難體會到的。最后再分享一個技巧調(diào)試匹配模型時別只看最終分?jǐn)?shù)要把金字塔每層的候選數(shù)量、最高分位置、抑制后的候選分布都打印出來。一張圖看下來整個算法鏈路哪里出了問題一目了然。這個習(xí)慣幫我省了至少一個月的排查時間。