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 |
|
|
60 |
|
762a5e
|
61 |
def segment_by_contours(image, sorted_ctrs, classifier): |
SP |
62 |
sid_no = "" |
|
63 |
for i, ctr in enumerate(sorted_ctrs): |
|
64 |
# Get bounding box |
|
65 |
x, y, w, h = cv2.boundingRect(ctr) |
|
66 |
# Getting ROI |
|
67 |
if w < h / 2: |
|
68 |
sid_no = sid_no + "1" |
|
69 |
continue |
|
70 |
roi = image[y : y + h, x : x + w] |
|
71 |
roi = img_as_ubyte(roi < 128) |
|
72 |
roi = cv2.resize(roi, (32, 32)) |
|
73 |
|
|
74 |
# cv2.rectangle(image,(x,y),( x + w, y + h ),(0,255,0),2) |
|
75 |
cv2.imwrite("sid_no_{}.png".format(i), roi) |
|
76 |
sid_no = sid_no + str(classifier.predict(roi.reshape(1, -1) / 255.0)[0]) |
|
77 |
return sid_no |
|
78 |
|
|
79 |
|
5cb7c1
|
80 |
def segment_by_sid_len(image, sid_mask, classifier): |
SP |
81 |
sid_no = "" |
|
82 |
sid_len = len(sid_mask) |
|
83 |
if sid_mask[0] == "1": |
|
84 |
move_left = 45 |
|
85 |
elif sid_mask[0] == "x": |
|
86 |
move_left = 55 |
|
87 |
else: |
|
88 |
move_left = 0 |
|
89 |
# find biggest block of pixels |
ac766e
|
90 |
|
e2fa6a
|
91 |
image1 = cv2.morphologyEx(image, cv2.MORPH_DILATE, kernel(5, 25), iterations=4) |
5cb7c1
|
92 |
cv2.imwrite("sidblock1.png", image1) |
ac766e
|
93 |
im2, ctrs, hier = cv2.findContours( |
SP |
94 |
image1.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE |
|
95 |
) |
5cb7c1
|
96 |
sorted_ctrs = sorted( |
SP |
97 |
ctrs, key=lambda ctr: cv2.contourArea(ctr) |
|
98 |
) # get bigges contour |
ac766e
|
99 |
x, y, w, h = cv2.boundingRect(sorted_ctrs[-1]) |
5cb7c1
|
100 |
image = image[y : y + h, x + 25 - move_left : x + w - 25] |
SP |
101 |
cv2.imwrite("sidblock2.png", image) |
ac766e
|
102 |
imgHeight, imgWidth = image.shape[0:2] |
5cb7c1
|
103 |
numWidth = int(imgWidth / (sid_len)) |
SP |
104 |
for i in range(0, sid_len): |
|
105 |
num = image[:, i * numWidth : (i + 1) * numWidth] |
ac766e
|
106 |
num = img_as_ubyte(num < 128) |
SP |
107 |
num = cv2.resize(num, (32, 32)) |
|
108 |
|
|
109 |
# cv2.rectangle(image,(x,y),( x + w, y + h ),(0,255,0),2) |
|
110 |
cv2.imwrite("sid_no_{}.png".format(i), num) |
|
111 |
sid_no = sid_no + str(classifier.predict(num.reshape(1, -1) / 255.0)[0]) |
|
112 |
return sid_no |
|
113 |
|
|
114 |
|
762a5e
|
115 |
def getSID(image, classifier, sid_mask): |
5cb7c1
|
116 |
sid_warn = [] |
762a5e
|
117 |
image = 255 - image |
SP |
118 |
image = img_as_ubyte(image > 100) |
9efc18
|
119 |
cv2.imwrite("enSID0.png", image) |
SP |
120 |
# Remove noise |
762a5e
|
121 |
image = cv2.morphologyEx(image, cv2.MORPH_OPEN, kernel(2, 2), iterations=1) |
9efc18
|
122 |
# Closing. Connect non connected parts |
02e0f7
|
123 |
image = cv2.morphologyEx(image, cv2.MORPH_CLOSE, kernel(5, 3), iterations=4) |
9efc18
|
124 |
# Again noise removal after closing |
02e0f7
|
125 |
|
5cb7c1
|
126 |
# image = cv2.morphologyEx(image, cv2.MORPH_OPEN, kernel(8, 8), iterations=1) |
SP |
127 |
# don't do too much noise removal. |
ac766e
|
128 |
image = cv2.morphologyEx(image, cv2.MORPH_OPEN, kernel(3, 3), iterations=1) |
SP |
129 |
|
9efc18
|
130 |
# Skeletonization |
762a5e
|
131 |
image = img_as_ubyte(morphology.thin(image > 128)) |
SP |
132 |
cv2.imwrite("enSID1.png", image) |
9efc18
|
133 |
# Stub removal (might not be necessary if thinning instead of skeletonize is used above |
SP |
134 |
# Making lines stronger |
|
135 |
image = cv2.morphologyEx(image, cv2.MORPH_DILATE, kernel(5, 5), iterations=1) |
|
136 |
|
|
137 |
image = cv2.morphologyEx(image, cv2.MORPH_CLOSE, kernel(10, 10)) |
|
138 |
# Thining again |
762a5e
|
139 |
image = img_as_ubyte(morphology.skeletonize(image > 0.5)) |
9efc18
|
140 |
image = cv2.morphologyEx(image, cv2.MORPH_DILATE, kernel(10, 10)) |
5cb7c1
|
141 |
cv2.imwrite("enhancedSID.png", image) |
762a5e
|
142 |
im2, ctrs, hier = cv2.findContours( |
SP |
143 |
image.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE |
|
144 |
) |
02e0f7
|
145 |
sorted_ctrs = sorted(ctrs, key=lambda ctr: cv2.boundingRect(ctr)[0]) |
SP |
146 |
|
762a5e
|
147 |
sid_no = "" |
5cb7c1
|
148 |
print(len(sid_mask), len(sorted_ctrs)) |
SP |
149 |
sid_no = segment_by_contours( |
|
150 |
image, sorted_ctrs[1:], classifier |
|
151 |
) # we remove largest contour that surrounds whole image |
02e0f7
|
152 |
print(sid_no) |
5cb7c1
|
153 |
if len(sid_no) != len(sid_mask): |
SP |
154 |
#print("Ooops have to find another way") |
|
155 |
sid_warn.append("Trying second SID algorithm.") |
|
156 |
sid_no = segment_by_sid_len(image, sid_mask, classifier) |
|
157 |
return (sid_no, [], sid_warn) |