基于OpenCV的條形碼區(qū)域分割
點擊上方“小白學視覺”,選擇加"星標"或“置頂”
重磅干貨,第一時間送達

本期,我們將一起學習如何從圖像中提取出含有條形碼的區(qū)域。下面的代碼,我們將在Anaconda中采用Python 2.7 完成,當然OpenCV中的圖像處理庫也是必不可少的。
分割是識別圖像內(nèi)一個或多個對象的位置的過程。我們要介紹的技術其實非常簡單,它利用了形態(tài)算子的擴張和侵蝕,以及諸如開運算,閉運算和黑帽算子的組合。
01.簡介
安裝Anaconda后,讓我們從Anaconda的提示符下使用以下命令轉到OpenCV安裝:
conda install -c https://conda.anaconda.org/menpo opencv
現(xiàn)在,讓我們從Anaconda啟動器啟動Spyder IDE。

Anaconda啟動器
一旦運行了Spyder,建議驗證OpenCV安裝是否成功。在Python控制臺的右下角,我們進行以下測試:
import cv2代碼講解
我們已經(jīng)創(chuàng)建了一個啟動GitHub存儲庫。小伙伴可以使用以下方法直接克隆它:
git?clone?--branch?step1https://github.com/lucapiccinelli/BarcodesTutorial.git
現(xiàn)在,我們將要下載測試圖像,并對他們進行讀取和顯示。

測試圖片
import cv2import?matplotlib.pyplot?as?pltim?=?cv2.imread(r’img\barcodes.jpg’,?cv2.IMREAD_GRAYSCALE)plt.imshow(im, cmap=’Greys_r’)
接下來,我們將對圖像進行二值化處理,這樣可以通過閾值的設定來提取出我們感興趣的部分。使用黑帽運算符,我們可以增加較暗的圖像元素。我們可以首先使用簡單的全局閾值安全地對圖像進行二值化處理。黑帽運算符使我們可以使用非常低的閾值,而不必過多地關注噪聲。
在應用blackhat時,我們使用的內(nèi)核會更加重視垂直圖像元素。內(nèi)核具有固定的大小,因此可以縮放圖像,這也可以提高性能(并支持某種輸入歸一化)。

黑帽+閾值處理
它遵循其他形態(tài)運算符的采用,順序地將它們組合在一起以獲得條形碼位置中的連接組件。
#riscalatura dell'immaginescale = 800.0 / im.shape[1]im = cv2.resize(im, (int(im.shape[1] * scale), int(im.shape[0] * scale)))#blackhatkernel = np.ones((1, 3), np.uint8)im = cv2.morphologyEx(im, cv2.MORPH_BLACKHAT, kernel, anchor=(1, 0))#sogliaturathresh, im = cv2.threshold(im, 10, 255, cv2.THRESH_BINARY)
膨脹和閉合的這種組合在測試圖像上效果很好,但可能無法在其他圖像上達到相同的效果。這沒有關系,大家可以嘗試改變參數(shù)和運算符的組合,直到對結果滿意為止。

膨脹+閉運算
最后的預處理步驟是應用具有很大內(nèi)核的開運算符,以刪除太少而無法適合條形碼形狀的元素。
kernel = np.ones((21, 35), np.uint8)im = cv2.morphologyEx(im, cv2.MORPH_OPEN, kernel, iterations=1)
這是我們希望得到的最終結果:

使用35x21內(nèi)核打開
現(xiàn)在,我們可以運行連接的組件的檢測算法,并檢索帶有坐標和尺寸的條形碼矩形。如大家在上一張圖像中所看到的那樣,最后的形態(tài)學步驟并未濾除全部的噪聲。但是,在這種情況下,將它們過濾掉非常簡單,以矩形區(qū)域值作為閾值就可以了。
#rilettura dell'immagine, stavolta a coloriim_out = cv2.imread(r'img\barcodes.jpg')#estrazione dei componenti connessicontours, hierarchy = cv2.findContours(im, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)unscale = 1.0 / scaleif contours != None:for contour in contours:# se l'area non è grande a sufficienza la saltoif cv2.contourArea(contour) <= 2000:continue#estraggo il rettangolo di area minima (in formato (centro_x, centro_y), (width, height), angolo)rect = cv2.minAreaRect(contour)#l'effetto della riscalatura iniziale deve essere eliminato dalle coordinate rilevaterect = \((int(rect[0][0] * unscale), int(rect[0][1] * unscale)), \(int(rect[1][0] * unscale), int(rect[1][1] * unscale)), \rect[2])#disegno il tutto sull'immagine originalebox = np.int0(cv2.cv.BoxPoints(rect))cv2.drawContours(im_out, [box], 0, (0, 255, 0), thickness = 2)plt.imshow(im_out)#scrittura dell' immagine finalecv2.imwrite(r'img\out.png', im_out)
最后,在上面的代碼中,我使用提取的矩形繪制它們,并將其覆蓋在原始圖像上。

