實(shí)戰(zhàn):從圖像處理到GUI)
簡介這是一套基于Python與OpenCV實(shí)現(xiàn)的紅綠燈識別系統(tǒng)源代碼面向計(jì)算機(jī)視覺初學(xué)者、課程設(shè)計(jì)或自動(dòng)駕駛?cè)腴T開發(fā)者幫助理解顏色空間轉(zhuǎn)換、閾值分割與形狀分析在交通燈識別中的落地方式。壓縮包共10個(gè)文件約23KB包含5個(gè)py腳本主程序、GUI界面與多個(gè)識別版本、3個(gè)json配置、1個(gè)png預(yù)覽圖及gitignore結(jié)構(gòu)輕量便于快速運(yùn)行與二次修改。系統(tǒng)提供圖形界面支持濾鏡選擇與色彩閾值調(diào)整可適應(yīng)不同光照與攝像頭視角并輸出實(shí)時(shí)識別結(jié)果。已有651人學(xué)習(xí)下載。讀者可從中獲得完整的圖像預(yù)處理、HSV色彩過濾、紅綠燈狀態(tài)判定與GUI交互實(shí)現(xiàn)思路同時(shí)接觸光照變化、實(shí)時(shí)性與錯(cuò)誤處理等工程考量適合作為計(jì)算機(jī)視覺項(xiàng)目練手與功能擴(kuò)展的參考模板。1. 紅綠燈識別系統(tǒng)從一段路口視頻到能跑起來的 GUI手頭有一段行車記錄儀拍的路口視頻想讓它自動(dòng)判斷當(dāng)前是紅燈還是綠燈這件事聽起來像是個(gè)深度學(xué)習(xí)項(xiàng)目實(shí)際上用 python-opencv 的傳統(tǒng)圖像處理思路就能做出一個(gè)能演示、能交互的版本。這套紅綠燈識別系統(tǒng)源代碼的核心邏輯并不復(fù)雜把圖像從 BGR 轉(zhuǎn)到 HSV 空間按顏色閾值把紅、黃、綠三個(gè)區(qū)域摳出來再做形態(tài)學(xué)去噪和輪廓篩選最后根據(jù)亮起的燈位判斷當(dāng)前信號。配上 GUI 界面之后你可以直接拖入圖片或視頻實(shí)時(shí)看到識別結(jié)果和置信度。它適合兩類人一類是剛學(xué)完 opencv 圖像處理、想找一個(gè)完整項(xiàng)目練手的入門者另一類是需要快速搭一個(gè)交通燈智能識別演示原型、驗(yàn)證算法可行性的工程師。整套方案不依賴 GPU普通筆記本就能跑重點(diǎn)在于參數(shù)調(diào)優(yōu)和工程細(xì)節(jié)。2. 顏色閾值分割HSV 空間為什么比 RGB 更適合紅綠燈2.1 從 BGR 到 HSV 的轉(zhuǎn)換邏輯與參數(shù)含義opencv 讀進(jìn)來的圖像默認(rèn)是 BGR 三通道直接在這個(gè)空間里做顏色判斷會(huì)非常痛苦因?yàn)楣庹找蛔僐、G、B 三個(gè)值同時(shí)漂移你很難畫出一條固定的分界線。HSV 把顏色拆成色調(diào)Hue、飽和度Saturation、明度Value三個(gè)維度紅綠燈的顏色差異主要體現(xiàn)在色調(diào)上光照強(qiáng)弱主要影響明度這樣就能把顏色判斷和亮度變化解耦開。轉(zhuǎn)換本身只需要一行import cv2 import numpy as np # 讀取圖像opencv 默認(rèn)按 BGR 通道順序讀入 frame cv2.imread(traffic_light.jpg) # 轉(zhuǎn)換到 HSV 色彩空間后續(xù)所有顏色判斷都在這個(gè)空間做 hsv cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)這里有個(gè)容易翻車的點(diǎn)opencv 的 H 通道取值范圍是 0 到 179不是常見的 0 到 360。這是因?yàn)?8 位圖像里 H 被壓縮了一半紅色在標(biāo)準(zhǔn)色環(huán)上跨 0 度在 opencv 里就變成了 H 接近 0 和接近 179 兩段。所以紅色閾值必須寫兩組不能只寫一組。2.2 紅黃綠三色的閾值范圍與掩膜生成下面是我一般會(huì)用的初始閾值實(shí)際項(xiàng)目里要根據(jù)攝像頭白平衡和路燈色溫微調(diào)# 紅色在 HSV 中跨越 0 度需要兩段閾值合并 lower_red1 np.array([0, 100, 100]) upper_red1 np.array([10, 255, 255]) lower_red2 np.array([160, 100, 100]) upper_red2 np.array([179, 255, 255]) # 黃色范圍較窄集中在 20 到 35 之間 lower_yellow np.array([20, 100, 100]) upper_yellow np.array([35, 255, 255]) # 綠色范圍相對寬但要注意避開青色干擾 lower_green np.array([40, 80, 80]) upper_green np.array([90, 255, 255]) # 生成掩膜每個(gè)顏色得到一張二值圖 mask_red1 cv2.inRange(hsv, lower_red1, upper_red1) mask_red2 cv2.inRange(hsv, lower_red2, upper_red2) mask_red cv2.bitwise_or(mask_red1, mask_red2) mask_yellow cv2.inRange(hsv, lower_yellow, upper_yellow) mask_green cv2.inRange(hsv, lower_green, upper_green)參數(shù)說明每個(gè) np.array 里的三個(gè)值依次是 H、S、V 的下限或上限。S 和 V 的下限設(shè)成 100 和 100 是為了過濾掉灰白色和暗色區(qū)域避免把白色車身或灰色路面誤判成燈。如果發(fā)現(xiàn)夜間識別不到可以把 V 的下限降到 50 左右如果白天誤檢多把 S 的下限提到 120 以上。2.3 形態(tài)學(xué)處理去掉噪點(diǎn)、補(bǔ)上空洞掩膜出來之后通常會(huì)有很多小噪點(diǎn)燈的中心也可能因?yàn)榉垂獬霈F(xiàn)空洞。這時(shí)候用開運(yùn)算去噪、閉運(yùn)算補(bǔ)洞# 定義一個(gè) 5x5 的橢圓結(jié)構(gòu)元素比矩形更貼合圓形燈體 kernel cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5, 5)) # 開運(yùn)算先腐蝕后膨脹去掉孤立噪點(diǎn) mask_red cv2.morphologyEx(mask_red, cv2.MORPH_OPEN, kernel) # 閉運(yùn)算先膨脹后腐蝕填補(bǔ)燈體內(nèi)部空洞 mask_red cv2.morphologyEx(mask_red, cv2.MORPH_CLOSE, kernel) # 對黃色和綠色做同樣處理 mask_yellow cv2.morphologyEx(mask_yellow, cv2.MORPH_OPEN, kernel) mask_yellow cv2.morphologyEx(mask_yellow, cv2.MORPH_CLOSE, kernel) mask_green cv2.morphologyEx(mask_green, cv2.MORPH_OPEN, kernel) mask_green cv2.morphologyEx(mask_green, cv2.MORPH_CLOSE, kernel)kernel 大小是個(gè)經(jīng)驗(yàn)值。5x5 適合 640x480 到 1280x720 的分辨率如果圖像更大比如 1920x1080建議用 7x7 或 9x9。kernel 太小去不掉噪點(diǎn)太大會(huì)把遠(yuǎn)處的小燈也一起吃掉。這一步做完掩膜上基本只剩下幾個(gè)干凈的色塊。3. 輪廓篩選與燈位判斷怎么從一堆色塊里找到真正的信號燈3.1 輪廓提取與面積、寬高比過濾掩膜上可能同時(shí)存在多個(gè)色塊比如紅色尾燈、紅色廣告牌、紅色衣服。需要靠幾何特征把真正的信號燈挑出來def find_candidates(mask, min_area50, max_area5000): 從掩膜中提取候選輪廓按面積和寬高比過濾 contours, _ cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) candidates [] for cnt in contours: area cv2.contourArea(cnt) # 面積太小是噪點(diǎn)太大可能是大面積紅色物體 if area min_area or area max_area: continue x, y, w, h cv2.boundingRect(cnt) aspect_ratio w / float(h) # 信號燈近似圓形或略扁寬高比在 0.5 到 2.0 之間 if aspect_ratio 0.5 or aspect_ratio 2.0: continue # 填充率輪廓面積與外接矩形面積之比圓形約 0.78 fill_ratio area / (w * h) if fill_ratio 0.5: continue candidates.append((x, y, w, h, area)) return candidatesmin_area 和 max_area 需要根據(jù)實(shí)際畫面里燈的大小來定。如果攝像頭離路口遠(yuǎn)燈在畫面里只有十幾個(gè)像素min_area 要降到 20 左右。fill_ratio 這個(gè)指標(biāo)很實(shí)用它能過濾掉細(xì)長條形的紅色物體比如紅色欄桿或紅色車身?xiàng)l紋。3.2 用位置關(guān)系排除干擾燈位排列的幾何約束真正的紅綠燈三個(gè)燈是垂直或水平排列的而且同一時(shí)刻通常只有一個(gè)燈亮。利用這個(gè)先驗(yàn)知識可以大幅降低誤判def group_traffic_lights(red_cands, yellow_cands, green_cands): 將三個(gè)顏色的候選框按位置聚類判斷是否屬于同一組信號燈 all_cands [] for c in red_cands: all_cands.append((red, c)) for c in yellow_cands: all_cands.append((yellow, c)) for c in green_cands: all_cands.append((green, c)) # 按 x 坐標(biāo)排序水平排列的信號燈 x 依次增大 all_cands.sort(keylambda item: item[1][0]) groups [] used [False] * len(all_cands) for i in range(len(all_cands)): if used[i]: continue group [all_cands[i]] used[i] True for j in range(i 1, len(all_cands)): if used[j]: continue # 兩個(gè)候選框中心點(diǎn)距離小于閾值認(rèn)為是同一組 xi, yi, wi, hi, _ all_cands[i][1] xj, yj, wj, hj, _ all_cands[j][1] cx1, cy1 xi wi / 2, yi hi / 2 cx2, cy2 xj wj / 2, yj hj / 2 dist ((cx1 - cx2) ** 2 (cy1 - cy2) ** 2) ** 0.5 if dist max(wi, hi) * 3: group.append(all_cands[j]) used[j] True groups.append(group) return groups這個(gè)聚類邏輯的核心是同一組信號燈的候選框中心距離不會(huì)太遠(yuǎn)大概是燈體尺寸的 2 到 3 倍。如果畫面里同時(shí)出現(xiàn)多個(gè)路口的燈這個(gè)分組能把它們分開后續(xù)每組獨(dú)立判斷。3.3 亮燈判定與置信度輸出一組信號燈里哪個(gè)顏色面積最大、填充率最高就判定為當(dāng)前亮燈def judge_signal(group): 輸入一組候選框輸出當(dāng)前信號燈顏色和置信度 if not group: return unknown, 0.0 # 按面積排序取最大的作為主候選 group.sort(keylambda item: item[1][4], reverseTrue) color, (x, y, w, h, area) group[0] # 置信度用填充率表示越接近 1 越像完整燈體 fill_ratio area / (w * h) confidence min(fill_ratio, 1.0) # 如果同組里有多個(gè)顏色面積接近說明判斷不可靠 if len(group) 1: second_area group[1][1][4] if second_area area * 0.7: return uncertain, confidence * 0.5 return color, confidence置信度低于 0.5 的時(shí)候界面上應(yīng)該顯示“不確定”而不是硬給一個(gè)結(jié)果。實(shí)際路口視頻里黃燈和紅燈切換的瞬間經(jīng)常出現(xiàn)兩個(gè)顏色同時(shí)可見的情況這時(shí)候輸出 uncertain 比輸出錯(cuò)誤答案更合理。4. GUI 界面搭建用 tkinter 把識別流程串起來4.1 界面布局與控件選擇python 自帶的 tkinter 足夠做這個(gè)演示界面不需要額外裝 PyQt。布局上分三塊頂部是文件選擇按鈕和模式切換中間是圖像顯示區(qū)底部是識別結(jié)果和參數(shù)調(diào)節(jié)滑塊。import tkinter as tk from tkinter import filedialog, ttk from PIL import Image, ImageTk class TrafficLightGUI: def __init__(self, root): self.root root self.root.title(紅綠燈識別系統(tǒng)) self.root.geometry(900x700) # 頂部控制欄 self.top_frame tk.Frame(root) self.top_frame.pack(filltk.X, padx10, pady5) self.btn_image tk.Button(self.top_frame, text選擇圖片, commandself.load_image) self.btn_image.pack(sidetk.LEFT, padx5) self.btn_video tk.Button(self.top_frame, text選擇視頻, commandself.load_video) self.btn_video.pack(sidetk.LEFT, padx5) self.btn_stop tk.Button(self.top_frame, text停止, commandself.stop_video, statetk.DISABLED) self.btn_stop.pack(sidetk.LEFT, padx5) # 圖像顯示區(qū) self.canvas tk.Canvas(root, width860, height500, bggray) self.canvas.pack(padx10, pady5) # 底部結(jié)果欄 self.bottom_frame tk.Frame(root) self.bottom_frame.pack(filltk.X, padx10, pady5) self.result_label tk.Label(self.bottom_frame, text等待輸入, font(Arial, 16)) self.result_label.pack(sidetk.LEFT) self.confidence_label tk.Label(self.bottom_frame, text置信度: --, font(Arial, 12)) self.confidence_label.pack(sidetk.RIGHT) self.video_capture None self.is_playing False這里用 PIL 的 ImageTk 來顯示 opencv 圖像因?yàn)?tkinter 原生不支持直接顯示 numpy 數(shù)組。轉(zhuǎn)換的時(shí)候要注意 BGR 轉(zhuǎn) RGB否則顏色會(huì)偏。4.2 圖片識別流程與結(jié)果回顯圖片模式的流程很直接讀圖、識別、畫框、顯示。def load_image(self): path filedialog.askopenfilename( filetypes[(Image files, *.jpg *.jpeg *.png *.bmp)]) if not path: return frame cv2.imread(path) if frame is None: self.result_label.config(text圖像讀取失敗) return result_frame, color, confidence self.process_frame(frame) self.show_frame(result_frame) self.result_label.config(textf識別結(jié)果: {color}) self.confidence_label.config(textf置信度: {confidence:.2f}) def process_frame(self, frame): 完整的識別流程返回畫了框的圖像和識別結(jié)果 hsv cv2.cvtColor(frame, cv2.COLOR_BGR2HSV) # 生成三個(gè)顏色的掩膜 mask_red self.get_red_mask(hsv) mask_yellow cv2.inRange(hsv, lower_yellow, upper_yellow) mask_green cv2.inRange(hsv, lower_green, upper_green) # 形態(tài)學(xué)處理 kernel cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5, 5)) for mask in [mask_red, mask_yellow, mask_green]: cv2.morphologyEx(mask, cv2.MORPH_OPEN, kernel, dstmask) cv2.morphologyEx(mask, cv2.MORPH_CLOSE, kernel, dstmask) # 提取候選并分組 red_cands find_candidates(mask_red) yellow_cands find_candidates(mask_yellow) green_cands find_candidates(mask_green) groups group_traffic_lights(red_cands, yellow_cands, green_cands) # 取面積最大的一組作為主信號燈 if groups: groups.sort(keylambda g: sum(c[1][4] for c in g), reverseTrue) color, confidence judge_signal(groups[0]) # 在圖上畫出所有候選框 for c in groups[0]: x, y, w, h, _ c[1] cv2.rectangle(frame, (x, y), (x w, y h), (0, 255, 0), 2) else: color, confidence unknown, 0.0 return frame, color, confidenceprocess_frame 是整個(gè)系統(tǒng)的核心它把前面幾章講的步驟串在一起。注意這里對每個(gè)掩膜都做了開閉運(yùn)算實(shí)際跑的時(shí)候如果發(fā)現(xiàn)視頻卡頓可以把形態(tài)學(xué)操作去掉靠輪廓篩選來兜底。4.3 視頻模式與幀循環(huán)處理視頻模式需要在主線程之外跑一個(gè)循環(huán)否則界面會(huì)卡死。tkinter 的 after 方法可以做到不阻塞界面def load_video(self): path filedialog.askopenfilename( filetypes[(Video files, *.mp4 *.avi *.mov)]) if not path: return self.video_capture cv2.VideoCapture(path) if not self.video_capture.isOpened(): self.result_label.config(text視頻打開失敗) return self.is_playing True self.btn_stop.config(statetk.NORMAL) self.update_video() def update_video(self): if not self.is_playing or self.video_capture is None: return ret, frame self.video_capture.read() if not ret: self.stop_video() return # 縮小分辨率加快處理速度 frame cv2.resize(frame, (640, 360)) result_frame, color, confidence self.process_frame(frame) self.show_frame(result_frame) self.result_label.config(textf識別結(jié)果: {color}) self.confidence_label.config(textf置信度: {confidence:.2f}) # 每 30 毫秒處理下一幀約 33fps self.root.after(30, self.update_video) def stop_video(self): self.is_playing False if self.video_capture: self.video_capture.release() self.video_capture None self.btn_stop.config(statetk.DISABLED)視頻處理時(shí)把分辨率降到 640x360 是個(gè)實(shí)用技巧識別精度損失不大但速度能提升三到四倍。如果原始視頻是 1080p直接處理會(huì)非???。5. 避坑與排查紅綠燈識別里最容易翻車的五個(gè)地方5.1 紅色總是識別不到或者識別成兩段現(xiàn)象畫面里紅燈明明很亮但掩膜上紅色區(qū)域是斷開的或者干脆沒有。原因opencv 的 H 通道紅色跨越 0 和 179 兩端只寫一組閾值只能覆蓋一半。另外如果攝像頭白平衡偏冷紅色會(huì)往品紅方向偏H 值可能跑到 170 以上。解決紅色閾值必須寫兩組并用 bitwise_or 合并。如果還是斷把 H 的上限從 10 放寬到 15下限從 160 降到 150。用 cv2.cvtColor 轉(zhuǎn)成 HSV 后可以先把 H 通道單獨(dú)取出來看直方圖確認(rèn)紅燈的 H 值到底落在哪個(gè)區(qū)間。5.2 白天正常、晚上全黑或者全是噪點(diǎn)現(xiàn)象白天跑得好好的一到夜間視頻要么什么都識別不到要么滿屏都是紅色噪點(diǎn)。原因夜間路燈和車燈造成大面積高亮區(qū)域S 和 V 的分布跟白天完全不同。固定的 S、V 下限在夜間會(huì)把很多暗紅色物體也框進(jìn)來。解決加一個(gè)亮度判斷分支。先算整幀的平均 V 值如果低于 60判定為夜間模式把 S 下限降到 60V 下限降到 40同時(shí)把 min_area 提高到 100 以上過濾掉小噪點(diǎn)。如果高于 180判定為強(qiáng)光模式把 S 下限提到 150避免白色高光被誤判。5.3 視頻處理越來越卡最后界面無響應(yīng)現(xiàn)象視頻剛開始還能跑幾十幀之后越來越慢最后整個(gè)窗口卡死。原因update_video 里每幀都創(chuàng)建新的 numpy 數(shù)組和 PIL Image 對象python 的垃圾回收跟不上內(nèi)存持續(xù)增長。另外如果 process_frame 里做了太多形態(tài)學(xué)操作單幀耗時(shí)超過 30 毫秒after 隊(duì)列會(huì)堆積。解決把 kernel 的創(chuàng)建提到循環(huán)外面不要每幀都 getStructuringElement。PIL Image 對象用完手動(dòng) del 并調(diào)用 gc.collect() 不現(xiàn)實(shí)更好的做法是復(fù)用同一個(gè) PhotoImage 對象用 paste 更新內(nèi)容。如果還是卡把形態(tài)學(xué)操作去掉只保留輪廓面積過濾。5.4 黃燈和紅燈同時(shí)亮起時(shí)判斷混亂現(xiàn)象黃燈亮的時(shí)候系統(tǒng)有時(shí)候報(bào)紅色有時(shí)候報(bào)黃色切換瞬間結(jié)果跳來跳去。原因黃燈的 H 范圍是 20 到 35紅燈的上限是 10中間有 10 個(gè)單位的間隔。但實(shí)際攝像頭里黃燈邊緣會(huì)有紅色漸變導(dǎo)致同一盞燈同時(shí)產(chǎn)生紅色和黃色候選框。解決在 judge_signal 里加一個(gè)優(yōu)先級規(guī)則如果同一組里同時(shí)有紅色和黃色候選且黃色面積大于紅色的 60%判定為黃色。另外在時(shí)間維度上做平滑連續(xù) 3 幀結(jié)果一致才更新界面顯示避免單幀誤判導(dǎo)致結(jié)果閃爍。5.5 綠色把路邊的樹和青色廣告牌也框進(jìn)來現(xiàn)象綠燈識別沒問題但畫面邊緣的綠色植物或青色招牌經(jīng)常被誤判成綠燈。原因綠色的 H 范圍 40 到 90 太寬把青色和部分黃綠色都包進(jìn)來了。另外植物的紋理復(fù)雜輪廓填充率可能剛好落在閾值邊緣。解決把綠色的 H 上限從 90 降到 75下限從 40 提到 45。同時(shí)提高 fill_ratio 的下限到 0.6因?yàn)檎嬲木G燈是均勻發(fā)光的圓盤填充率接近 0.78而樹葉的填充率通常低于 0.5。如果還有誤檢加一個(gè)位置約束只保留畫面下半部分的綠色候選因?yàn)樾盘枱敉ǔ2粫?huì)出現(xiàn)在畫面頂部。6. 進(jìn)階技巧用幀間差分和 ROI 把識別速度再提一倍前面講的方案是逐幀全圖處理在 640x360 分辨率下大概能跑到 15 到 20 幀。如果想讓它在樹莓派或者老筆記本上也能流暢跑有兩個(gè)技巧值得試。第一個(gè)是 ROI 裁剪。信號燈在畫面里的位置通常比較固定尤其是固定攝像頭拍路口。你可以先手動(dòng)框一個(gè)感興趣區(qū)域后續(xù)只在這個(gè)區(qū)域里做顏色分割。ROI 一般取畫面中間偏上的位置寬度占全圖的三分之一到一半。這樣處理面積直接降到原來的四分之一速度提升非常明顯。def crop_roi(frame, roi_ratio(0.3, 0.1, 0.7, 0.6)): 按比例裁剪 ROI默認(rèn)取畫面中間偏上區(qū)域 h, w frame.shape[:2] x1 int(w * roi_ratio[0]) y1 int(h * roi_ratio[1]) x2 int(w * roi_ratio[2]) y2 int(h * roi_ratio[3]) return frame[y1:y2, x1:x2], (x1, y1)roi_ratio 四個(gè)值依次是左、上、右、下的比例。這個(gè)參數(shù)需要根據(jù)你的攝像頭安裝位置調(diào)沒有萬能值。調(diào)的時(shí)候先把 ROI 框畫出來看一眼確保信號燈始終在框內(nèi)。第二個(gè)是幀間差分。視頻里相鄰兩幀的背景幾乎不變只有信號燈切換的時(shí)候?qū)?yīng)區(qū)域才有明顯變化??梢韵人惝?dāng)前幀和上一幀的差值只在差值大的區(qū)域做顏色識別。這樣大部分幀只需要做一次減法耗時(shí)可以忽略。class FrameDiffDetector: def __init__(self, threshold25, min_changed_ratio0.01): self.prev_gray None self.threshold threshold self.min_changed_ratio min_changed_ratio def need_process(self, frame): 判斷當(dāng)前幀是否需要做完整識別 gray cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) gray cv2.GaussianBlur(gray, (5, 5), 0) if self.prev_gray is None: self.prev_gray gray return True diff cv2.absdiff(self.prev_gray, gray) _, thresh cv2.threshold(diff, self.threshold, 255, cv2.THRESH_BINARY) changed_ratio np.count_nonzero(thresh) / thresh.size self.prev_gray gray # 變化面積超過閾值才做完整識別 return changed_ratio self.min_changed_ratiothreshold 控制像素差多少算變化25 是個(gè)比較穩(wěn)的值太低會(huì)把噪點(diǎn)當(dāng)變化太高會(huì)漏掉緩慢的燈色切換。min_changed_ratio 控制變化面積占比0.01 表示畫面 1% 的像素變了才觸發(fā)識別。實(shí)際用的時(shí)候信號燈切換那一兩幀會(huì)觸發(fā)識別其余幀直接復(fù)用上一次結(jié)果整體幀率能翻倍。這兩個(gè)技巧疊加使用的時(shí)候要注意順序先做 ROI 裁剪再做幀間差分最后在 ROI 內(nèi)做顏色分割。ROI 裁剪減少處理面積幀間差分減少處理頻次兩者不沖突。我自己調(diào)這套系統(tǒng)的時(shí)候最大的教訓(xùn)是不要一上來就追求全自動(dòng)。先把單張圖片的閾值調(diào)穩(wěn)再跑視頻最后加 GUI。很多人反過來界面先搭好結(jié)果識別效果一塌糊涂調(diào)參的時(shí)候連個(gè)穩(wěn)定的測試基準(zhǔn)都沒有。另外 HSV 閾值沒有一套參數(shù)能通吃所有攝像頭換一個(gè)設(shè)備就要重新標(biāo)定這個(gè)心理預(yù)期要有。希望幫到你。本文還有配套的精品資源點(diǎn)擊獲取