
1. 表計讀數(shù)識別為什么必須加校正這一環(huán)做表計讀數(shù)識別的朋友應該都有過這種經(jīng)歷算法模型在干凈的樣張上識別準確率挺高一到現(xiàn)場實拍圖就全線崩潰。我第一版做字符分割時就踩過這個坑——采集器抓拍的表計圖像往往帶有明顯的旋轉(zhuǎn)和透視傾斜直接按固定區(qū)域切字符邊界全歪分割出來的數(shù)字殘缺不全識別率連五成都不到。后來才想明白讀數(shù)識別的前置條件不是這張圖里有表盤而是表盤必須正對相機刻度方向是標準的”?,F(xiàn)場安裝的攝像槍很難做到完美的正面拍攝角度一偏整個數(shù)字區(qū)域的幾何結(jié)構(gòu)就變了。所以第二篇我們要做的事情就是通過SURF特征匹配把任意角度、任意透視變形的表計圖像校正到和標準模板一致的位置。這個環(huán)節(jié)在整套表計讀數(shù)識別流程里承上啟下——上承接第一篇對表盤的定位與裁剪下為后續(xù)的刻度檢測、字符分割和讀數(shù)識別提供一張規(guī)矩的圖像。校正做得好不好直接影響后面每一步的容錯率。我用的開發(fā)環(huán)境是 VS2015 搭配 OpenCV整套流程全部用 C 實現(xiàn)。之所以在環(huán)境選擇上多費了些周折是因為 SURF 在 OpenCV 里的歸屬比較特殊——它不在主模塊里而是放在擴展模塊 opencv_contrib 的 xfeatures2d 命名空間中。版本匹配、動態(tài)庫鏈接、模塊加載每一步都可能浪費半天時間。這篇文章會把環(huán)境搭建、特征提取參數(shù)調(diào)節(jié)、匹配策略選擇、單應性矩陣求解與透視變換完整走一遍最后附上我在實際圖像上遇到的問題和調(diào)參經(jīng)驗供準備做同類項目的朋友參考。2. VS2015與OpenCV的版本匹配SURF模塊引入的兼容性取舍先交代環(huán)境細節(jié)。我本機是 Windows 10IDE 是 VS2015編譯器工具集對應 vc14。OpenCV 這里有個很容易踩的坑官方發(fā)布的預編譯庫Windows 版只提供 vc14 和 vc15 兩個工具集版本其中 vc14 對應的就是 VS2015vc15 對應 VS2017。如果你機器上裝了 VS2019直接下載 vc15 的庫也能用因為 VS 本身向后兼容但 VS2015 用戶就必須老老實實選 vc14 版本否則鏈接階段會報一大堆 LNK2038 之類的運行時庫不匹配錯誤。關(guān)于 OpenCV 的具體版本號我建議選 3.4.x 系列不要選 4.x也不建議選更老的 2.x。原因有三點2.x 的 API 風格比較老很多函數(shù)名和 3.x/4.x 不統(tǒng)一網(wǎng)上資料也以新版本為主排查問題不方便。4.x 雖然 API 和 3.x 基本一致但部分舊模塊位置有調(diào)整SURF 依然在 contrib 里可很多第三方博客針對的是 3.x 的路徑配置照抄容易出錯。3.4.x 處于一個穩(wěn)定性較高的狀態(tài)SURF、SIFT 這些非自由算法依然可以通過 contrib 正常調(diào)用社區(qū)資料也最豐富。我在項目里選用的是 OpenCV 3.4.16這也是 3.4 系列最后一個版本。下載的時候需要注意擴展包和解壓路徑有講究。下載頁面通常提供兩個壓縮包一個是標準包 opencv-3.4.16-vc14_vc15.exe另一個是擴展包 opencv_contrib-3.4.16.zip。標準包解壓到 D:\opencv3416擴展包不用單獨解壓直接保留 zip 文件即可因為 CMake 編譯的時候只需要指定它的路徑而不是先解壓到某個固定目錄。等等這里有個問題如果你下載的是官方預編譯的 Windows 庫SURF 是已經(jīng)編譯好的直接在鏈接器里加上 opencv_xfeatures2d3416.lib 就能用。但如果你的 OpenCV 是從源碼自己編譯的就需要先配置 OpenCV 的 CMake 工程把 OPENCV_EXTRA_MODULES_PATH 指向擴展包路徑然后重新生成解決方案。兩種方式我都試過。官方預編譯的方式最簡單但有個隱患——預編譯庫的 contrib 模塊默認是開啟狀態(tài)可實際使用中SURF 需要額外的第三方庫支持如果 install 目錄里的 include 文件不齊全編譯時找不到 features2d 頭文件會很麻煩。我后來實際部署時選擇了自己編譯因為項目需要同時啟用一些 contrib 下的私有模塊而預編譯庫只開放了默認子集。自己編譯時在 CMake 里需要打開 BUILD_EXAMPLES 和 BUILD_opencv_xfeatures2d同時確認 OPENCV_ENABLE_NONFREE 設(shè)置為 ON。這里特別提醒一下SURF 和 SIFT 屬于有專利限制的算法OpenCV 官方默認編譯時是不開啟的必須在 CMake 配置階段顯式把 OPENCV_ENABLE_NONFREE 打開否則雖然特征提取函數(shù)能編譯通過運行時會拋異常提示 This algorithm is patented and is excluded in this build。編譯耗時大概要一個多小時建議直接用 Release 和 Debug 兩套配置都編譯一遍避免后面調(diào)試時發(fā)現(xiàn) Debug 庫缺失又要重新來。我一開始只編了 Release后來程序崩了想用 Debug 查才發(fā)現(xiàn)沒有對應的 dll又等了將近四十分鐘白白浪費時間。環(huán)境配置部分include 目錄和 lib 目錄的設(shè)置不展開細說了有一點值得單獨提醒鏈接器附加依賴項里不要一股腦把 lib 全加進去至少需要以下幾個opencv_world3416.lib或你編譯時生成的 opencv_world 庫文件名opencv_xfeatures2d3416.libopencv_features2d3416.libopencv_core3416.libopencv_imgproc3416.libopencv_calib3d3416.lib鏈接順序無所謂VS 不像老式 GCC 那樣對庫順序敏感。運行時需要把對應的 opencv_world3416.dll 和 opencv_xfeatures2d3416.dll 放到 exe 同目錄否則啟動時報找不到 dll這是最常見的發(fā)布環(huán)境問題。3. 模板圖像選擇與SURF特征點提取的細節(jié)處理3.1 模板圖的準備邏輯校正的本質(zhì)是把待識別圖像變換到模板圖的視角下所以模板圖的質(zhì)量直接決定校正的上限。我的經(jīng)驗是模板圖必須滿足三個條件表盤完整清晰邊緣沒有被裁切。表盤中心盡量位于圖像中心長邊與圖像邊緣平行也就是標準的正面正對視角。表盤本體在畫面中占比盡量大不要把無關(guān)背景大量帶進來。你可以用一張已經(jīng)貼墻安裝、且相機正對表計拍攝的照片作為模板也可以用廠家提供的標準表計渲染圖。我個人更推薦用真實拍攝并人工轉(zhuǎn)正后的照片作為模板因為 SURF 特征點對光照變化有一定魯棒性但紋理細節(jié)與真實表的差距過大時匹配效果會明顯下降。比如我用的是某款智能電表的正面照片表盤上有液晶屏、單位標簽、條碼區(qū)、品牌標識這些都能提供豐富的角點和紋理特征SURF 才能找到足夠多的可區(qū)分性關(guān)鍵點。順帶說一下模板圖分辨率不宜過低。建議長邊至少 800 像素以上否則特征點數(shù)量不夠后面的匹配容易失敗。我用的模板是 1280x960 的灰度圖特征點提取數(shù)量默認在 400 左右效果比較理想。3.2 SURF檢測器的基本使用方式OpenCV 里 SURF 的調(diào)用方式很簡單但參數(shù)命名和版本間有差異。在 OpenCV 3.4.16 的 xfeatures2d 命名空間下標準寫法是#include opencv2/features2d.hpp #include opencv2/xfeatures2d.hpp #include opencv2/xfeatures2d/nonfree.hpp cv::Ptrcv::xfeatures2d::SURF detector cv::xfeatures2d::SURF::create(400); std::vectorcv::KeyPoint keypoints_tpl, keypoints_query; cv::Mat descriptors_tpl, descriptors_query; detector-detectAndCompute(templateImg, cv::Mat(), keypoints_tpl, descriptors_tpl); detector-detectAndCompute(queryImg, cv::Mat(), keypoints_query, descriptors_query);這里的 create(400) 其實是給的是 Hessian 閾值。SURF 算法通過計算圖像每個像素點的 Hessian 矩陣行列式來尋找斑點狀特征閾值越高檢測到的特征點越少但每個點的穩(wěn)定性越強。閾值 400 是個比較中庸的取值適合大多數(shù)場景。模板圖和查詢圖建議使用同一閾值避免兩個圖的特征點數(shù)量差異過大導致匹配失衡。3.3 Hessian閾值與特征點數(shù)量的聯(lián)動關(guān)系實際調(diào)參時我發(fā)現(xiàn)Hessian 閾值對結(jié)果的影響遠比想象中敏感。閾值設(shè)得過高比如 1000 以上模板圖和實拍圖可能各自只剩幾十個特征點匹配時正確匹配對數(shù)量不夠RANSAC 求單應性矩陣時容易出現(xiàn)退化情況。閾值設(shè)得過低比如 100特征點密密麻麻匹配計算量暴漲而且容易出現(xiàn)誤匹配反而降低模型精度。我在實際項目中采用了一個簡單的自適應策略先用 400 這個默認閾值提取特征點如果匹配率低于某個閾值比如正確匹配對少于 20 對就把 Hessian 閾值調(diào)低到 200 重新提取再不行降到 100。反之如果特征點數(shù)量超過 1500 且匹配很慢就上調(diào)到 600。項目里大多數(shù)表計圖像用 300~400 之間的閾值都能穩(wěn)定工作。另外SURF 默認檢測的是帶方向的特征點即每個關(guān)鍵點會計算一個主方向。這個方向的作用是讓描述子具備旋轉(zhuǎn)不變性。如果你的表計圖像本身傾斜角度不會很大比如只差 5~10 度可以把 create 時的參數(shù)改為不計算方向加速明顯。但本項目的核心需求恰恰就是矯正大角度傾斜所以方向信息必須保留不能為了性能犧牲旋轉(zhuǎn)適應性。3.4 描述子與匹配前的數(shù)據(jù)預處理detectAndCompute 函數(shù)返回的描述子矩陣類型是 CV_32F行數(shù)為關(guān)鍵點數(shù)量列數(shù)為描述子維度。SURF 默認描述子維度是 64也有擴展的 128 維版本需要在 create 時設(shè)置 extended 參數(shù)。我一開始想當然用了 128 維以為描述子越長匹配越準實際測試后發(fā)現(xiàn) 64 維在表計圖像上已經(jīng)足夠而且 128 維占用內(nèi)存翻倍匹配速度慢不少。特征點的區(qū)分度取決于表計紋理的獨特性而不是盲目增加描述子長度。還有一個小細節(jié)如果輸入圖像是彩色圖detectAndCompute 默認會先轉(zhuǎn)成灰度再處理。不過為了控制變量我習慣在調(diào)用前手動 cvtColor把原圖統(tǒng)一轉(zhuǎn)灰度再做一次高斯濾波降噪這樣可以避免不同色彩空間帶來的匹配結(jié)果不穩(wěn)定。特別是表計拍攝環(huán)境中常有 LED 顯示屏或強反光彩色信息反而會引入額外的光照干擾。4. 特征匹配策略KNN、距離比例篩選與交叉驗證4.1 為什么不用簡單的暴力匹配拿到兩組特征描述子之后第一步是把模板圖中的每個特征點到查詢圖中找最相似的特征點。最直觀的方法是暴力枚舉也就是 Brute-Force Matching對每個模板特征點逐一計算和所有查詢特征點的歐氏距離取最小距離對應的點作為匹配點。這種做法在特征點數(shù)量少、圖像內(nèi)容簡單時沒問題但在表計圖像上畫面里的刻度線、數(shù)字、條碼區(qū)域非常密集很多局部紋理看起來幾乎一樣暴力匹配很容易給某個特征點找到一個錯誤但距離偏小的假匹配。直接拿這種匹配結(jié)果去做單應性估計RANSAC 雖然能剔除一部分外點但外點比例太高時RANSAC 的內(nèi)點率算不出來模型直接崩掉。所以標準做法是先做 KNN 匹配取 k2。也就是對每個模板特征點找到查詢圖里距離最近的前兩個候選點然后比較最近距離和次近距離的比值。如果最近距離明顯小于次近距離說明這個匹配是獨一無二的可信度高如果兩者差不多說明該特征點在查詢圖里有多個相似區(qū)域匹配結(jié)果不可靠直接丟掉。這個思路用生活化的話說就是如果一個人在一群人中只有一個長得很像的朋友那他很容易被認出來如果他在人群里有一堆孿生兄弟那你認出他的把握就要打折扣。4.2 距離比例系數(shù)的選擇與實現(xiàn)OpenCV 里實現(xiàn) KNN 匹配和比例篩選的代碼如下cv::BFMatcher matcher(cv::NORM_L2); std::vectorstd::vectorcv::DMatch knn_matches; matcher.knnMatch(descriptors_tpl, descriptors_query, knn_matches, 2); const float ratio_thresh 0.8f; std::vectorcv::DMatch good_matches; for (size_t i 0; i knn_matches.size(); i) { if (knn_matches[i][0].distance ratio_thresh * knn_matches[i][1].distance) { good_matches.push_back(knn_matches[i][0]); } }距離比例系數(shù) ratio_thresh 是 Lowe 在 SIFT 論文里提出的方法經(jīng)典取值為 0.8。意思是最近距離必須小于次近距離的 80%才被認為是有效匹配。這個值在 SIFT 上表現(xiàn)好換到 SURF 上也大致適用。我實測在表計圖像上0.8 的篩選力度比較適中大約能濾掉 40%~60% 的不可靠匹配剩下的匹配對正確率明顯提高。如果你發(fā)現(xiàn) good_matches 數(shù)量太多可以調(diào)小 ratio_thresh 到 0.7匹配對數(shù)會顯著減少但每個匹配的置信度更高。反之如果 good_matches 太少說明圖像差異大可以放寬到 0.9。注意這個參數(shù)不能無腦往 1.0 靠否則等于退化成直接取最近鄰誤匹配會大量涌入。4.3 交叉驗證我推薦額外加的篩選層只用比例篩選還不夠。有一次我在一個反光嚴重的表計圖像上測試比例篩選后 good_matches 有 80 多對看起來一切正常但后續(xù) RANSAC 的 inlier 比例只有 40%明顯不對。后來查了一下發(fā)現(xiàn)相當一部分錯誤匹配呈現(xiàn)出對稱性錯誤——模板圖的 A 點在查詢圖里匹配到 B 點而查詢圖的 B 點在模板圖里匹配到的卻是 C 點雙方不是互認的。最簡單的解決辦法是加交叉驗證也就是正向匹配和反向匹配同時進行從模板到查詢做一次匹配再從查詢到模板做一次匹配只保留兩個方向都認為對方是最優(yōu)匹配的特征點對。這個策略代碼上要增加一次 knnMatch 調(diào)用和一組集合判斷但效果立竿見影。我加上交叉驗證后后續(xù) RANSAC 的 inlier 比例普遍能從 50% 提升到 75% 以上。4.4 均勻化特征點分布防止局部區(qū)域主導匹配還有一個比較隱蔽的問題。表計圖像的左上角如果有一塊密密麻麻的條碼區(qū)特征點會大量集中在這個區(qū)域?qū)е缕ヅ鋵σ苍言谶@里單應性矩陣被這些局部點牽著鼻子走而表盤其他區(qū)域的特征點幾乎沒有參與計算。這樣求出來的變換矩陣對整體圖像不友好可能出現(xiàn)表盤中心區(qū)域校正得還行但邊緣區(qū)域扭曲明顯。針對這個問題我寫了一個簡單的均勻化邏輯把圖像劃分為 4x4 網(wǎng)格對每個網(wǎng)格內(nèi)返回的 good_matches 數(shù)量設(shè)上限。超過上限的網(wǎng)格只保留距離比例最小的前 N 個匹配這樣保證每個區(qū)域的匹配點都有貢獻。這個操作對提高單應性矩陣的全局魯棒性幫助很大尤其是當拍攝角度比較大的時候強烈建議加上。5. 單應性矩陣求解與透視變換理論到代碼的銜接5.1 單應性矩陣是什么為什么它能完成校正先解釋基礎(chǔ)概念。單應性矩陣Homography Matrix是一個 3x3 矩陣描述的是同一個平面在兩個不同視角下的投影映射關(guān)系。表計的表盤是一個近似平面所以從任意角度拍攝到的表計圖像和標準模板圖之間可以由一個單應性矩陣完成映射。數(shù)學上假設(shè)模板圖上的一個點為 (u, v)查詢圖上對應點為 (x, y)那么映射關(guān)系為[ s \cdot \begin{bmatrix} x \ y \ 1 \end{bmatrix} H \cdot \begin{bmatrix} u \ v \ 1 \end{bmatrix} ]其中 s 是尺度因子。展開后H 矩陣的 8 個自由度足以描述旋轉(zhuǎn)、縮放、平移和透視變形。OpenCV 的 findHomography 函數(shù)就是從一組匹配點對中求解這 8 個參數(shù)。最少需要 4 對匹配點即可求解但實際使用時匹配點對中難免有錯誤點所以必須配合 RANSAC 或 LMEDS 這類魯棒估計算法來剔除外點。5.2 findHomography 的參數(shù)選擇RANSAC 還是 LMEDSOpenCV 的 findHomography 提供了幾個計算方法其中實際項目里常用的是 RANSAC 和 LMEDS。兩者的區(qū)別在于對錯誤數(shù)據(jù)外點的容忍方式和計算效率。RANSAC 算法的思路是隨機抽取 4 對匹配點計算一個候選 H然后用這個 H 去測試所有匹配點統(tǒng)計滿足誤差閾值的點內(nèi)點數(shù)量反復迭代多次取內(nèi)點數(shù)量最多的那個 H。LMEDS 則是最小化內(nèi)點的中位誤差它在數(shù)據(jù)包含較多外點時也能有不錯的估計但計算量更大而且對誤差閾值的設(shè)置不像 RANSAC 那么直觀。我在項目里使用 RANSAC并設(shè)置了兩個關(guān)鍵參數(shù)閾值 ransacReprojThreshold默認取 3.0單位是像素。這個值表示某匹配點經(jīng)過 H 變換后的位置和實際位置的距離小于該值時視為內(nèi)點。值越小篩選越嚴格。最大迭代次數(shù)OpenCV 默認是 2000 次一般夠用。特征點數(shù)量很大時可以適當降低減少耗時。代碼實現(xiàn)如下cv::Mat H cv::findHomography(points_tpl, points_query, cv::RANSAC, 3.0);這里要特別留意輸入點的類型。points_tpl 和 points_query 必須是 std::vector cv::Point2f 而且順序要一一對應即 points_tpl[i] 和 points_query[i] 是同一條匹配對的兩個端點。我之前犯過一個低級錯誤把模板點和查詢點搞反了結(jié)果 H 矩陣求出來是反的透視變換后的圖像直接被翻轉(zhuǎn)排查了很久才發(fā)現(xiàn)問題。5.3 從 DMatch 提取點坐標的正確姿勢KNN 匹配得到的是 DMatch 結(jié)構(gòu)體數(shù)組每個 DMatch 包含 queryIdx查詢圖特征點索引、trainIdx模板圖特征點索引和 distance。注意這里的query和train指的是調(diào)用 knnMatch 時兩個描述子的傳入順序而不是圖像語義上的查詢圖或模板圖。我的代碼里以模板圖作為第一個參數(shù)所以模板圖的特征點對應 trainIdx查詢圖的特征點對應 queryIdx。提取坐標時一定要搞清楚這個對應關(guān)系否則又是一處隱性翻車點。std::vectorcv::Point2f points_tpl, points_query; for (const cv::DMatch m : good_matches) { points_tpl.push_back(keypoints_tpl[m.trainIdx].pt); points_query.push_back(keypoints_query[m.queryIdx].pt); }5.4 透視變換與圖像尺寸的決策求解得到 H 矩陣之后用 warpPerspective 函數(shù)完成變換cv::Mat correctedImg; cv::warpPerspective(queryImg, correctedImg, H, templateImg.size());這里的關(guān)鍵是輸出尺寸的選擇。我直接把 templateImg.size() 作為輸出尺寸這樣校正后的圖像和模板圖在空間上一一對應后續(xù)如果要做模板匹配或者區(qū)域比對坐標可以直接復用。相比之下如果輸出尺寸設(shè)成 queryImg.size()校正后的圖像雖然變正了但尺寸還是原圖的坐標體系和模板不同后續(xù)操作要多一層坐標換算沒必要。warpPerspective 的插值方式默認是 INTER_LINEAR對于表計讀數(shù)識別場景線性插值夠用。如果你的表計圖像分辨率很高放大時想保留更清晰的邊緣可以試試 INTER_CUBIC但速度會略慢。邊界模式一般默認 BORDER_CONSTANT填充黑色方便觀察校正后的圖像區(qū)域。5.5 校正效果自檢怎么判斷變換矩陣是否可信拿到 H 之后不要急著往下走先做一個簡單的可視化自檢把 good_matches 中的匹配點在模板圖、查詢圖上畫出來同時把 transform 后的圖顯示出來肉眼觀察表計是否轉(zhuǎn)正、刻度方向是否水平、長寬比例是否自然。這一步雖然土但非常有效。另外推薦一個數(shù)值化的校驗方法計算 H 矩陣的條件數(shù)或者檢查 H 矩陣中某些奇異值是否過大。如果 H 矩陣數(shù)值不穩(wěn)定說明求解過程出現(xiàn)了退化情況這時候即使圖像看起來好像轉(zhuǎn)正了也可能是巧合后續(xù)識別步驟很容易出錯。更嚴格的做法是隨機抽取一部分匹配點做交叉驗證看穩(wěn)定匹配比例是否超過 60%。6. 實戰(zhàn)中的問題排查記錄與調(diào)參技巧6.1 真實場景一表計反光導致特征點丟失第一個讓我頭疼的場景是夜間或強光下的反光表計。這類表計的玻璃面板會反射出環(huán)境光源形成一個亮斑區(qū)SURF 在亮斑附近提取的特征點質(zhì)量很差而且方向描述子容易受到高光干擾。我的解決方案是在做特征提取前先對圖像進行一次自適應直方圖均衡化CLAHE把局部對比度增強同時限制光照不均勻造成的梯度偏移。這個預處理只影響特征點提取不對原圖做永久修改。實測下來匹配對數(shù)從 30 對提高到 70 多對效果好得明顯。6.2 真實場景二鏡頭畸變導致邊緣匹配錯位某些廣角攝像頭存在明顯的桶形畸變靠近畫面邊緣的表計會發(fā)生輕微的彎曲變形。SURF 特征點不受這種低頻形變的影響太大但單應性矩陣只能描述平面投影變換不能描述非線性畸變導致邊緣部分的匹配殘差較大。這種情況我建議在特征匹配前先調(diào)用 getOptimalNewCameraMatrix 和 undistort 對圖像做畸變校正或者在采集端直接使用長焦鏡頭減少廣角畸變。后一種方式往往更省事成本也低。6.3 真實場景三匹配對數(shù)不足怎么兜底無論怎么優(yōu)化總會遇到某些表計圖像因為遮擋或極端角度導致匹配對數(shù)不足 10 對。這時候強行求單應性矩陣是危險的我采用了一個分級降級策略先嘗試 SURF RANSAC。匹配對數(shù)不足時改用 ORB 特征再做一次匹配ORB 對光照變化不如 SURF但在一些紋理簡單的表計上反而更好使。如果還是不足就退化為邊緣直線檢測通過找表盤矩形邊緣估算旋轉(zhuǎn)角做簡單的仿射校正。這個降級策略保證了流程在絕大多數(shù)情況下不會完全中斷。雖然第三步精度不如特征匹配但至少后續(xù)步驟能拿到一個可用的初值不會因為一個異常圖導致整條識別鏈路卡死。6.4 耗時優(yōu)化實時性不夠怎么辦SURF 的計算量比 ORB 大得多在 1280x960 的圖上特征提取加匹配大約需要 150~300 毫秒具體取決于 CPU 性能。對于離線批量識別場景這個速度可以接受但如果要做實時視頻流需要做兩處優(yōu)化把模板圖的特征點和描述子在程序啟動時預先計算一次后續(xù)每一幀只用檢測查詢圖并匹配省掉模板圖檢測的時間。查詢圖在進入特征提取前先做一次降采樣。比如把長邊降到 640 像素。SURF 對尺度變化不敏感降采樣后的特征點數(shù)量雖然減少但匹配質(zhì)量不會下降太多。實測降采樣到 640 后一幀的處理時間能控制在 80 毫秒左右基本能達到 12 FPS 的準實時效果。6.5 一個容易忽略的小細節(jié)dll 運行環(huán)境最后提一個容易被發(fā)布環(huán)節(jié)坑到的地方。VS2015 編譯的程序默認依賴 vc14 運行庫如果目標機器上沒有安裝對應版本的 Visual C Redistributable程序啟動會直接報程序無法正常啟動。我當時把程序部署到車間工控機上就踩了這個坑工控機上只裝了 VS2013 的運行庫結(jié)果跑不起來。后來在發(fā)布包里附帶了 vc_redist.x64.exe并在部署腳本里自動安裝問題才解決。類似的還有 OpenCV 的 dll 依賴。建議在最終發(fā)布時把用到的 opencv_world3416.dll 和 opencv_xfeatures2d3416.dll 直接復制到 exe 同目錄不要依賴系統(tǒng)的 PATH 環(huán)境變量工控機環(huán)境往往很干凈沒有任何配好的環(huán)境變量。7. 模板匹配之外校正后的圖像還能做什么圖像校正完成后后續(xù)的讀數(shù)識別步驟就變得非常簡單。由于校正后的圖像與模板圖尺寸一致、視角一致你可以直接按模板圖里手工標注好的數(shù)字區(qū)域坐標去裁剪字符然后用模板匹配、OCR 或者輕量級 CNN 做數(shù)字分類。這一整套流程下來識別的穩(wěn)定性比直接拿傾斜圖硬識別高出不少。我個人在實際項目中的體會是校正這一環(huán)在整個表計讀數(shù)識別系統(tǒng)里的重要性被很多人低估了。不少團隊一上來就搞深度學習模型指望網(wǎng)絡(luò)能自動學到旋轉(zhuǎn)不變性但工程上的魯棒性往往來自前端幾何校正的扎實程度。數(shù)據(jù)增強雖然能在訓練時引入各種角度但推理時如果輸入圖像角度過偏模型的表現(xiàn)仍然不理想而幾何校正正好把這個問題從源頭解決掉。另外SURF 校正方案對模板圖的依賴比較強如果實際場景中表計型號很多每種型號都需要準備一張模板圖。建議把模板圖特征點離線緩存成二進制文件運行時按型號加載避免每次啟動都重新提取特征。這個緩存文件通常只有幾 MB加載速度在毫秒級幾乎不影響啟動時間。如果你在項目里也打算走表計讀數(shù)識別這條路先別急著上高端模型把 SURF 校正這塊打磨扎實后面每一步都會省心很多。