
簡介這套資源提供 objectmarker 工具的完整源碼與可執(zhí)行程序面向需要自制物體識別數(shù)據(jù)集的計算機視覺初、中級開發(fā)者。它基于 OpenCV 實現(xiàn)交互式正樣本標注用戶通過鼠標在圖像上框選目標區(qū)域程序會將多個邊界框信息寫入 info.txt方便后續(xù)訓練檢測或分類模型。壓縮包共 10 個文件包含 cpp 源碼、exe 可執(zhí)行文件、dll 運行庫、txt 說明文檔及示例位圖整體體積僅 1.01MB結(jié)構(gòu)緊湊、便于直接運行或二次修改。已有 291 人學習下載。借助該工具讀者可快速理解正樣本描述格式圖像名加邊界框坐標并掌握用 OpenCV 處理鼠標事件、顯示圖像和保存標注結(jié)果的實現(xiàn)思路對于公開數(shù)據(jù)集難以覆蓋的小眾類別也能自行批量標注圖像構(gòu)建貼合業(yè)務(wù)需求的訓練集。附帶的 readme 與 info 示例文件有助于快速上手適合用于物體識別項目的前期數(shù)據(jù)準備和算法驗證。1. ObjectMarker給 OpenCV 識別批量生成正樣本描述的工具提到 OpenCV 識別從零訓練級聯(lián)分類器是最常見的一條路而這條路的第一步就是準備正樣本。正樣本不是把圖片丟進文件夾就完事OpenCV 要求一個記錄目標位置的描述文件格式稍有偏差后面訓練直接崩。很多項目卡在圖片有幾百張、目標又小又多手工數(shù)坐標能讓心態(tài)崩掉。ObjectMarker附源碼就是用來解決這個問題的打開圖片鼠標框出目標自動生成 OpenCV 訓練要用的正樣本描述文本。適合訓練人臉、車牌、缺陷檢測這類目標區(qū)域明確的識別場景也適合第一次接觸 opencv_traincascade 的初學者。2. 正樣本描述文件的格式邊界先看懂 info.txt 再開始標用級聯(lián)分類器訓練前OpenCV 要求把所有正樣本的標注寫進一個文本。這個文件的行格式直接決定后續(xù) opencv_createsamples 能否正確裁出正樣本。很多人第一次訓練失敗不是標注得不準而是格式不對。所以先用一節(jié)把它掰開揉碎。2.1 每種 OpenCV 訓練工具吃的格式一行一個邏輯樣本opencv_traincascade 的 -info 參數(shù)接受的文件每一行由三部分組成圖像路徑、目標個數(shù)、目標矩形列表。標準寫法是relative/path/to/image.jpg 1 x y w h其中 x、y 是矩形左上角坐標w、h 是寬度和高度單位是像素。所有坐標必須以圖片左上角為原點向右向下為正。一張圖里有多個目標就把數(shù)量改成 2后面跟兩組 x y w hrelative/path/to/image.jpg 3 10 15 80 90 120 130 60 70 200 40 55 65這里有個新手常踩的誤區(qū)以為 w、h 是右下角坐標。實際上 opencv_createsamples 在讀取時是按寬度和高度來理解你填成右下角坐標它會把樣本裁成一個寬高等于右下角減左上角的巨大區(qū)域后面訓練直接報廢。我一般會寫一個小腳本先校驗 w、h 都大于 0 且不超過圖像尺寸。這里還要區(qū)分路徑的寫法。OpenCV 按相對路徑讀取圖片而相對路徑是相對于你執(zhí)行訓練命令時的當前目錄。如果 info.txt 寫在 train_data 目錄下圖片在 train_data/positive_images 里路徑就應(yīng)該寫成positive_images/001.jpg而不是帶一串絕對路徑。否則換一臺機器跑或者從別的目錄啟動訓練全部路徑找不到。2.2 為什么手動維護坐標清單容易出低級錯誤當圖片數(shù)量上百張每張里有兩三個小目標時手動把坐標敲進文本很難保證不出錯。我把常見錯誤按出現(xiàn)頻率排了個序框沒有完整框住目標目標邊緣被切掉一部分。一張圖里多個目標眼睛數(shù)漏了數(shù)量字段和實際矩形個數(shù)對不上。復(fù)制粘貼路徑時大小寫不一致Windows 下沒問題Linux 下直接找不到文件。坐標重疊或者負數(shù)訓練中途才彈 assert failed。這些錯誤不會在訓練剛開始就暴露往往在跑了一兩個小時后彈出異?;蛘哂柧毘龅哪P驼`檢率高得離譜。因此用工具生成描述文件本質(zhì)上是在把“人肉計算坐標”換成“鼠標拖框 自動換算”從源頭減少數(shù)字出錯。2.3 ObjectMarker 的標注核心鼠標回調(diào)與矩形框管理ObjectMarker 的源碼核心其實不復(fù)雜基于 OpenCV 的窗口鼠標回調(diào)來記錄矩形。我拆過類似的工具最關(guān)鍵是三塊鼠標按下開始記錄鼠標移動時更新臨時矩形鼠標松開時把合法矩形存進列表。下面這段代碼就是核心循環(huán)import cv2 boxes [] # 已確認的矩形列表 start None # 按下鼠標時的坐標 temp None # 移動中的臨時矩形 def on_mouse(event, x, y, flags, param): global start, temp if event cv2.EVENT_LBUTTONDOWN: start (x, y) temp (x, y, x, y) elif event cv2.EVENT_MOUSEMOVE: if start: temp (start[0], start[1], x, y) elif event cv2.EVENT_LBUTTONUP: if start and x start[0] and y start[1]: boxes.append((start[0], start[1], x, y)) start None cv2.namedWindow(marker) cv2.setMouseCallback(marker, on_mouse)邏輯說明回調(diào)函數(shù)根據(jù)鼠標事件維護三個變量boxes存放已確認的矩形start記錄按下瞬間坐標temp用于實時顯示。當左鍵松開時判斷x start[0]且y start[1]作用是排除反向拖拽產(chǎn)生的負寬高矩形。存進boxes的是圖片顯示坐標系下的絕對坐標寫入文件前不需要再換算。參數(shù)說明cv2.setMouseCallback里的窗口名必須和namedWindow保持一致?;卣{(diào)里的x, y是窗口顯示坐標系的像素值。如果圖片在窗口中被縮放顯示那么原圖坐標需要做一次除法原圖坐標 顯示坐標 / 縮放比例。大多數(shù)復(fù)刻源碼里會保留這個縮放因子否則框出來的位置會偏移。這塊也是你拿到源碼后最值得改的地方。我建議在顯示窗口上方加一個類似狀態(tài)欄的區(qū)域?qū)崟r顯示當前圖片路徑、已框目標數(shù)量、當前矩形寬高。這個小改動在標注大量小目標時非常救命每框一個目標就 1減少漏標率。如果你的目標物體邊緣不明顯先用圖像增強把對比度拉開再標避免框太小或者框偏移。2.4 程序里如何管理圖像目錄與自動保存ObjectMarker 一般有個簡單的文件隊列加載目錄下所有 jpg/png按文件名排序逐張展示。標注完一張切到下一張前把該圖所有矩形寫入輸出文件再清空boxes。這樣即使中途關(guān)掉程序已標注的圖片也不會丟。你如果拿到源碼想二次開發(fā)主要改這幾處文件后綴過濾目前只認.jpg和.png如果你的數(shù)據(jù)集是.bmp要手動加后綴。輸出路徑默認生成在運行目錄下建議改成絕對路徑避免不同終端啟動時寫入位置不一致。圖片縮放顯示原圖分辨率很大時窗口可能放不下需要按比例縮小顯示但坐標要還原回原圖尺度。3. 從圖片目錄到輸出文件ObjectMarker 標注實操流程3.1 環(huán)境準備與源碼運行方式拿到 ObjectMarker 源碼后先確認 OpenCV 環(huán)境。如果源碼是 Python 版本安裝 opencv-python 就夠了如果源碼是 C 版本需要編譯 OpenCV 并配置 CMake。一個比較省心的做法是先建一個干凈的虛擬環(huán)境跑通腳本再逐步加依賴。python -m venv venv source venv/bin/activate pip install opencv-python numpy參數(shù)說明虛擬環(huán)境避免把全局 Python 環(huán)境搞亂。opencv-python 自帶常用圖像處理模塊numpy 用于圖像數(shù)組操作。如果你的源碼還用到了 tqdm 做進度條也一并裝上。邏輯說明先激活環(huán)境再裝依賴避免裝到系統(tǒng)目錄里。后面的運行指令都在這個虛擬環(huán)境下執(zhí)行退出后不影響其他項目。如果源碼是 C 版本核心依賴是 opencv 庫和 CMake。編譯過程相比 Python 會多一步但運行效率更高適合大批量圖片。不過大多數(shù)場景下Python 版本已經(jīng)夠用畢竟標注操作是交互式的瓶頸在人的手速不在程序速度。3.2 命令行參數(shù)與操作流程很多復(fù)刻的 ObjectMarker 工具會提供三個參數(shù)圖片目錄、輸出文件路徑、顯示窗口尺寸。常見調(diào)用如下python objectmarker.py --img-dir ./positive_images --output info.txt --win-width 800 --win-height 600這里--img-dir指定存放正樣本圖片的目錄--output指定生成的描述文件名--win-width和--win-height是顯示窗口的尺寸不是訓練尺寸。這兩個參數(shù)只影響畫框時的窗口縮放比例不影響最終坐標精度。如果你把顯示窗口設(shè)得比原圖小內(nèi)部會按比例縮放坐標換算到原圖時依然是準確像素。然后按下面的步驟操作運行命令后窗口出現(xiàn)第一張圖。按住鼠標左鍵拖出矩形框住目標松開左鍵確認一個框。同一張圖有多個目標就繼續(xù)框直到框完所有目標。按空格鍵保存本圖所有矩形到輸出文件并跳到下一張。全部完成后按 q 退出。需要特別提醒的是這里的“保存”是增量寫入也就是說跳下一張之前把當前圖片的矩形寫進文件而不是等全部標注完統(tǒng)一寫。這樣程序意外崩潰時已標注的圖片不會白標。3.3 目錄組織建議與輸出文件檢查建議把正樣本圖片放在一個獨立目錄生成文件放在上一級。比如train_data/ positive_images/ 001.jpg 002.jpg info.txt這樣在 info.txt 里寫positive_images/001.jpg訓練時只要在 train_data 目錄下啟動命令就能找到所有圖片。避免寫絕對路徑否則換一臺機器路徑全變。保存后打開 info.txt 檢查前三行。如果看到類似這樣的內(nèi)容positive_images/001.jpg 2 10 15 80 90 120 130 60 70 positive_images/002.jpg 1 30 40 110 95說明每行格式基本對。再檢查矩形是否超出圖像邊界以及數(shù)量字段和實際坐標組數(shù)是否一致。我見過最陰間的錯誤是某一行數(shù)量寫成 2但后面跟了 3 組坐標OpenCV 讀取時按數(shù)量截取第三組被當成下一行的開頭后面全部錯位。所以這個檢查不能省。3.4 批量預(yù)處理把圖片裁好再標省得訓練時踩坑標注之前建議對圖片做一次統(tǒng)一預(yù)處理。常見做法是用腳本把圖片統(tǒng)一命名成 6 位數(shù)字比如000001.jpg再按需縮放。這樣 info.txt 里的文件名是整齊的后續(xù)寫數(shù)據(jù)增強腳本也方便。下面是一個簡單的前置處理腳本把一張大圖按滑動窗口切成小圖并跳過面積小于閾值的子圖import cv2 import os def preprocess(image_path, output_dir, patch_size256, overlap64): img cv2.imread(image_path) if img is None: print(ffail to read: {image_path}) return h, w img.shape[:2] idx 0 for y in range(0, h - patch_size 1, patch_size - overlap): for x in range(0, w - patch_size 1, patch_size - overlap): patch img[y:y patch_size, x:x patch_size] out_path os.path.join(output_dir, f{idx:06d}.jpg) cv2.imwrite(out_path, patch) idx 1 preprocess(raw.jpg, positive_images)參數(shù)說明patch_size是滑窗尺寸overlap是相鄰窗口重疊區(qū)域這兩個值決定你從一張大圖能得到多少張子圖。如果你的目標物體不會正好跨邊界可以不用重疊減少重復(fù)樣本。邏輯說明腳本按patch_size - overlap作為步長滑動切割避免目標剛好被切碎。所有子圖統(tǒng)一命名生成的正樣本描述文件里路徑也會整齊很多。這里有個取舍滑窗切出來的子圖可能大量不包含目標你需要后續(xù)用 ObjectMarker 把包含目標的子圖挑出來其余刪掉否則負樣本混進正樣本里模型會學得很困惑。4. 標注與訓練銜接的常見問題排查五個最容易翻車的坑4.1 訓練時提示找不到圖片文件現(xiàn)象opencv_traincascade 啟動后報錯Can not open file或者Failed to read image。原因info.txt 里的路徑是相對路徑但命令運行的當前目錄不在 train_data 下或者路徑里的子目錄名寫錯。解決先確認工作目錄cd到 train_data 再執(zhí)行訓練命令。在 info.txt 中統(tǒng)一使用斜杠/不要混用 Windows 的反斜杠。如果你用 Windows建議在標注工具里直接把路徑替換成標準斜杠避免跨平臺時出問題。4.2 生成的樣本全是全黑或全白現(xiàn)象訓練出的 .vec 里樣本無法看標記的物體沒有出現(xiàn)在裁片中。原因坐標 w、h 被誤填成右下角坐標導(dǎo)致目標偏移或裁到背景或者圖像通道處理錯誤BGR 和 RGB 混用。解決在源碼輸出前強制檢查 w、h 為正數(shù)并且x w、y h不超過圖像寬高。可加代碼把坐標限制在合法范圍內(nèi)def clamp_rect(x, y, w, h, img_w, img_h): x max(0, min(x, img_w - 1)) y max(0, min(y, img_h - 1)) w min(w, img_w - x) h min(h, img_h - y) return x, y, w, h參數(shù)說明img_w和img_h是原圖寬高傳入前從cv2.imread讀取。clamp_rect的作用是把越界坐標拉回圖像范圍內(nèi)避免生成無效裁剪區(qū)域。邏輯說明先 clamp 左上角坐標再根據(jù)剩余空間收縮寬高。這里不直接修改原圖只是保證寫入 info.txt 的坐標合法。建議在保存前對每一條矩形都調(diào)用一次。4.3 多目標圖只檢測出來一部分現(xiàn)象訓練完成后檢測率很低特別是那些包含多個目標的圖片。原因標注的時候漏框某些目標被當成背景模型學到的特征不完整。解決保存前顯示當前圖片上已經(jīng)畫好的所有框確認數(shù)量一致再跳下一張。如果圖片數(shù)量大可以每 20 張停一次把本輪標注結(jié)果打印到終端。批量標注最容易出現(xiàn)“標著標著忘了這張圖有幾個目標”的情況所以工具里顯示已框數(shù)量不是小事。4.4 坐標正常但訓練一直報錯現(xiàn)象提示Invalid rectangle或者訓練中途崩潰。原因某些圖片尺寸不一致而框坐標是從上一張圖片繼承下來沒有清空。解決每次切換圖片時強制清空boxes數(shù)組。如果圖片尺寸相差大最好在標注前統(tǒng)一縮放或填充保證所有圖片的寬高范圍一致。否則即使坐標合法不同尺寸的樣本混在一起訓練也會導(dǎo)致歸一化時出現(xiàn)問題。4.5 負樣本數(shù)量足夠但正樣本描述文件行首數(shù)字是 0現(xiàn)象info.txt 里某行是path.jpg 0opencv_createsamples 直接跳過這行。原因按下空格時boxes列表為空說明沒有框任何目標。解決工具里加判斷如果boxes為空就不寫入該行同時提示當前圖片沒有標記讓你決定是重新標還是跳過。不要寫個 0 混過去這一行不會報錯但會浪費一次樣本機會并且讓你以為這批數(shù)據(jù)沒問題。5. 接進 opencv_createsamples用一次可視化校驗收尾描述文件生成后最終要喂給 opencv_createsamples 生成 .vec 文件這一步是訓練前最后一道關(guān)卡。命令格式如下opencv_createsamples -info info.txt -num 2000 -w 24 -h 24 -vec positives.vec參數(shù)說明-info指定 ObjectMarker 生成的正樣本描述文件-num是抽取的樣本數(shù)不要超過描述文件中的目標總數(shù)否則會報錯-w和-h是統(tǒng)一縮放尺寸建議保持 24 或 20不要設(shè)太大否則訓練時間劇增-vec是輸出的樣本向量文件。邏輯說明-num的值代表最終參與訓練的正樣本數(shù)量。如果你的描述文件里一共有 1500 個目標就填 1500。填多了會報錯填少了浪費已有數(shù)據(jù)。生成 vec 后我習慣做一次可視化校驗不急著開訓。最直接的方式是用 opencv_createsamples 自帶的展示功能opencv_createsamples -vec positives.vec -show 10這個命令會彈出窗口一屏展示前 10 個正樣本。你可以按空格翻頁按 ESC 退出。只需要花兩分鐘就能確認每個樣本里目標是否完整、有無被裁掉一半的情況。我以前跳過這步直接開訓結(jié)果訓練了兩小時模型識別不到目標回頭查才發(fā)現(xiàn)框選時所有框都偏了半個身位。從那以后我每次標注完都強制走一遍-show檢查確認框內(nèi)是完整目標再啟動訓練。希望幫到你。本文還有配套的精品資源點擊獲取