Development of the ocr part of AOI
Samo Penic
2018-11-17 d5c694ac711ca3b434bf16bd920b90d1a7e758c4
commit | author | age
9efc18 1 import cv2
SP 2 import numpy as np
762a5e 3 from skimage import morphology, img_as_ubyte
02e0f7 4
9efc18 5
SP 6 """
7   (1) The text is an array of chars (in row-major order) where
8  *          each char can be one of the following:
9  *             'x': hit
10  *             'o': miss
11  *             ' ': don't-care
12  *      (2) When the origin falls on a hit or miss, use an upper case
13  *          char (e.g., 'X' or 'O') to indicate it.  When the origin
14  *          falls on a don't-care, indicate this with a 'C'.
15  *          The string must have exactly one origin specified.
16  *      (3) The advantage of this method is that the text can be input
17  *          in a format that shows the 2D layout of the Sel; e.g.,
18
19
20     :::: AND ::::
21  
22  
23    (10) The sequence string is formatted as follows:
24  *            ~ An arbitrary number of operations,  each separated
25  *              by a '+' character.  White space is ignored.
26  *            ~ Each operation begins with a case-independent character
27  *              specifying the operation:
28  *                 d or D  (dilation)
29  *                 e or E  (erosion)
30  *                 o or O  (opening)
31  *                 c or C  (closing)
32  *                 r or R  (rank binary reduction)
33  *                 x or X  (replicative binary expansion)
34  *                 b or B  (add a border of 0 pixels of this size)
35  *            ~ The args to the morphological operations are bricks of hits,
36  *              and are formatted as a.b, where a and b are horizontal and
37  *              vertical dimensions, rsp.
38  *            ~ The args to the reduction are a sequence of up to 4 integers,
39  *              each from 1 to 4.
40  *            ~ The arg to the expansion is a power of two, in the set
41  *              {2, 4, 8, 16}.
42  *      (11) An example valid sequence is:
43  *               "b32 + o1.3 + C3.1 + r23 + e2.2 + D3.2 + X4"
44  *           In this example, the following operation sequence is carried out:
45  *             * b32: Add a 32 pixel border around the input image
46  *             * o1.3: Opening with vert sel of length 3 (e.g., 1 x 3)
47  *             * C3.1: Closing with horiz sel of length 3  (e.g., 3 x 1)
48  *             * r23: Two successive 2x2 reductions with rank 2 in the first
49  *                    and rank 3 in the second.  The result is a 4x reduced pix.
50  *             * e2.2: Erosion with a 2x2 sel (origin will be at x,y: 0,0)
51  *             * d3.2: Dilation with a 3x2 sel (origin will be at x,y: 1,0)
52  *             * X4: 4x replicative expansion, back to original resolution
53
54 """
55
56
57 def kernel(x, y):
58     return np.ones((x, y), np.uint8)
59
d5c694 60 def sid_compare(sid_no, sid_mask):
SP 61     for s,es in zip(sid_mask,sid_no):
62         if s!='x' and s!=es:
63             return False
64     return True
65
66
9efc18 67
762a5e 68 def segment_by_contours(image, sorted_ctrs, classifier):
SP 69     sid_no = ""
70     for i, ctr in enumerate(sorted_ctrs):
71         # Get bounding box
72         x, y, w, h = cv2.boundingRect(ctr)
73         # Getting ROI
74         if w < h / 2:
75             sid_no = sid_no + "1"
76             continue
77         roi = image[y : y + h, x : x + w]
78         roi = img_as_ubyte(roi < 128)
79         roi = cv2.resize(roi, (32, 32))
80
81         # cv2.rectangle(image,(x,y),( x + w, y + h ),(0,255,0),2)
82         cv2.imwrite("sid_no_{}.png".format(i), roi)
83         sid_no = sid_no + str(classifier.predict(roi.reshape(1, -1) / 255.0)[0])
84     return sid_no
85
86
d5c694 87 def segment_by_sid_len(image, original_image, sid_mask, classifier):
5cb7c1 88     sid_no = ""
SP 89     sid_len = len(sid_mask)
90     if sid_mask[0] == "1":
91         move_left = 45
92     elif sid_mask[0] == "x":
93         move_left = 55
94     else:
95         move_left = 0
d5c694 96         # Remove noise
SP 97     image2 = cv2.morphologyEx(original_image, cv2.MORPH_OPEN, kernel(2, 2), iterations=7)
5cb7c1 98     # find biggest block of pixels
d5c694 99     image1 = cv2.morphologyEx(image2, cv2.MORPH_DILATE, kernel(5, 25), iterations=4)
SP 100     image1=img_as_ubyte(image1>50)
5cb7c1 101     cv2.imwrite("sidblock1.png", image1)
ac766e 102     im2, ctrs, hier = cv2.findContours(
SP 103         image1.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE
104     )
5cb7c1 105     sorted_ctrs = sorted(
SP 106         ctrs, key=lambda ctr: cv2.contourArea(ctr)
107     )  # get bigges contour
ac766e 108     x, y, w, h = cv2.boundingRect(sorted_ctrs[-1])
d5c694 109     image = image[y : y + h, x + 25 - move_left : x + w - 40] #+25,-25
5cb7c1 110     cv2.imwrite("sidblock2.png", image)
ac766e 111     imgHeight, imgWidth = image.shape[0:2]
5cb7c1 112     numWidth = int(imgWidth / (sid_len))
SP 113     for i in range(0, sid_len):
114         num = image[:, i * numWidth : (i + 1) * numWidth]
ac766e 115         num = img_as_ubyte(num < 128)
SP 116         num = cv2.resize(num, (32, 32))
117
118         # cv2.rectangle(image,(x,y),( x + w, y + h ),(0,255,0),2)
119         cv2.imwrite("sid_no_{}.png".format(i), num)
120         sid_no = sid_no + str(classifier.predict(num.reshape(1, -1) / 255.0)[0])
121     return sid_no
122
d5c694 123 def segment_by_7segments(image,original_image,sid_mask,classifier):
SP 124     block_image = cv2.morphologyEx(original_image, cv2.MORPH_CLOSE, kernel(2, 2), iterations=10)
125     block_image =img_as_ubyte(block_image<50)
126     cv2.imwrite("sid_3rd1.png", block_image)
127     template = cv2.imread("template-8.png", 0)
128     w, h = template.shape[::-1]
129     res = cv2.matchTemplate(block_image, template, cv2.TM_CCOEFF_NORMED)
130     loc = np.where(res >= 0.75)
131     cimg = cv2.cvtColor(image, cv2.COLOR_GRAY2BGR)
132     loc_filtered_x=[]
133     loc_filtered_y=[]
134     for pt in zip(*loc[::-1]):
135         pt=(pt[0]-10,pt[1]-10)
136         loc_filtered_y.append(pt[1])
137         loc_filtered_x.append(pt[0])
138 #        points.append(pt)
139     #filter points
140     if(len(loc_filtered_x)==0):
141         return ""
142     loc_filtered_x, loc_filtered_y = zip(
143                 *sorted(zip(loc_filtered_x, loc_filtered_y))
144             )
145     a = np.diff(loc_filtered_x) > int(w/2)
146     a = np.append(a, True)
147     loc_filtered_x = np.array(loc_filtered_x)
148     loc_filtered_y = np.array(loc_filtered_y)
149     points = [loc_filtered_y[a], loc_filtered_x[a]]
150     for pt in zip(*points[::-1]):
151         cv2.rectangle(cimg, pt, (pt[0] + w, pt[1] + h), (0, 255, 255), 2)
152     cv2.imwrite("sid_3rd2.png", cimg)
153
154     sid_no=""
155     for i,pt in enumerate(zip(*points[::-1])):
156         num=image[pt[1]:pt[1] + h, pt[0]:pt[0]+w]
157         #cv2.imwrite("sid_3no_{}.png".format(i), num)
158         num = img_as_ubyte(num < 128)
159         try:
160             num = cv2.resize(num, (32, 32))
161         except:
162             return ""
163         cv2.imwrite("sid_3no_{}.png".format(i), num)
164         sid_no = sid_no + str(classifier.predict(num.reshape(1, -1) / 255.0)[0])
165
166     return sid_no
ac766e 167
762a5e 168 def getSID(image, classifier, sid_mask):
5cb7c1 169     sid_warn = []
d5c694 170     sid_err=[]
762a5e 171     image = 255 - image
d5c694 172     image_original=image.copy()
762a5e 173     image = img_as_ubyte(image > 100)
9efc18 174     cv2.imwrite("enSID0.png", image)
SP 175     # Remove noise
d5c694 176     image = cv2.morphologyEx(image, cv2.MORPH_OPEN, kernel(2, 2), iterations=3)
9efc18 177     # Closing. Connect non connected parts
02e0f7 178     image = cv2.morphologyEx(image, cv2.MORPH_CLOSE, kernel(5, 3), iterations=4)
9efc18 179     # Again noise removal after closing
02e0f7 180
5cb7c1 181     # image = cv2.morphologyEx(image, cv2.MORPH_OPEN, kernel(8, 8), iterations=1)
SP 182     # don't do too much noise removal.
ac766e 183     image = cv2.morphologyEx(image, cv2.MORPH_OPEN, kernel(3, 3), iterations=1)
SP 184
9efc18 185     # Skeletonization
762a5e 186     image = img_as_ubyte(morphology.thin(image > 128))
SP 187     cv2.imwrite("enSID1.png", image)
9efc18 188     # Stub removal (might not be necessary if thinning instead of skeletonize is used above
SP 189     # Making lines stronger
190     image = cv2.morphologyEx(image, cv2.MORPH_DILATE, kernel(5, 5), iterations=1)
191
192     image = cv2.morphologyEx(image, cv2.MORPH_CLOSE, kernel(10, 10))
193     # Thining again
762a5e 194     image = img_as_ubyte(morphology.skeletonize(image > 0.5))
9efc18 195     image = cv2.morphologyEx(image, cv2.MORPH_DILATE, kernel(10, 10))
5cb7c1 196     cv2.imwrite("enhancedSID.png", image)
762a5e 197     im2, ctrs, hier = cv2.findContours(
SP 198         image.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE
199     )
02e0f7 200     sorted_ctrs = sorted(ctrs, key=lambda ctr: cv2.boundingRect(ctr)[0])
SP 201
5cb7c1 202     print(len(sid_mask), len(sorted_ctrs))
SP 203     sid_no = segment_by_contours(
204         image, sorted_ctrs[1:], classifier
205     )  # we remove largest contour that surrounds whole image
02e0f7 206     print(sid_no)
d5c694 207     if len(sid_no) != len(sid_mask) or not sid_compare(sid_no,sid_mask):
5cb7c1 208         sid_warn.append("Trying second SID algorithm.")
d5c694 209         sid_no = segment_by_7segments(image, image_original, sid_mask, classifier)
SP 210     print(sid_no)
211     if(len(sid_no))!=len(sid_mask):
212         sid_no = segment_by_sid_len(image, image_original, sid_mask, classifier)
213         sid_warn.append("Trying third SID algorithm.")
214
215
216     if not sid_compare(sid_no, sid_mask):
217             sid_err=['Wrong SID!']
218
219     return (sid_no, sid_err, sid_warn)