:從灰度化到輪廓提取的圖像預(yù)處理全流程)
學(xué)習(xí)OpenCV到了第4天正好處在一個很容易讓人迷茫也很容易讓人開竅的節(jié)點。我經(jīng)常被剛?cè)腴T的朋友問到“opencv day_4到底該學(xué)什么”還有人已經(jīng)跟著教程跑完了前3天看完圖像讀取、像素操作、ROI裁剪之后突然不知道下一步該往哪兒走。這篇內(nèi)容就是圍繞 day_4 這個學(xué)習(xí)節(jié)點展開的我會把第4天最核心的主線任務(wù)、關(guān)鍵知識點、實操順序和翻車經(jīng)驗一次性講清楚。它適合兩類人一是剛剛學(xué)完OpenCV基礎(chǔ)、準(zhǔn)備進(jìn)入圖像分析階段的新手二是學(xué)過一遍但總覺得知識是散的、想回頭把“預(yù)處理到輪廓提取”這條線理順的朋友。說句實話第4天學(xué)得好不好直接決定你后面能不能獨立完成像OCR、條碼識別、物體計數(shù)這類實際任務(wù)。1. day_4 該學(xué)什么從像素操作跨到圖像結(jié)構(gòu)理解1.1 為什么第4天是整個學(xué)習(xí)曲線的關(guān)鍵轉(zhuǎn)折點我在帶新人或者復(fù)盤自己學(xué)習(xí)路徑的時候最直觀的感受是第1天到第3天基本屬于“圖像讀寫和像素搬運”反饋非常即時。讀進(jìn)一張圖用 OpenCV 的窗口彈出來BGR通道拆開把某個區(qū)域的像素變成白色你會覺得“我好像會P圖了”。但到了第4天畫風(fēng)突然變了圖像變成了不同程度的灰度圖黑白的邊緣線、二值化的前景背景操作的直觀性下降了不少人在這一階段會懷疑自己是不是學(xué)偏了。其實完全沒有偏。如果你把前3天理解成“修改一個像素矩陣”那么第4天就是“讓程序理解一張圖像里有什么”。灰度化和二值化這些操作本質(zhì)上是把彩色圖像里冗余的顏色信息壓縮掉只保留亮度差異和結(jié)構(gòu)信息算法才能穩(wěn)定地去分析輪廓、位置、甚至后續(xù)的目標(biāo)類別。所以第4天不是知識變難了而是學(xué)習(xí)視角從“改圖”變成了“讀圖”。能不能完成這個視角切換決定你后續(xù)學(xué)特征提取、模板匹配、目標(biāo)檢測時是否順暢。還有一種常見心態(tài)也要提前擺正在第4天你看到的算法效果可能并不如第3天用鼠標(biāo)點個像素那么“驚艷”。邊緣檢測出來的結(jié)果往往是斷斷續(xù)續(xù)的線閾值處理后總有一些噪聲點形態(tài)學(xué)操作稍不注意就把目標(biāo)弄沒了。這些都是正常的因為真實世界的圖像本身就包含光照不均勻、背景干擾、噪聲這些麻煩。第4天學(xué)習(xí)的核心目標(biāo)不是追求完美結(jié)果而是建立一套“預(yù)處理工作流”面對一張不干凈的圖像你知道先在哪個環(huán)節(jié)下手再在哪個環(huán)節(jié)兜底。1.2 第4天的主線任務(wù)拆解如果要把第4天安排得明明白白我會把學(xué)習(xí)內(nèi)容拆成四個模塊按順序推進(jìn)顏色空間轉(zhuǎn)換BGR轉(zhuǎn)灰度、BGR轉(zhuǎn)HSV以及用 inRange 做顏色范圍篩選。濾波去噪高斯模糊、中值濾波為后續(xù)邊緣檢測和閾值處理降低噪聲干擾。閾值分割全局閾值、Otsu自動閾值、自適應(yīng)閾值把灰度圖變成黑白二值圖。邊緣檢測與輪廓提取Canny邊緣檢測、findContours、drawContours把圖像中的物體輪廓圈出來。這四個模塊不是割裂的知識點。它們是一條流水線上的連續(xù)工序彩色圖先轉(zhuǎn)成灰度圖或HSV圖然后模糊去噪再通過閾值或者邊緣檢測把目標(biāo)區(qū)域從背景里分離出來最后找到輪廓并繪制出來。很多教學(xué)文章喜歡把這幾個函數(shù)分開講每個函數(shù)一個示例這樣看起來簡單但恰恰容易讓你學(xué)完就忘。第4天我會建議你把它們串起來做一個完整的“圖像 - 輪廓”小項目哪怕只是一個簡單的“找出圖片里的瓶子”Demo也比單獨跑十個示例函數(shù)有效得多。2. 核心知識點逐項拆解與實操要點2.1 顏色空間轉(zhuǎn)換為什么要先處理“顏色”O(jiān)penCV 讀取彩色圖像時默認(rèn)的通道排列是 BGR不是大多數(shù)圖像處理庫和圖像編輯器使用的 RGB。這個歷史原因很多文章講過我不再展開但你必須形成一個肌肉記憶在 OpenCV 里操作圖像如果不做轉(zhuǎn)換顯示出來顏色會偏藍(lán)偏紅。顏色空間轉(zhuǎn)換最常用的兩個方向是灰度化和 HSV 轉(zhuǎn)換。灰度化我放在后面和閾值一起講這里先重點說灰度化的價值計算機(jī)視覺算法面對三維的 BGR 數(shù)據(jù)時需要處理的信息量是單通道灰度圖的三倍而且很多算法對顏色本身并不敏感。比如一個邊緣檢測算子它關(guān)心的是亮度梯度彩色圖三個通道都要算梯度再合并算法復(fù)雜度上去了結(jié)果卻不一定更好。經(jīng)驗做法是先cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)把問題降維到單通道上去處理。HSV 轉(zhuǎn)換則是另一類場景的標(biāo)配當(dāng)你希望按顏色篩選目標(biāo)時BGR 空間并不符合人對顏色的感知方式。HSV 把顏色拆成色調(diào)、飽和度、亮度三個維度其中色調(diào) H 直接對應(yīng)“這是什么顏色”。比如你想在一塊綠背景里找出紅色的水杯直接對 BGR 三個通道設(shè)范圍非常難受但轉(zhuǎn)成 HSV 之后只需要給 H 通道一個紅色范圍再把 S 和 V 的上下限放開一點就能很好地分割出目標(biāo)。下面是一段典型的 HSV 顏色篩選代碼我建議第4天親手跑一遍并且把上下限改成你隨手找的照片import cv2 import numpy as np img cv2.imread(sample.jpg) hsv cv2.cvtColor(img, cv2.COLOR_BGR2HSV) # HSV 下限與上限針對紅色目標(biāo)可以這樣調(diào)整 lower np.array([0, 100, 100]) upper np.array([10, 255, 255]) mask cv2.inRange(hsv, lower, upper) result cv2.bitwise_and(img, img, maskmask) cv2.imshow(mask, mask) cv2.imshow(result, result) cv2.waitKey(0) cv2.destroyAllWindows()實際調(diào)試時你會發(fā)現(xiàn)光看打印出來的 H 值很難一次找準(zhǔn)范圍。我的習(xí)慣是把 H、S、V 的范圍先放寬比如 H 上下范圍先給 30S 和 V 給 50 到 255再根據(jù)掩膜多選還是漏選逐步收窄。另外OpenCV 的 H 通道范圍是 0 到 179不是 0 到 360這個坑幾乎每個新手都會踩你用其他工具查到的紅色是 0 度轉(zhuǎn)換時卻寫 360結(jié)果掩膜全黑。如果你發(fā)現(xiàn) inRange 的結(jié)果一片黑先檢查是不是把 H 上限寫大了。2.2 閾值分割把圖像變成“是/否”的判斷題閾值處理的本質(zhì)是給每個像素的灰度值出一道判斷題大于某個數(shù)的算一類小于等于某個數(shù)的算另一類。輸出是一張二值圖像素值只有 0 和 255。這在邏輯上很像給全班同學(xué)按分?jǐn)?shù)線分組分?jǐn)?shù)線選得好分組才有意義。最基礎(chǔ)的函數(shù)是cv2.threshold它有幾種模式。固定閾值適合圖像整體亮度相對均勻的情況比如掃描件、純色背景的文檔。你指定閾值 127凡是灰度值大于 127 的變成 255白色小于等于 127 的變成 0黑色。但真實場景里圖像光照往往不均勻固定閾值就會出問題同一張圖里亮的地方背景過曝暗的地方前景和背景糊在一起無論把閾值調(diào)到多少總有一部分分錯。這時候可以用 Otsu 算法也就是cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY cv2.THRESH_OTSU)。Otsu 會自動遍歷所有可能的閾值找出讓前景類和背景類灰度方差最大的那個值。它不需要你手工指定閾值對光照相對平緩的圖像效果好很多。但 Otsu 也怕強(qiáng)噪聲和嚴(yán)重的光照不均。遇到光照不均更可靠的是自適應(yīng)閾值cv2.adaptiveThreshold。它不計算全局閾值而是對圖像中每個小鄰域分別計算閾值相當(dāng)于不同的局部區(qū)域使用不同的分?jǐn)?shù)線。我用過一個很典型的生產(chǎn)案例攝像頭拍攝的金屬零件表面反光嚴(yán)重全局閾值無論怎么調(diào)都沒法完整分離字符最后換成自適應(yīng)閾值雖然噪聲變多了但字符結(jié)構(gòu)完整保留下來后續(xù)再用形態(tài)學(xué)清理噪聲就解決了。第4天剛開始時我建議你建立一個“先直方圖再選閾值方法”的習(xí)慣。先用cv2.calcHist或者 NumPy 統(tǒng)計灰度值的分布看一眼直方圖是單峰還是雙峰。如果雙峰明顯Otsu 大概率好用如果直方圖擠成一團(tuán)就需要濾波或者自適應(yīng)閾值來干預(yù)。這個習(xí)慣的價值會在你后面處理真實圖片時體現(xiàn)得淋漓盡致。2.3 形態(tài)學(xué)操作給二值圖像做“整容”閾值處理完得到的二值圖通常不會干干凈凈可能有小白點有目標(biāo)內(nèi)部的空洞有邊緣毛刺。形態(tài)學(xué)操作就是專門處理這種二值圖形態(tài)的工具。它操作的對象是二值圖但你也可以對灰度圖做只是第4天階段先專注二值圖就夠用。四個基本操作要記牢腐蝕、膨脹、開運算、閉運算。腐蝕是讓白色區(qū)域變小能把細(xì)小的白色噪聲點消除但同時也會讓目標(biāo)邊緣往里縮膨脹是讓白色區(qū)域變大能把目標(biāo)內(nèi)部的小黑洞填上把斷開的區(qū)域接上但也會讓噪聲一起變大開運算是先腐蝕后膨脹適合去除小白點同時保持目標(biāo)原本的尺寸閉運算是先膨脹后腐蝕適合填補(bǔ)目標(biāo)內(nèi)部的空洞和連接斷裂的條狀結(jié)構(gòu)。內(nèi)核 kernel 是一個很關(guān)鍵但容易被忽略的參數(shù)也就是結(jié)構(gòu)元。它決定每次操作時掃描鄰域的形狀和大小。我見過不少人在初學(xué)階段把內(nèi)核設(shè)成 20x20結(jié)果圖像幾乎被“抹平”了。第4天先從 3x3 或者 5x5 的全1矩陣開始效果不夠再慢慢加大。下面是開運算和閉運算的典型用法import cv2 import numpy as np kernel np.ones((3, 3), np.uint8) # 開運算去小白點 opening cv2.morphologyEx(binary, cv2.MORPH_OPEN, kernel) # 閉運算填補(bǔ)目標(biāo)內(nèi)部小黑洞連接斷口 closing cv2.morphologyEx(binary, cv2.MORPH_CLOSE, kernel)形態(tài)學(xué)操作雖然簡單但它能解決你在實際項目中遇到的大部分“臟數(shù)據(jù)”問題。我經(jīng)常把二值圖比作一塊毛坯鐵板閾值分割只是把鐵板粗加工成型形態(tài)學(xué)操作是精加工修毛邊、補(bǔ)沙眼、打磨連接處。順序也很重要一般先做一次輕度的開運算去噪再做閉運算填補(bǔ)目標(biāo)內(nèi)部的空洞最后如果需要平滑邊緣可以再來一次輕微的開運算。2.4 邊緣檢測Canny 算子為何是第4天的重中之重邊緣檢測和閾值分割經(jīng)常被混為一談但它們解決的問題不一樣。閾值分割依據(jù)的是像素本身的灰度值屬于哪一類邊緣檢測依據(jù)的是像素與周圍像素之間的灰度差異。一個邊緣點意味著在這個位置上灰度值發(fā)生了劇烈變化。第4天我最推薦優(yōu)先吃透 Canny 邊緣檢測。Canny 內(nèi)部是一整套流程先用高斯濾波降噪再計算每個像素的梯度強(qiáng)度和方向然后做非極大值抑制把細(xì)線邊緣抑制到單像素寬度最后用雙閾值和滯后連接把真正的邊緣連接成線。雙閾值是理解 Canny 參數(shù)的關(guān)鍵如果一個像素的梯度值高于高閾值它被認(rèn)定為強(qiáng)邊緣低于低閾值它被直接丟棄處在兩個閾值之間的弱邊緣只有和強(qiáng)邊緣相連才會被保留下來。這個機(jī)制的作用是把“確定的邊緣”和“可能的邊緣”分開。實際調(diào)用非常簡單import cv2 img cv2.imread(sample.jpg) gray cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) blurred cv2.GaussianBlur(gray, (5, 5), 0) edges cv2.Canny(blurred, 50, 150)雙閾值的取值沒有一個放之四海而皆準(zhǔn)的公式但有一個比較穩(wěn)妥的經(jīng)驗比例高閾值約為低閾值的 2 到 3 倍。如果圖像噪聲大低閾值就不要太小否則會檢測出一堆碎邊緣如果邊緣太稀疏、斷斷續(xù)續(xù)說明閾值設(shè)置整體偏高或者模糊核設(shè)太大把細(xì)節(jié)平滑沒了。還要區(qū)分邊緣和輪廓邊緣是像素層面的梯度變化位置結(jié)果是一張二值圖輪廓是物體邊界的幾何描述由一串坐標(biāo)點組成。cv2.findContours輸入二值圖通常是 Canny 的輸出或者經(jīng)形態(tài)學(xué)處理后的閾值圖返回每個輪廓的點集之后可以計算面積、周長、外接矩形也可以把輪廓直接畫出來。第4天的收尾任務(wù)就是把“邊緣圖”升級成“輪廓列表”。3. 第4天實操完成“圖像 - 輪廓”整條流水線3.1 用一天時間打通最小可用的圖像分析流程了解了上面的知識點之后最重要的事情是動手整合。我建議第4天只做一個項目拿一張照片最好是有明顯前景物體的照片比如桌上的水杯、地上的足球、墻邊的工具箱完成從讀取圖像到繪制輪廓的完整流程。選擇“目標(biāo)與背景對比不明顯的照片”會更有練習(xí)價值。如果照片里物體和背景顏色很接近你會被迫去思考光照不均、反光、陰影這些問題而不是順風(fēng)順?biāo)嘏芡ùa。相反如果一開始就找一張白底黑色物體的圖閾值一設(shè)就完事那你學(xué)到的東西馬上會還給教程。實操中最推薦的處理順序是讀取圖像 - 轉(zhuǎn)灰度 - 高斯模糊 - 邊緣檢測或閾值處理 - 形態(tài)學(xué)修補(bǔ) - 查找輪廓 - 繪制輪廓。為什么轉(zhuǎn)灰度和模糊要放在邊緣檢測前面因為 Canny 只接受單通道灰度圖作為輸入而且噪聲會制造大量假邊緣。高斯模糊相當(dāng)于給圖像做了一次平滑讓真正的邊緣保留下來讓細(xì)碎的噪聲邊緣被壓制。3.2 實戰(zhàn)現(xiàn)場參數(shù)選擇與中間結(jié)果觀察我現(xiàn)在隨手模擬一個典型的實操過程假設(shè)我用一張塑料瓶的照片練手。原圖里瓶子是半透明的背景是淺色墻面墻面上有反光。第一版代碼簡單粗暴灰度圖 - 固定閾值 127 - findContours結(jié)果發(fā)現(xiàn)瓶子完全和背景融在一起閾值割出來的形狀一團(tuán)糟。這一步就暴露出固定閾值在光照不均時的弱點。于是換上 Otsu 閾值目標(biāo)主體大致分出來了但瓶頸的透明部分產(chǎn)生了空洞瓶身上還有反光造成的高光塊。此時我先用了閉運算把瓶身斷開的區(qū)域重新接上又用了一次開運算把背景里的小噪點清掉。然后 Canny 檢測邊緣輪廓數(shù)量依然偏多因為反光邊緣也被檢測出來。最后我在 findContours 之后加了一道篩選用cv2.contourArea把面積小于某個閾值的輪廓直接丟棄最終穩(wěn)定找到 3 個輪廓瓶子主體、瓶蓋、一個背景雜物。這個現(xiàn)場過程正是第4天應(yīng)該反復(fù)練習(xí)的調(diào)參模式。你需要把自己的代碼寫成“參數(shù)驅(qū)動”的模式把模糊核大小、Canny 的雙閾值、形態(tài)學(xué)內(nèi)核尺寸都提成變量方便來回調(diào)整。我額外說一個經(jīng)驗調(diào)參的時候不要一次改多個參數(shù)這是很多人的通病。一次只改一個變量觀察它對輸出產(chǎn)生了什么影響你才能真正積累手感。3.3 完整示例代碼與結(jié)果判斷標(biāo)準(zhǔn)下面這段代碼是我建議第4天必須完整跑通并理解的最小范例import cv2 import numpy as np img cv2.imread(object.jpg) gray cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # 高斯模糊減少噪聲核大小必須是奇數(shù) blurred cv2.GaussianBlur(gray, (5, 5), 0) # Otsu 自動閾值 _, binary cv2.threshold(blurred, 0, 255, cv2.THRESH_BINARY_INV cv2.THRESH_OTSU) # 形態(tài)學(xué)修補(bǔ)先閉運算連接斷口再開運算去掉噪點 kernel np.ones((3, 3), np.uint8) closed cv2.morphologyEx(binary, cv2.MORPH_CLOSE, kernel, iterations2) opened cv2.morphologyEx(closed, cv2.MORPH_OPEN, kernel, iterations1) # Canny 邊緣檢測雙閾值采用 50 和 150 edges cv2.Canny(opened, 50, 150) # 查找輪廓不同 OpenCV 版本返回值不同 contours, hierarchy cv2.findContours(edges, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) # 繪制所有面積大于 500 的輪廓 result img.copy() for cnt in contours: area cv2.contourArea(cnt) if area 500: cv2.drawContours(result, [cnt], -1, (0, 255, 0), 2) print(area:, area) cv2.imshow(binary, binary) cv2.imshow(edges, edges) cv2.imshow(result, result) cv2.waitKey(0) cv2.destroyAllWindows()判斷這個流水線是否合格不要只看有沒有輪廓被畫出來要看三個點一是輪廓數(shù)量是否符合預(yù)期二是輪廓邊界是否貼合目標(biāo)的真實形狀三是二值圖和邊緣圖上的噪點是否在可接受范圍內(nèi)。如果結(jié)果不對我建議把binary、closed、edges三張中間結(jié)果同時彈出來對比看問題出在第幾個環(huán)節(jié)而不是盯著最終結(jié)果干瞪眼。4. 實戰(zhàn)中容易翻車的現(xiàn)場與排查方法4.1 高頻問題速查表第4天前后我見過太多人在同一個坑里反復(fù)打轉(zhuǎn)。下面這張表基本覆蓋了最常見的翻車場景建議收藏后對著排查現(xiàn)象常見原因排查與解決方向圖像顯示出來顏色詭異用 Matplotlib 顯示 OpenCV 圖像時沒有從 BGR 轉(zhuǎn) RGBplt.imshow(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))imshow 窗口一閃而過或卡死缺少cv2.waitKey(0)或參數(shù)寫錯確保在銷毀窗口前調(diào)用cv2.waitKey(0)二值圖全黑或全白閾值方向選錯或 inRange 上下限寫反檢查THRESH_BINARY_INV是否正確打印灰度最大最小值inRange 結(jié)果為空H、S、V 范圍設(shè)置不合理H 通道寫成 0~360先放寬 S、V 范圍H 對照 OpenCV 的 0~179 范圍調(diào)整邊緣斷成一段一段Canny 高閾值過高或模糊核過大降低高閾值比例或改用 3x3 模糊核輪廓數(shù)量比實際物體多背景噪聲、紋理被誤檢用形態(tài)學(xué)開運算去噪再用contourArea過濾小面積輪廓findContours 報錯“not enough values to unpack”不同 OpenCV 版本返回參數(shù)數(shù)量不同使用contours, _ cv2.findContours(...)兼容形態(tài)學(xué)處理后目標(biāo)消失內(nèi)核過大、迭代次數(shù)過多從 3x3 內(nèi)核、迭代1次開始逐步調(diào)整還有一些問題不是代碼層面的而是數(shù)據(jù)層面的。比如一張 JPEG 圖像經(jīng)過壓縮后邊緣會有噪點同一張圖換成 PNG 結(jié)果完全不同手機(jī)拍攝的照片自帶很強(qiáng)的噪聲不做濾波直接跑 Canny結(jié)果就是一團(tuán)亂麻。第4天就要建立起一個概念算法效果差先別急著懷疑算法檢查前一步的輸入是否干凈。4.2 最值得養(yǎng)成的兩個調(diào)試習(xí)慣第一個習(xí)慣是寫一個“中間結(jié)果拼接顯示”的小工具。把灰度圖、二值圖、邊緣圖、最終結(jié)果橫向拼在一起一次看全??梢杂胏v2.hconcat或者 NumPy 的np.hstack這個工具能讓你在改動參數(shù)時快速定位問題環(huán)節(jié)。比如最終輪廓不對如果發(fā)現(xiàn)二值圖沒問題、邊緣圖出了問題那問題大概率出在邊緣檢測參數(shù)上和前面的閾值處理無關(guān)。第二個習(xí)慣是用滑動條實時調(diào)參。OpenCV 里有cv2.createTrackbar可以為 Canny 的低閾值、高閾值、模糊核大小等參數(shù)創(chuàng)建滑動條拖動時實時刷新結(jié)果。這是調(diào)參效率最高的方式比改一行代碼重啟一次程序爽太多。核心代碼邏輯很簡單cv2.namedWindow(adjust) cv2.createTrackbar(low, adjust, 30, 255, lambda x: None) cv2.createTrackbar(high, adjust, 120, 255, lambda x: None) while True: low cv2.getTrackbarPos(low, adjust) high cv2.getTrackbarPos(high, adjust) edges cv2.Canny(blurred, low, high) cv2.imshow(adjust, edges) if cv2.waitKey(1) 0xFF ord(q): break滑動條的最大價值是讓你直觀看到參數(shù)對結(jié)果的影響幅度閾值低一點邊緣增加多少閾值高一點邊緣斷成什么樣。這種手感比任何理論都重要。等調(diào)出理想?yún)?shù)之后再把數(shù)值寫回正式代碼中。5. 第4天之后如何走得更穩(wěn)5.1 把第4天能力復(fù)用進(jìn)你自己的小項目學(xué)完第4天的內(nèi)容你已經(jīng)可以獨立完成不少實際需求了。我強(qiáng)烈建議在第4天到第6天之間做兩個小項目來檢驗?zāi)芰Α5谝粋€是“硬幣計數(shù)”類的物體統(tǒng)計項目拍一張桌面照片使用灰度化、閾值分割、形態(tài)學(xué)處理、findContours 和cv2.contourArea統(tǒng)計畫面里有多少個硬幣并畫出每個硬幣的最小外接圓。第二個是“文檔矯正”類項目拍攝一張有輕微傾斜的紙張照片用邊緣檢測找到最大四邊形輪廓再用透視變換把紙張矯正到正面視角。這兩個項目用到的核心能力幾乎全部來自第4天。我自己帶人學(xué)習(xí)的經(jīng)驗是一天看十個算法示例不如花三天做完一個小項目。項目會逼著你面對真實數(shù)據(jù)真實數(shù)據(jù)會逼著你學(xué)會做預(yù)處理而預(yù)處理正是第4天的主線。后期如果你要對圖像做 OCR字符分割的準(zhǔn)確率很大程度上取決于第4天的閾值處理和形態(tài)學(xué)處理做得好不好?,F(xiàn)在偷的懶后面都會加倍還回來。5.2 一個學(xué)習(xí)安排上的建議如果你是按天打卡學(xué)習(xí)我建議第4天不要把目標(biāo)定得太寬。很多教程把幾十個函數(shù)塞到一天的內(nèi)容里導(dǎo)致學(xué)習(xí)者每個函數(shù)都跑了一遍但一個都不熟。我個人的建議是第4天只深度掌握兩組技能一組是“閾值 形態(tài)學(xué)”另一組是“Canny findContours”。其他的濾波、顏色空間轉(zhuǎn)換只需要會用不需要背參數(shù)。第4天結(jié)束后可以花十分鐘做點自我測試不看文檔憑記憶寫一段代碼把一張彩色圖處理成包含物體輪廓的標(biāo)注圖能順利寫出來才算真正過關(guān)。寫不出來就回到上面的示例代碼重新敲一遍直到自己能在空白文件里獨立完成。學(xué)習(xí) OpenCV 是一個復(fù)利過程第4天打下的“預(yù)處理 輪廓”基礎(chǔ)幾乎會在你之后每個圖像處理項目里反復(fù)出現(xiàn)。關(guān)于“還想再學(xué)多一點”的沖動我也多說一句你完全可以在第5天開始學(xué)輪廓特征比如cv2.arcLength、cv2.minAreaRect、cv2.boundingRect這些工具就可以對輪廓做更精細(xì)的篩選。但如果連 Canny 和閾值都還沒有形成直覺后面的特征提取會讓你在空中樓閣上蓋房子。把今天內(nèi)容里每一個“為什么”想明白比多刷十個示例更有價值。我踩過不少坑也帶人踩過不少坑最后發(fā)現(xiàn)大家真正拉開差距的地方并不是天賦而是是否愿意在第4天停下來把基礎(chǔ)磨扎實。把這條“圖像 - 預(yù)處理 - 輪廓”鏈路吃進(jìn)肌肉記憶再往后走就是水到渠成的事了。