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