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