-
Notifications
You must be signed in to change notification settings - Fork 17
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
🎉 Barcode detection and recognition first approach
- Loading branch information
Achraf KHAZRI
authored and
Achraf KHAZRI
committed
Sep 26, 2019
1 parent
524d5b8
commit b382bd2
Showing
2 changed files
with
130 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,130 @@ | ||
import numpy as np | ||
import cv2 | ||
from pyzbar import pyzbar | ||
|
||
|
||
def grab_contours(cnts): | ||
# if the length the contours tuple returned by cv2.findContours | ||
# is '2' then we are using either OpenCV v2.4, v4-beta, or | ||
# v4-official | ||
if len(cnts) == 2: | ||
cnts = cnts[0] | ||
|
||
# if the length of the contours tuple is '3' then we are using | ||
# either OpenCV v3, v4-pre, or v4-alpha | ||
elif len(cnts) == 3: | ||
cnts = cnts[1] | ||
|
||
# otherwise OpenCV has changed their cv2.findContours return | ||
# signature yet again and I have no idea WTH is going on | ||
else: | ||
raise Exception(("Contours tuple must have length 2 or 3, " | ||
"otherwise OpenCV changed their cv2.findContours return " | ||
"signature yet again. Refer to OpenCV's documentation " | ||
"in that case")) | ||
|
||
# return the actual contours array | ||
return cnts | ||
|
||
def deskew(img): | ||
|
||
gray = cv2.bitwise_not(img) | ||
|
||
# threshold the image, setting all foreground pixels to | ||
# 255 and all background pixels to 0 | ||
thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1] | ||
# grab the (x, y) coordinates of all pixel values that | ||
# are greater than zero, then use these coordinates to | ||
# compute a rotated bounding box that contains all | ||
# coordinates | ||
|
||
coords = np.column_stack(np.where(thresh > 0)) | ||
angle = cv2.minAreaRect(coords)[-1] | ||
|
||
# the `cv2.minAreaRect` function returns values in the | ||
# range [-90, 0); as the rectangle rotates clockwise the | ||
# returned angle trends to 0 -- in this special case we | ||
# need to add 90 degrees to the angle | ||
if angle < -45: | ||
angle = -(90 + angle) | ||
|
||
# otherwise, just take the inverse of the angle to make | ||
# it positive | ||
else: | ||
angle = -angle | ||
|
||
# rotate the image to deskew it | ||
(h, w) = img.shape[:2] | ||
center = (w // 2, h // 2) | ||
M = cv2.getRotationMatrix2D(center, angle, 1.0) | ||
rotated = cv2.warpAffine(img, M, (w, h), flags=cv2.INTER_CUBIC, borderMode=cv2.BORDER_REPLICATE) | ||
|
||
return rotated, angle | ||
|
||
# load the image and convert it to grayscale | ||
image = cv2.imread("test.jpg") | ||
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) | ||
|
||
# compute the Scharr gradient magnitude representation of the images | ||
# in both the x and y direction using OpenCV 2.4 | ||
ddepth = 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) | ||
|
||
# subtract the y-gradient from the x-gradient | ||
gradient = cv2.subtract(gradX, gradY) | ||
gradient = cv2.convertScaleAbs(gradient) | ||
|
||
# blur and threshold the image | ||
blurred = cv2.blur(gradient, (9, 9)) | ||
(_, thresh) = cv2.threshold(blurred, 225, 255, cv2.THRESH_BINARY) | ||
|
||
# construct a closing kernel and apply it to the thresholded image | ||
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (21, 7)) | ||
closed = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, kernel) | ||
|
||
# perform a series of erosions and dilations | ||
closed = cv2.erode(closed, None, iterations = 4) | ||
closed = cv2.dilate(closed, None, iterations = 4) | ||
|
||
# find the contours in the thresholded image, then sort the contours | ||
# by their area, keeping only the largest one | ||
cnts = cv2.findContours(closed.copy(), cv2.RETR_EXTERNAL, | ||
cv2.CHAIN_APPROX_SIMPLE) | ||
cnts = grab_contours(cnts) | ||
c = sorted(cnts, key=cv2.contourArea, reverse=True)[0] | ||
|
||
# compute the rotated bounding box of the largest contour | ||
rect = cv2.minAreaRect(c) | ||
box = cv2.boxPoints(rect) | ||
box = np.int0(box) | ||
scale = 20 | ||
b1 = box[0] | ||
b2 = box[1] | ||
b3 = box[2] | ||
b4 = box[3] | ||
x = [b1[1], b2[1], b3[1], b4[1]] | ||
y = [b1[0], b2[0], b3[0], b4[0]] | ||
x_min = min(x) - scale | ||
y_min = min(y) - scale | ||
x_max = max(x) + scale | ||
y_max = max(y) + scale | ||
|
||
out = gray[x_min:x_max,y_min:y_max] | ||
deskewed, angle = deskew(out) | ||
# draw a bounding box arounded the detected barcode and display the | ||
# image | ||
thresh = cv2.threshold(deskewed, 0, 255, cv2.THRESH_OTSU)[1] | ||
|
||
cv2.imshow("deskew", deskewed) | ||
cv2.waitKey(0) | ||
cv2.destroyAllWindows() | ||
|
||
# Zbar reader | ||
barcodes = pyzbar.decode(thresh) | ||
|
||
# zxing reader | ||
"""import zxing | ||
reader = zxing.BarCodeReader() | ||
barcodes = reader.decode(thresh)""" | ||
print(barcodes) |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.