最終結果,條形碼以綠色框突出顯示。
結論
? 提出的技術非常簡單有效,但存在一些令人討厭的缺點:
? 它對條形碼偏斜非常敏感;它可以很好地工作到大約45度,然后您必須執(zhí)行第二遍,修改內(nèi)核的方向。
? 它只能在固定尺寸范圍內(nèi)找到條形碼。
? 盡管對矩形區(qū)域施加了過濾,但仍有可能無法清除某些非條形碼。
第一個和第二個可能不是真正的問題,但是最后一個可能會花費大家大量時間來嘗試解碼非條形碼的內(nèi)容。
一個很好的解決方案是將條形碼特征(圖像梯度,傅立葉變換)輸入給神經(jīng)網(wǎng)絡(或一些其他一些分類器),并在第二時刻過濾掉噪聲。
下面給出完整的示例代碼。
import cv2import matplotlib.pyplot as pltimport numpy as npim = cv2.imread(r'img\barcodes.jpg', cv2.IMREAD_GRAYSCALE)im_out = cv2.imread(r'img\barcodes.jpg')#riscalatura dell'immaginescale = 800.0 / im.shape[1]im = cv2.resize(im, (int(im.shape[1] * scale), int(im.shape[0] * scale)))#blackhatkernel = np.ones((1, 3), np.uint8)im = cv2.morphologyEx(im, cv2.MORPH_BLACKHAT, kernel, anchor=(1, 0))#sogliaturathresh, im = cv2.threshold(im, 10, 255, cv2.THRESH_BINARY)#operazioni morfologichekernel = np.ones((1, 5), np.uint8)im = cv2.morphologyEx(im, cv2.MORPH_DILATE, kernel, anchor=(2, 0), iterations=2) #dilatazioneim = cv2.morphologyEx(im, cv2.MORPH_CLOSE, kernel, anchor=(2, 0), iterations=2) #chiusurakernel = np.ones((21, 35), np.uint8)im = cv2.morphologyEx(im, cv2.MORPH_OPEN, kernel, iterations=1)#estrazione dei componenti connessicontours, hierarchy = cv2.findContours(im, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)unscale = 1.0 / scaleif contours != None:for contour in contours:# se l'area non è grande a sufficienza la saltoif cv2.contourArea(contour) <= 2000:continue#estraggo il rettangolo di area minima (in formato (centro_x, centro_y), (width, height), angolo)rect = cv2.minAreaRect(contour)#l'effetto della riscalatura iniziale deve essere eliminato dalle coordinate rilevaterect = \((int(rect[0][0] * unscale), int(rect[0][1] * unscale)), \(int(rect[1][0] * unscale), int(rect[1][1] * unscale)), \rect[2])#disegno il tutto sull'immagine originalebox = np.int0(cv2.cv.BoxPoints(rect))cv2.drawContours(im_out, [box], 0, (0, 255, 0), thickness = 2)plt.imshow(im_out)#scrittura dell' immagine finalecv2.imwrite(r'img\out.png', im_out)
交流群
歡迎加入公眾號讀者群一起和同行交流,目前有SLAM、三維視覺、傳感器、自動駕駛、計算攝影、檢測、分割、識別、醫(yī)學影像、GAN、算法競賽等微信群(以后會逐漸細分),請掃描下面微信號加群,備注:”昵稱+學校/公司+研究方向“,例如:”張三?+?上海交大?+?視覺SLAM“。請按照格式備注,否則不予通過。添加成功后會根據(jù)研究方向邀請進入相關微信群。請勿在群內(nèi)發(fā)送廣告,否則會請出群,謝謝理解~
