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