:從圖像處理到金額自動計算)
簡介這是一份基于Python與OpenCV的硬幣檢測與識別項目資源面向計算機視覺初學者和OpenCV實戰(zhàn)學習者可解決照片中硬幣的自動計數(shù)與總價值估算問題適合用來入門圖像處理或完成課程設計。壓縮包共13個文件由12張jpg圖像和1個py腳本組成整體約7.39MB其中圖像部分包括原始照片、模板圖像、測試圖像和檢測結(jié)果圖py腳本則是完整的檢測識別程序便于直接運行和對照調(diào)試。目前已有2242人學習下載實踐參考價值較高尤其適合需要在實驗中快速驗證算法的開發(fā)者和學生。通過學習讀者可以掌握圖像預處理、邊緣檢測、圓形目標定位、特征匹配與小物體計數(shù)等常用技術路線理解從圖像輸入到價值判斷的完整流程也能基于這套代碼進一步擴展用于其他圓形物體識別場景。 先聊點實際的。我當初做這個硬幣檢測與識別項目起因是幫朋友寫一個自動數(shù)零錢的小工具——就是拍一張照片程序自己告訴我里面有多少錢。聽起來挺簡單對吧不就是找圓、看大小、算面值嘛。真動手之后才發(fā)現(xiàn)找圓這一步就夠喝一壺的反光、陰影、硬幣挨得太近、背景亂七八糟全是坑。而看大小算面值還得先解決像素和真實尺寸之間怎么換算的問題。這篇文章就把我完整跑通的思路、代碼和踩坑記錄都攤開講給正在做 python-opencv 硬幣檢測識別的同學一個可以直接參考的路徑。先說清楚一件事檢測和識別是兩個環(huán)節(jié)。檢測負責定位告訴你圖像里第幾個位置有一枚硬幣半徑是多少像素識別負責分類告訴你這枚硬幣是1元、5角還是1角。兩個環(huán)節(jié)的錯誤是會疊加的圓沒找準后面面值必錯圓找準了但分類邏輯太糙照樣數(shù)錯錢。所以每一步都得穩(wěn)。1. 先拆開找硬幣和認面值這個項目的第一道坎1.1 檢測和識別是兩件事很多人一上來就搜硬幣識別然后指望一個函數(shù)把結(jié)果全吐出來這其實是把兩個問題攪在一起了。檢測環(huán)節(jié)說白了就是目標定位。OpenCV 里典型的做法有兩種一是用 HoughCircles 做圓形檢測二是用 findContours 找輪廓再擬合圓。我不太建議一上來就上深度學習原因很簡單硬幣是高度規(guī)矩的圓形物體傳統(tǒng)圖像處理在受控環(huán)境下完全夠用而且可解釋性強出了問題你能看到是邊緣斷了還是閾值錯了深度模型給不了這種可調(diào)試性。識別環(huán)節(jié)則是另一套邏輯。硬幣的面值判斷依據(jù)有三個維度按可靠程度排序物理尺寸、顏色、表面圖案/文字。物理尺寸最可靠因為人民幣硬幣的直徑是官方固定的只要知道像素和真實尺寸的換算比例量一下半徑就能確定面值范圍。顏色是第二可靠的維度5角是金黃色1元和大部分1角是銀白色。最不可靠的是圖案識別——硬幣表面磨損、反光、角度變化都會嚴重影響模板匹配或OCR的效果所以我把圖案識別放在了最后的擴展方案里而不是主方案。1.2 識別依據(jù)尺寸、顏色、圖案的優(yōu)先級拿我國目前常見的硬幣來說直徑參數(shù)大致是面值直徑材質(zhì)顏色備注1元25.0mm鋼芯鍍鎳銀白色新版硬幣5角20.5mm鋼芯鍍銅合金金黃色新版硬幣1角19.0mm不銹鋼銀白色2019年新版注意這里有個容易踩的坑1角和5角的直徑只差1.5mm在普通分辨率圖像里可能只差十個像素左右單純靠半徑閾值很容易混淆所以我后面增加了一道顏色校驗邏輯專門區(qū)分這兩種面值。把識別依據(jù)排個優(yōu)先級先看尺寸尺寸落在模糊區(qū)間的再用顏色兜底。圖案文字識別這種重方案只有在硬幣版本特別多、或者必須識別年份版別的需求出現(xiàn)時才值得上。1.3 傳統(tǒng)CV還是深度學習我為什么選了前者我當時考慮過兩套方案一套是本文講的傳統(tǒng)方法另一套是用 YOLO 之類的目標檢測模型直接檢測并分類硬幣。選傳統(tǒng)方法的核心原因是工程量匹配。我需要處理的場景非常固定硬幣平鋪在深色底板上相機固定在正上方固定高度拍攝。在這個前提下硬幣就是一個標準圓尺寸是已知的顏色是明確的傳統(tǒng)CV可以做到又快又穩(wěn)。深度學習的優(yōu)勢在復雜場景——硬幣隨意堆疊、任意姿態(tài)、光照劇烈變化、背景雜亂——那種場景下 YOLO 的魯棒性確實碾壓傳統(tǒng)方法。但代價是你需要標注幾百張圖片、訓練模型、調(diào)參數(shù)還要面對新的誤檢方式。對一個數(shù)零錢的需求來說性價比太低了。如果你的場景不再滿足平鋪、正拍、背景可控這幾個條件別硬用傳統(tǒng)方法換檢測模型是更明智的選擇。這是后話我放到最后一節(jié)展開。2. 拍攝環(huán)境沒你想的那么隨便背景、光源和鏡頭位置2.1 環(huán)境依賴安裝這部分最簡單直接在 Python 環(huán)境里裝 opencv-python 和 numpy 就行pip install opencv-python numpy如果需要讀取中文路徑下的圖片建議加裝 opencv-contrib-python 并配合 imdecode 讀取這個坑我后面細說。版本方面我用的是 OpenCV 4.xAPI 和 3.x 差別不大老代碼基本能直接跑。2.2 用底板和光源把變量鎖死我第一版代碼是在辦公桌上隨手拍的結(jié)果硬幣和木質(zhì)桌面的紋理混在一起Canny 邊緣檢測出來的全是木紋線Hough 找圓找出來一堆亂七八糟的結(jié)果。后來我找了個深色的純色鼠標墊當?shù)装逭`檢率立刻大幅下降。這里有個原則圖像處理項目里能通過控制拍攝環(huán)境解決的問題不要留給算法硬扛。底板的顏色最好和硬幣顏色有明顯區(qū)分。深色底配銀色硬幣效果最好因為銀白色硬幣在暗背景下邊緣對比度極高。如果你要重點處理金色5角深藍或黑色的背景也比淺色背景更合適。光源方面均勻光比強光更重要。我當時用了一盞普通臺燈從側(cè)上方照明結(jié)果每枚硬幣靠近光源的一半亮、另一半有陰影Canny 只能檢出半圈邊緣圓檢測全都偏了。后來我把光源改成漫反射環(huán)境光散射光均勻照明或者在拍攝時盡量讓房間整體明亮、避免單一強光源直射效果就好了很多。2.3 相機鏡頭要垂直于桌面鏡頭的拍攝角度會影響硬幣成像形狀。硬幣本身是圓形的但如果相機沒有正對桌面圓會變成橢圓HoughCircles 的半徑檢測就會飄面值判斷自然出錯。我的建議是鏡頭光軸盡量垂直于桌面固定高度拍攝。如果實在做不到完全垂直那就保持固定角度然后用一個已知尺寸的標定物做換算——標定物能幫你直接把像素和實際毫米數(shù)的比例定死拍攝角度造成的系統(tǒng)誤差在換算后會穩(wěn)定在一個固定范圍內(nèi)反而不影響分類。3. 從噪聲到干凈邊緣預處理與候選圓提取3.1 灰度化、濾波與光照均衡預處理的目標很純粹讓硬幣的邊緣盡量干凈地被檢測出來同時讓背景紋理的干擾降到最低。第一步是把 BGR 圖像轉(zhuǎn)成灰度圖。第二步是濾波。我實測下來高斯濾波核大小取 (9, 9)sigma 取 2對硬幣這類大目標來說去噪效果和邊緣保留之間的平衡最好。中值濾波也可以它對椒鹽噪聲更有效但邊緣會偏鈍一點看你的圖像情況。如果你拍攝的光照不均勻可以加一步光照均衡處理。當時我的照片左側(cè)亮右側(cè)暗硬幣邊緣在暗側(cè)幾乎斷了。解決辦法是用形態(tài)學的黑帽/頂帽變換分離出光照分量或者直接對灰度圖做高斯模糊后與原圖相減。OpenCV 里最簡單粗暴的寫法blurred cv2.GaussianBlur(gray, (31, 31), 0) equalized cv2.subtract(gray, blurred)這行代碼的效果是把緩慢變化的光照背景去掉只保留局部變化的高頻信息硬幣邊緣的對比度會被拉回來。很實用尤其是在環(huán)境光不受控的場合。3.2 Canny邊緣檢測的參數(shù)門道Canny 的閾值是新手最容易卡住的地方。它有兩個閾值高閾值和低閾值。高于高閾值的像素一定被當成邊緣低于低閾值的肯定丟棄介于兩者之間的只有與強邊緣相連才會被保留。我的經(jīng)驗是高閾值和低閾值的比例放在 1:2 到 1:3 之間比較合適。比如高閾值 100、低閾值 50或者 150、60。硬幣場景下我常用(100, 200)這個組合邊緣干凈、斷點少。閾值設太低木紋、陰影邊緣全進來Hough 投票就亂了閾值設太高硬幣邊緣斷成碎片圓檢測可能直接失敗。參數(shù)的直覺不是調(diào)大或調(diào)小而是想清楚你當前圖像的問題是哪一種再決定往哪邊調(diào)。3.3 HoughCircles vs 輪廓擬合這是硬幣定位的核心環(huán)節(jié)我兩種方法都寫了因為實際使用中它們互補。方案一HoughCirclescircles cv2.HoughCircles( gray_gaussian, cv2.HOUGH_GRADIENT, dp1.2, minDist50, param1100, param260, minRadius20, maxRadius60 )HoughCircles 的核心思想是每個邊緣點都可以在所有候選圓心位置投票投票數(shù)大于閾值的圓心和半徑組合才會被保留。所以dp1.2累加器分辨率一般取 1~2 即可越大越快但可能丟小圓。minDist兩個圓心之間的最小距離。如果比硬幣直徑的像素值還小同一枚硬幣會被檢出好幾個重疊圓。這個參數(shù)是我踩坑最多的后面細講。param1內(nèi)部 Canny 的高閾值。注意 HoughCircles 內(nèi)部會自己做一次 Canny所以如果你在外部已經(jīng)做過預處理這里的高閾值不用太小我的經(jīng)驗是 100 左右起步。param2累加器投票閾值。越小檢出的圓越多誤檢也越多越大越嚴格但可能漏掉邊緣不太完美的硬幣。60 到 80 是比較常用的區(qū)間。minRadius/maxRadius候選圓的半徑范圍。一定要盡量量準。我在代碼里加了一個預處理步驟先用輪廓分析預估硬幣的像素半徑再把這個范圍喂給 HoughCircles誤檢率直接下降一大截。方案二輪廓分析 圓形度校驗_, binary cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV cv2.THRESH_OTSU) contours, _ cv2.findContours(binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) for cnt in contours: area cv2.contourArea(cnt) if area 500: continue perimeter cv2.arcLength(cnt, True) circularity 4 * np.pi * area / (perimeter * perimeter) if circularity 0.8: continue (x, y), radius cv2.minEnclosingCircle(cnt)輪廓法的核心是先用 OTSU 自動閾值把圖像轉(zhuǎn)成二值圖然后找外輪廓用圓形度公式過濾掉非圓形狀。圓形度的定義是4π × 面積 / 周長2完美圓的圓形度是 1一般大于 0.8 的都可以認為接近圓。這兩個方案怎么選如果硬幣之間離得比較開、圖像干凈HoughCircles 效率高如果硬幣有輕微遮擋、邊緣有缺失或者背景中有大量非圓物體輪廓法更穩(wěn)。我最終的項目里是兩套都跑Hough 結(jié)果優(yōu)先輪廓法做兜底兩個結(jié)果用圓心坐標合并去重。4. 用一枚1元硬幣標定像素比再把半徑換算成面值4.1 標定像素是怎么變成毫米的要判斷面值光知道半徑的像素值沒用因為照片的像素量和拍攝距離直接相關。我一開始犯過這個錯直接在測試圖上量出 1 元硬幣半徑大約 30 像素就拿半徑 30 以上是 1 元去套所有照片換一個拍攝距離全變了。正確做法是做一個標定在拍攝畫面里放一枚已知尺寸的硬幣我用 1 元直徑 25mm檢測到它的像素直徑后算出比例scale ref_diameter_px / 25.0 # 單位像素/毫米把每枚硬幣的像素直徑除以 scale就得到真實直徑毫米數(shù)。后面只要拍攝條件不變這個比例就不用重新算。標定有兩種思路第一種是把參考硬幣放在畫面內(nèi)自動檢測并計算 scale第二種是拍一張空白背景帶標定物的圖預先測好 scale 寫死在配置里。我用的第二種——因為我的相機是固定安裝的拍一次標定圖就夠了把 scale 存成配置文件后續(xù)所有圖片共用同一個比例邏輯更簡單。如果你的拍攝距離每次都會變那就必須走第一種讓標定邏輯隨圖片走。4.2 直徑閾值表與分類邏輯拿到毫米直徑后分類邏輯就清晰了。我實測的直徑分布大概是真實面值實測直徑范圍mm中間閾值1角18.5 ~ 19.519.85角20.0 ~ 21.021.51元24.5 ~ 25.523.0注意一個問題1角19mm和 5角20.5mm只差 1.5mm如果標定稍微偏一點或者硬幣因為拍攝角度有一點形變這兩個就會互相混淆。所以我的邏輯里做了一個雙閾值區(qū)間設計直徑 23mm → 判為 1元直徑在 21.0 ~ 23mm 之間 → 判為 5角直徑在 19.8 ~ 21.0mm 之間 → 候選5角/1角交給顏色判斷直徑 19.8mm → 判為 1角這個設計比單純一個閾值穩(wěn)得多。落在中間模糊帶的硬幣再去提取它的顏色特征做二次判斷。4.3 用顏色信息兜底金色占比判斷5角硬幣是金黃色鋼芯鍍銅合金1角硬幣是銀白色不銹鋼在 HSV 空間里差異明顯。我之前在 BGR 空間直接判斷顏色吃了不少苦頭因為光照一變金色在 BGR 三通道的比值就亂了。換成 HSV 空間后用色調(diào) H 通道就能輕松區(qū)分。判斷邏輯是把檢測到的圓區(qū)域裁剪出來轉(zhuǎn) HSV 空間統(tǒng)計 H 值在 10~35 之間、且飽和度 S 比較高大于 60的像素占比。如果金色像素在圓區(qū)域內(nèi)占比超過 10%就判為 5角否則判為 1角。hsv cv2.cvtColor(img, cv2.COLOR_BGR2HSV) mask_gold cv2.inRange(hsv, (10, 60, 60), (35, 255, 255)) gold_ratio cv2.countNonZero(mask_gold) / (np.pi * radius * radius)這個邏輯在大多數(shù)室內(nèi)光線下都穩(wěn)定。唯一的坑是硬幣反光會產(chǎn)生高光白斑但白斑的飽和度低不會被算進金色像素里所以實際影響不大。反過來如果環(huán)境光偏暖銀白色硬幣也可能被照出一點淡金色所以我只把這個判斷放在直徑模糊帶不作為第一判斷依據(jù)——這是不少實現(xiàn)容易搞錯的地方把顏色當成主判斷然后被一個偏黃的燈光坑到懷疑人生。5. 可直接復用的完整代碼讀圖、找圓、分類、算總額下面是我最終落地版本的簡化代碼保留了完整的流程圖預處理、候選圓提取、圓形度校驗、標定、面值分類、總額累加。你可以直接抄去跑。import cv2 import numpy as np # ---------- 全局配置 ---------- SCALE_FILE scale.txt # 標定結(jié)果文件單位像素/毫米 REF_DIAMETER_MM 25.0 # 標定硬幣直徑1元硬幣25mm def load_scale(): with open(SCALE_FILE, r) as f: return float(f.read().strip()) def save_scale(scale): with open(SCALE_FILE, w) as f: f.write(str(scale)) def read_image(path): # 用imdecode讀取支持中文路徑 stream np.fromfile(path, dtypenp.uint8) return cv2.imdecode(stream, cv2.IMREAD_COLOR) def detect_coins(image, gray, scale): # 方案一HoughCircles 檢測 circles None if scale is not None: min_r int(19.0 * scale * 0.85) max_r int(25.0 * scale * 1.15) circles cv2.HoughCircles( gray, cv2.HOUGH_GRADIENT, dp1.2, minDistint(25.0 * scale * 1.2), param1100, param260, minRadiusmin_r, maxRadiusmax_r ) detections [] if circles is not None: circles np.round(circles[0]).astype(int) for (x, y, r) in circles: detections.append((x, y, r, hough)) # 方案二輪廓分析 圓形度過濾 blurred cv2.GaussianBlur(gray, (9, 9), 2) _, binary cv2.threshold(blurred, 0, 255, cv2.THRESH_BINARY_INV cv2.THRESH_OTSU) contours, _ cv2.findContours(binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) for cnt in contours: area cv2.contourArea(cnt) if area 1000: continue perimeter cv2.arcLength(cnt, True) if perimeter 0: continue circularity 4 * np.pi * area / (perimeter * perimeter) if circularity 0.8: continue (x, y), r cv2.minEnclosingCircle(cnt) detections.append((int(x), int(y), int(r), contour)) # 去重合并兩種方案檢測到的同一個圓 result [] for d in detections: x, y, r, source d duplicate False for rx, ry, rr, rs in result: if (x - rx) ** 2 (y - ry) ** 2 (max(r, rr) * 0.5) ** 2: duplicate True break if not duplicate: result.append(d) return result def classify_coin(img, x, y, r, scale): # 計算直徑像素 diameter_px 2 * r diameter_mm diameter_px / scale # 裁剪圓區(qū)域用于顏色判斷 mask np.zeros(img.shape[:2], dtypenp.uint8) cv2.circle(mask, (x, y), r, 255, -1) hsv cv2.cvtColor(img, cv2.COLOR_BGR2HSV) gold_mask cv2.inRange(hsv, (10, 60, 60), (35, 255, 255)) gold_ratio cv2.countNonZero(cv2.bitwise_and(gold_mask, gold_mask, maskmask)) / (np.pi * r * r) # 面值判斷直徑優(yōu)先顏色兜底 if diameter_mm 23.0: return 1.0 elif diameter_mm 21.0: return 0.5 elif diameter_mm 19.8: return 0.5 if gold_ratio 0.1 else 0.1 else: return 0.1 def main(): img read_image(coins.jpg) if img is None: print(圖片讀取失敗) return gray cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # 標定流程第一次運行時用1元硬幣標定 scale None try: scale load_scale() except FileNotFoundError: print(未找到標定文件請在畫面正中放置一枚1元硬幣3秒后自動標定...) cv2.imshow(calibration, img) cv2.waitKey(3000) # 這里簡化處理手動用Hough檢測畫面中1元硬幣直徑 # 實際場景可以增加人工確認邏輯 ref_circles cv2.HoughCircles(gray, cv2.HOUGH_GRADIENT, dp1.2, minDist50, param1100, param260, minRadius30, maxRadius80) if ref_circles is not None: _, _, ref_r np.round(ref_circles[0][0]).astype(int) scale (2 * ref_r) / REF_DIAMETER_MM save_scale(scale) print(f標定完成scale{scale:.3f} 像素/毫米) else: print(標定失敗未檢測到參考硬幣) return coins detect_coins(img, gray, scale) total 0.0 for (x, y, r, source) in coins: value classify_coin(img, x, y, r, scale) total value label {1.0: 1元, 0.5: 5角, 0.1: 1角}[value] cv2.circle(img, (x, y), r, (0, 255, 0), 2) cv2.putText(img, f{label}, (x - 20, y - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 0), 2) print(f檢測到 {len(coins)} 枚硬幣總金額{total:.1f} 元) cv2.imshow(result, img) cv2.waitKey(0) if __name__ __main__: main()代碼里的detect_coins同時跑兩種檢測方案再用去重邏輯合并這樣 Hough 的效率和輪廓法的容錯性都能兼顧。分類邏輯就是我上面說的雙閾值加顏色兜底。read_image用 imdecode 是為了兼容中文路徑——很多同學在 Windows 上圖片路徑帶中文直接用 cv2.imread 會返回 None這個問題排查起來非常隱蔽。6. 我踩過的四個坑重復圓、斷邊、誤檢與面值混淆6.1 同一枚硬幣被重復檢測出多個圓這個問題幾乎每個用 HoughCircles 的人都會遇到。表現(xiàn)是一枚硬幣上疊著兩三個半徑相近的圓圓心稍微偏移幾像素。原因是minDist設得太小了小于硬幣的實際直徑像素。排查方法很簡單把檢測到的所有圓都畫出來看一眼。修復方法是把minDist調(diào)到硬幣直徑像素的 1.2~1.5 倍。如果你不確定硬幣直徑像素是多少先跑一次輪廓分析估算一個平均半徑再去設 minDist。我代碼里用int(25.0 * scale * 1.2)就是為了讓這個參數(shù)自動跟著標定結(jié)果走省得每次換拍攝距離都要重調(diào)。6.2 邊緣斷成兩截導致圓檢測失敗硬幣迎光面和背光面的邊緣灰度差異很大Canny 只會把對比度夠高的部分判成邊緣于是出現(xiàn)半圓邊緣。Hough 在這種輸入下會漏檢或者檢出一個偏小的圓。我最終的修復方案是組合拳首先用高斯模糊去掉細小噪聲然后用頂帽變換/減背景光照的做法拉平光照最后把 Canny 的閾值從 (50, 150) 調(diào)到 (100, 200)——提高低閾值會減少微小邊緣的干擾讓主要邊緣更突出。如果你發(fā)現(xiàn)硬幣邊緣本身太弱調(diào)高 Canny 低閾值反而會斷得更厲害這時先檢查光照是不是要修而不是繼續(xù)加濾波。6.3 背景紋理被誤檢成圓辦公室桌面、報紙、木紋板——這些表面都有大量類圓紋理Hough 會把它們當成硬幣。我的解決順序是換深色純色底板從源頭消滅背景紋理。用圓形度過濾非圓輪廓直接丟棄。限制候選圓半徑范圍木紋里那些半圓紋理的半徑往往和真實硬幣半徑差很多一限制就過濾掉了。這三步做完誤檢率降到了幾乎為零。核心思路是你先用物理知識把搜索空間縮到最小剩下的交給算法。6.4 5角和1角直徑相近分類一直在邊緣橫跳這個問題在前期最折磨人。1角直徑 19mm5角直徑 20.5mm差距本來就小如果標定稍有偏差閾值 19.8mm 附近的硬幣就會反復橫跳。我最后的處理就是前面說的設置一個 19.8~21.0mm 的模糊區(qū)間區(qū)間內(nèi)的硬幣用金色像素占比來判斷。金色占比超過 10% 判 5角否則判 1角。在自然室內(nèi)光下這套邏輯很穩(wěn)。有一次在暖黃燈光下銀白色的 1 角被照出了明顯金色調(diào)金色占比超過了 10%被誤判成 5角。后來我加了一個條件檢測金色區(qū)域內(nèi)像素的平均飽和度是否夠高——反光引起的假金飽和度普通偏低而 5角本身的合金顏色飽和度明顯更高。兩種金色從肉眼上你看得出來差別在 HSV 的 S 通道上更是清清楚楚。7. 方案升級思路如果場景變復雜還是得上模型我前面反復強調(diào)傳統(tǒng)方法好用前提是場景受限。如果你的實際需求變成一堆硬幣隨意堆疊在一起從桌面上雜亂環(huán)境下直接識別需要識別出硬幣的年份版別傳統(tǒng)方案的短板就會暴露得很明顯。堆疊硬幣Hough 找不到完整圓輪廓法也會被粘連搞崩潰這時候需要分水嶺算法做粘連分割或者直接用目標檢測模型。復雜背景圓檢測會把大量類圓物體當成候選過濾邏輯越寫越復雜誤差不可控。區(qū)分年份版別尺寸和顏色完全不夠用必須識別正反面的文字圖案。硬幣表面磨損嚴重傳統(tǒng)模板匹配基本不可靠需要用小數(shù)據(jù)集微調(diào)一個 CNN 分類器或者用現(xiàn)成的 OCR 模型識別圖案里的數(shù)字。我的建議是先用本文這套方法把數(shù)據(jù)收集和場景規(guī)范做起來——控制背景、固定機位、準備標定物這些工作無論你最后用不用深度學習都是必須的。等你積累了足夠多的真實圖片再訓練一個輕量目標檢測模型去替換傳統(tǒng)檢測環(huán)節(jié)識別的部分用裁剪出來的硬幣圖像微調(diào)分類模型。這條路走起來平滑很多因為你已經(jīng)對問題有了足夠深的理解知道模型在哪一步幫到你、哪一步反而會引入新的麻煩。最后再分享一個我個人的經(jīng)驗做這類圖像處理項目可視化中間結(jié)果比調(diào)算法參數(shù)更節(jié)省時間。我當時把灰度圖、光照均衡圖、Canny 邊緣圖、Hough 結(jié)果圖、輪廓二值圖全都用 imshow 彈出來看一眼就能定位是預處理的問題、檢測的問題還是分類閾值的問題。如果只看最終結(jié)果調(diào)參你永遠在賭運氣。另一個小技巧是標定文件單獨存一份拍攝距離不變就不需要重新標定但每次跑批量圖片前都先在結(jié)果圖上隨便畫一兩個圓看看半徑是否合理——標定錯了后面全是白算。本文還有配套的精品資源點擊獲取