前言
在日常生活中,經常會看到條形碼的應用,比如超市買東西的生活,圖書館借書的時候。那么這些東西是如何做到準確檢測出條形碼的位置呢?本篇博文的目標是演示使用計算機視覺和圖像處理技術實現條形碼的檢測。
通過本篇文章的學習,我們能學到的內容包括:
圖像處理中常用的一些操作流程,包括濾波、閾值化處理、膨脹、腐蝕和輪廓查找等;
更重要的一點,希望通過這個案例,能夠幫助大家建立分析問題和處理問題的思路。
需要注意的是,這個算法并不適用于所有的條形碼,但是它應該能給你一個基本的直覺,告訴你應該應用什么類型的技術。
一、條形碼的檢測
1.1 目標:找到條形碼的位置,而去除掉干擾的因素。
1.2 思路:利用條形碼的自身特點,一般都是矩形形狀,而且條碼的條帶是黑色的,矩形區域是白色的。
1.3 代碼
step 1: 得到只剩下高水平梯度和低垂直梯度的圖像區域。
import argparse import imutils import cv2 # 構造參數解析并分析參數 ap = argparse.ArgumentParser() ap.add_argument("-i", "--image", required=True, help="path to the image file") args = vars(ap.parse_args()) image = cv2.imread(args["image"]) gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) # 計算圖片 x 和 y 方向的 Scharr 梯度大小 # 使用 Scharr 操作符(這里制定ksize=1)去構造圖片在水平和垂直方向上的梯度幅值表示。 ddepth = cv2.cv.CV_32F if imutils.is_cv2() else cv2.CV_32F gradX = cv2.Sobel(gray, ddepth=ddepth, dx=1, dy=0, ksize=-1) gradY = cv2.Sobel(gray, ddepth=ddepth, dx=0, dy=1, ksize=-1) # 用 x 方向的梯度減去 y 方向的梯度 -> 得到只剩下了高水平梯度和低垂直梯度的圖像區域。 gradient = cv2.subtract(gradX, gradY) gradient = cv2.convertScaleAbs(gradient) cv2.imwrite('./test_img/gradient.jpg', gradient)
step 2: 如何過濾掉圖片中的噪聲,重點關注條形碼區域。
# 對圖片進行模糊和閾值化操作 # 使用一個卷積核大小為 9x9 的均值濾波作用于梯度圖片。對圖片進行這個操作將有助于平滑圖片中的高頻噪聲。 # 然后將模糊化后的圖片進行閾值化, blurred = cv2.blur(gradient, (9, 9)) cv2.imwrite('./test_img/blurred.jpg', blurred) (_, thresh) = cv2.threshold(blurred, 200, 255, cv2.THRESH_BINARY) # thresh = cv2.adaptiveThreshold(blurred, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY_INV, 5, 3) cv2.imwrite('./test_img/thresh.jpg', thresh) # 進行閾值化操作,更加容易的檢測出條形碼的“斑點”狀區域 # 核的寬度大于高度,因此允許我們縮小條形碼垂直條帶之間的間隙 kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (21, 7)) closed = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, kernel) cv2.imwrite('./test_img/closed.jpg', closed)
step 3: 進行腐蝕與膨脹操作,腐蝕操作將會“腐蝕”掉圖片中的白色像素點,因此將會清除這些小的斑點,而膨脹操作將會“擴張”剩余的白色像素,并使白色區域變長。如果在腐蝕過程中去除了小的斑點,則在膨脹的過程中不會再次出現。在一系列的腐蝕和膨脹操作之后,這些小斑點已經被成功的移除了,只剩下條形碼的區域。
step 4: 最后尋找一下圖片中條形碼的區域的輪廓。
# 找到閾值化后圖片中的輪廓,然后進行根據區域進行排序,僅保留最大區域 cnts = cv2.findContours(closed.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) cnts = imutils.grab_contours(cnts) c = sorted(cnts, key=cv2.contourArea, reverse=True)[0] # 計算最大輪廓的旋轉邊界框 rect = cv2.minAreaRect(c) box = cv2.cv.BoxPoints(rect) if imutils.is_cv2() else cv2.boxPoints(rect) box = np.int0(box) # 在檢測到的條形碼周圍繪制邊界框并顯示圖片 cv2.drawContours(image, [box], -1, (0, 255, 0), 3) cv2.imwrite('./test_img/bar_det.jpg', image)
二、條形碼的識別
識別圖片中的條碼(pyzbar)及條碼圖片矯正和增強。
step 1: 導入所需要的包
2.1 正常角度
2.2 180度旋轉
2.3 45度旋轉
def barcode(gray): texts = pyzbar.decode(gray) if not texts: angle = barcode_angle(gray) if angle < -45: angle = -90 - angle texts = bar(gray, angle) if not texts: gray = np.uint8(np.clip((1.1 * gray + 10), 0, 255)) angle = barcode_angle(gray) if angle < -45: angle = -90 - angle texts = bar(gray, angle) return texts def bar(image, angle): gray = image bar = rotate_bound(gray, 0 - angle) roi = cv2.cvtColor(bar, cv2.COLOR_BGR2RGB) texts = pyzbar.decode(roi) return texts def barcode_angle(image): gray = image ret, binary = cv2.threshold(gray, 220, 255, cv2.THRESH_BINARY_INV) kernel = np.ones((8, 8), np.uint8) dilation = cv2.dilate(binary, kernel, iterations=1) erosion = cv2.erode(dilation, kernel, iterations=1) erosion = cv2.erode(erosion, kernel, iterations=1) erosion = cv2.erode(erosion, kernel, iterations=1) contours, hierarchy = cv2.findContours(erosion, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE) if len(contours) == 0: rect = [0, 0, 0] else: rect = cv2.minAreaRect(contours[0]) return rect[2] def rotate_bound(image, angle): (h, w) = image.shape[:2] (cX, cY) = (w // 2, h // 2) M = cv2.getRotationMatrix2D((cX, cY), -angle, 1.0) cos = np.abs(M[0, 0]) sin = np.abs(M[0, 1]) nW = int((h * sin) + (w * cos)) nH = int((h * cos) + (w * sin)) M[0, 2] += (nW / 2) - cX M[1, 2] += (nH / 2) - cY return cv2.warpAffine(image, M, (nW, nH)) image = cv2.imread("./test_img/test03.png") gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) texts = barcode(gray) print(texts) if not texts: print("未識別成功") else: tt = '' for text in texts: tt = text.data.decode("utf-8") print("識別成功") print(tt)
編輯:黃飛
?
評論