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