Development of the ocr part of AOI
Samo Penic
2018-11-16 511c2e7452a37fd32e7e7e1b83d46277ea57b6e3
commit | author | age
e555c0 1 from pyzbar.pyzbar import decode
SP 2 import cv2
3 import numpy as np
4 import math
5
6
511c2e 7 class Paper:
SP 8     def __init__(self, filename=None):
9         self.filename = filename
10         self.invalid = None
11         self.QRData = None
12         self.errors = []
13         self.warnings = []
14         if filename is not None:
15             self.loadImage(filename)
16             self.runOcr()
e555c0 17
511c2e 18     def loadImage(self, filename, rgbchannel=0):
SP 19         self.img = cv2.imread(filename, rgbchannel)
20         if self.img is None:
21             self.errors.append("File could not be loaded!")
22             self.invalid = True
23             return
24         self.imgHeight, self.imgWidth = self.img.shape[0:2]
e555c0 25
511c2e 26     def saveImage(self, filename="debug_image.png"):
SP 27         cv2.imwrite(filename, self.img)
e555c0 28
511c2e 29     def runOcr(self):
SP 30         if self.invalid == True:
31             return
32         self.decodeQRandRotate()
33         self.imgTreshold()
34         skewAngle = 0
35         #         try:
36         #             skewAngle=self.getSkewAngle()
37         #         except:
38         #             self.errors.append("Could not determine skew angle!")
39         #         self.rotateAngle(skewAngle)
e555c0 40
511c2e 41         self.generateAnswerMatrix()
e555c0 42
511c2e 43         self.saveImage()
e555c0 44
511c2e 45     def decodeQRandRotate(self):
SP 46         if self.invalid == True:
47             return
48         blur = cv2.blur(self.img, (3, 3))
49         d = decode(blur)
50         self.img = blur
51         if len(d) == 0:
52             self.errors.append("QR code could not be found!")
53             self.data = None
54             self.invalid = True
55             return
56         self.QRDecode = d
57         self.QRData = d[0].data
58         xpos = d[0].rect.left
59         ypos = d[0].rect.top
60         # check if image is rotated wrongly
61         if xpos > self.imgHeight / 2.0 and ypost > self.imgWidth / 2.0:
62             self.rotateAngle(180)
e555c0 63
511c2e 64     def rotateAngle(self, angle=0):
SP 65         rot_mat = cv2.getRotationMatrix2D(
66             (self.imgHeight / 2, self.imgWidth / 2), angle, 1.0
67         )
68         result = cv2.warpAffine(
69             self.img,
70             rot_mat,
71             (self.imgHeight, self.imgWidth),
72             flags=cv2.INTER_CUBIC,
73             borderMode=cv2.BORDER_CONSTANT,
74             borderValue=(255, 255, 255),
75         )
e555c0 76
511c2e 77         self.img = result
SP 78         self.imgHeight, self.imgWidth = self.img.shape[0:2]
e555c0 79
511c2e 80         # todo, make better tresholding
e555c0 81
511c2e 82     def imgTreshold(self):
SP 83         (self.thresh, self.bwimg) = cv2.threshold(
84             self.img, 128, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU
85         )
e555c0 86
511c2e 87     def getSkewAngle(self):
SP 88         neg = 255 - self.bwimg  # get negative image
89         cv2.imwrite("debug_1.png", neg)
e555c0 90
511c2e 91         angle_counter = 0  # number of angles
SP 92         angle = 0.0  # collects sum of angles
93         cimg = cv2.cvtColor(self.img, cv2.COLOR_GRAY2BGR)
e555c0 94
511c2e 95         # get all the Hough lines
SP 96         for line in cv2.HoughLinesP(neg, 1, np.pi / 180, 325):
97             x1, y1, x2, y2 = line[0]
98             cv2.line(cimg, (x1, y1), (x2, y2), (0, 0, 255), 2)
99             # calculate the angle (in radians)
100             this_angle = np.arctan2(y2 - y1, x2 - x1)
101             if this_angle and abs(this_angle) <= 10:
102                 # filtered zero degree and outliers
103                 angle += this_angle
104                 angle_counter += 1
e555c0 105
511c2e 106                 # the skew is calculated of the mean of the total angles, #try block helps with division by zero.
SP 107         try:
108             skew = np.rad2deg(
109                 angle / angle_counter
110             )  # the 1.2 factor is just experimental....
111         except:
112             skew = 0
e555c0 113
511c2e 114         cv2.imwrite("debug_2.png", cimg)
SP 115         return skew
e555c0 116
511c2e 117     def locateUpMarkers(self, threshold=0.85, height=200):
SP 118         template = cv2.imread("template.png", 0)
119         w, h = template.shape[::-1]
120         crop_img = self.img[0:height, :]
121         res = cv2.matchTemplate(crop_img, template, cv2.TM_CCOEFF_NORMED)
122         loc = np.where(res >= threshold)
123         cimg = cv2.cvtColor(crop_img, cv2.COLOR_GRAY2BGR)
124         # remove false matching of the squares in qr code
125         loc_filtered_x = []
126         loc_filtered_y = []
127         if len(loc[0]) == 0:
128             min_y = -1
129         else:
130             min_y = np.min(loc[0])
131             for pt in zip(*loc[::-1]):
132                 if pt[1] < min_y + 20:
133                     loc_filtered_y.append(pt[1])
134                     loc_filtered_x.append(pt[0])
135                     # order by x coordinate
136             loc_filtered_x, loc_filtered_y = zip(
137                 *sorted(zip(loc_filtered_x, loc_filtered_y))
138             )
139         # loc=[loc_filtered_y,loc_filtered_x]
140         # remove duplicates
141             a = np.diff(loc_filtered_x) > 40
142             a = np.append(a, True)
143             loc_filtered_x = np.array(loc_filtered_x)
144             loc_filtered_y = np.array(loc_filtered_y)
145             loc = [loc_filtered_y[a], loc_filtered_x[a]]
146             for pt in zip(*loc[::-1]):
147                 cv2.rectangle(cimg, pt, (pt[0] + w, pt[1] + h), (0, 255, 255), 2)
e555c0 148
511c2e 149         cv2.imwrite("debug_3.png", cimg)
e555c0 150
511c2e 151         self.xMarkerLocations = loc
SP 152         return loc
e555c0 153
511c2e 154     def locateRightMarkers(self, threshold=0.85, width=200):
SP 155         template = cv2.imread("template.png", 0)
156         w, h = template.shape[::-1]
157         crop_img = self.img[:, -width:]
158         res = cv2.matchTemplate(crop_img, template, cv2.TM_CCOEFF_NORMED)
159         loc = np.where(res >= threshold)
160         cimg = cv2.cvtColor(crop_img, cv2.COLOR_GRAY2BGR)
161         # remove false matching of the squares in qr code
162         loc_filtered_x = []
163         loc_filtered_y = []
164         if len(loc[1]) == 0:
165             min_x = -1
166         else:
167             max_x = np.max(loc[1])
168             for pt in zip(*loc[::-1]):
169                 if pt[1] > max_x - 20:
170                     loc_filtered_y.append(pt[1])
171                     loc_filtered_x.append(pt[0])
172                     # order by y coordinate
173             loc_filtered_y, loc_filtered_x = zip(
174                 *sorted(zip(loc_filtered_y, loc_filtered_x))
175             )
176             # loc=[loc_filtered_y,loc_filtered_x]
177             # remove duplicates
178             a = np.diff(loc_filtered_y) > 40
179             a = np.append(a, True)
180             loc_filtered_x = np.array(loc_filtered_x)
181             loc_filtered_y = np.array(loc_filtered_y)
182             loc = [loc_filtered_y[a], loc_filtered_x[a]]
183             for pt in zip(*loc[::-1]):
184                 cv2.rectangle(cimg, pt, (pt[0] + w, pt[1] + h), (0, 255, 255), 2)
e555c0 185
511c2e 186         cv2.imwrite("debug_4.png", cimg)
e555c0 187
511c2e 188         self.yMarkerLocations = [loc[0], loc[1] + self.imgWidth - width]
SP 189         return self.yMarkerLocations
e555c0 190
511c2e 191     def generateAnswerMatrix(self):
SP 192         self.locateUpMarkers()
193         self.locateRightMarkers()
e555c0 194
511c2e 195         roixoff = 10
SP 196         roiyoff = 5
197         roiwidth = 50
198         roiheight = roiwidth
199         totpx = roiwidth * roiheight
e555c0 200
511c2e 201         self.answerMatrix = []
SP 202         for y in self.yMarkerLocations[0]:
203             oneline = []
204             for x in self.xMarkerLocations[1]:
205                 roi = self.bwimg[
206                     y - roiyoff : y + int(roiheight - roiyoff),
207                     x - roixoff : x + int(roiwidth - roixoff),
208                 ]
209                 # cv2.imwrite('ans_x'+str(x)+'_y_'+str(y)+'.png',roi)
210                 black = totpx - cv2.countNonZero(roi)
211                 oneline.append(black / totpx)
212             self.answerMatrix.append(oneline)