"Project Practice" Barcode Detection and Recognition


Preface

In daily life, we often see the application of barcodes, such as the life of shopping in the supermarket, and when borrowing books in the library. . .
So how do these things accurately detect the position of the barcode?
This is what we will introduce today
. The goal of this blog post is to demonstrate the use of computer vision and image processing technology to achieve barcode detection.

Through the study of this article, what we can learn includes:

  1. Some common operation procedures in image processing, including filtering, thresholding, dilation, erosion, contour search, etc.
  2. More importantly, I hope that through this case, it can help you establish ideas for analyzing and dealing with problems.

It should be noted that this algorithm is not applicable to all barcodes, but it should give you a basic intuition and tell you what type of technology should be applied

1. Barcode detection

Insert picture description here

1.1 Goal

Find the position of the barcode, and remove the interference factors

1.2 Ideas

Utilizing the characteristics of barcodes, they are generally rectangular, and the strips of the barcode are black and the rectangular area is white

1.3 Code

Step 1: Get the image area with only high horizontal gradient and low vertical gradient

import numpy as np
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: How to filter out the noise in the picture, focusing on the barcode area.

# 对图片进行模糊和阈值化操作
# 使用一个卷积核大小为 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: Perform corrosion and expansion operations. The corrosion operation will "erode" the white pixels in the picture, so these small spots will be removed, and the expansion operation will "expand" the remaining white pixels and make the white area lengthen. If the small spots are removed during the corrosion process, they will not reappear during the expansion process. After a series of corrosion and expansion operations, these small spots have been successfully removed, leaving only the barcode area.

# 执行一系列的腐蚀和膨胀操作
closed = cv2.erode(closed, None, iterations=2)
closed = cv2.dilate(closed, None, iterations=2)

Insert picture description here
step 4: Finally, look for the outline of the barcode area in the picture.

# 找到阈值化后图片中的轮廓,然后进行根据区域进行排序,仅保留最大区域
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)

Insert picture description here

2. Barcode recognition

Recognize the bar code (pyzbar) in the picture and the bar code picture correction and enhancement

step 1: Import the required packages

import cv2
import pyzbar.pyzbar as pyzbar
import numpy as np

2.1 Normal angle

image = cv2.imread("./test_img/test01.png")
gray = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
texts = pyzbar.decode(gray)
tt = ''
for text in texts:
    tt = text.data.decode("utf-8")
print(tt)

2.2 180 degree rotation

image = cv2.imread("./test_img/test03.png")
gray = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
texts = pyzbar.decode(gray)
print(texts)
if not texts:
    print("未识别成功")
else:
    tt = ''
    for text in texts:
        tt = text.data.decode("utf-8")
    print("识别成功")
    print(tt)

2.3 45 degree rotation

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)

Guess you like

Origin blog.csdn.net/libo1004/article/details/112795208