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 sid_compare(sid_no, sid_mask):
|
for s,es in zip(sid_mask,sid_no):
|
if s!='x' and s!=es:
|
return False
|
return True
|
|
|
|
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, original_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
|
# Remove noise
|
image2 = cv2.morphologyEx(original_image, cv2.MORPH_OPEN, kernel(2, 2), iterations=7)
|
# find biggest block of pixels
|
image1 = cv2.morphologyEx(image2, cv2.MORPH_DILATE, kernel(5, 25), iterations=4)
|
image1=img_as_ubyte(image1>50)
|
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 - 40] #+25,-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 segment_by_7segments(image,original_image,sid_mask,classifier):
|
block_image = cv2.morphologyEx(original_image, cv2.MORPH_CLOSE, kernel(2, 2), iterations=10)
|
block_image =img_as_ubyte(block_image<50)
|
cv2.imwrite("sid_3rd1.png", block_image)
|
template = cv2.imread("template-8.png", 0)
|
w, h = template.shape[::-1]
|
res = cv2.matchTemplate(block_image, template, cv2.TM_CCOEFF_NORMED)
|
loc = np.where(res >= 0.75)
|
cimg = cv2.cvtColor(image, cv2.COLOR_GRAY2BGR)
|
loc_filtered_x=[]
|
loc_filtered_y=[]
|
for pt in zip(*loc[::-1]):
|
pt=(pt[0]-10,pt[1]-10)
|
loc_filtered_y.append(pt[1])
|
loc_filtered_x.append(pt[0])
|
# points.append(pt)
|
#filter points
|
if(len(loc_filtered_x)==0):
|
return ""
|
loc_filtered_x, loc_filtered_y = zip(
|
*sorted(zip(loc_filtered_x, loc_filtered_y))
|
)
|
a = np.diff(loc_filtered_x) > int(w/2)
|
a = np.append(a, True)
|
loc_filtered_x = np.array(loc_filtered_x)
|
loc_filtered_y = np.array(loc_filtered_y)
|
points = [loc_filtered_y[a], loc_filtered_x[a]]
|
for pt in zip(*points[::-1]):
|
cv2.rectangle(cimg, pt, (pt[0] + w, pt[1] + h), (0, 255, 255), 2)
|
cv2.imwrite("sid_3rd2.png", cimg)
|
|
sid_no=""
|
for i,pt in enumerate(zip(*points[::-1])):
|
num=image[pt[1]:pt[1] + h, pt[0]:pt[0]+w]
|
#cv2.imwrite("sid_3no_{}.png".format(i), num)
|
num = img_as_ubyte(num < 128)
|
try:
|
num = cv2.resize(num, (32, 32))
|
except:
|
return ""
|
cv2.imwrite("sid_3no_{}.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 = []
|
sid_err=[]
|
image = 255 - image
|
image_original=image.copy()
|
image = img_as_ubyte(image > 100)
|
cv2.imwrite("enSID0.png", image)
|
# Remove noise
|
image = cv2.morphologyEx(image, cv2.MORPH_OPEN, kernel(2, 2), iterations=3)
|
# 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])
|
|
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) or not sid_compare(sid_no,sid_mask):
|
sid_warn.append("Trying second SID algorithm.")
|
sid_no = segment_by_7segments(image, image_original, sid_mask, classifier)
|
print(sid_no)
|
if(len(sid_no))!=len(sid_mask):
|
sid_no = segment_by_sid_len(image, image_original, sid_mask, classifier)
|
sid_warn.append("Trying third SID algorithm.")
|
|
|
if not sid_compare(sid_no, sid_mask):
|
sid_err=['Wrong SID!']
|
|
return (sid_no, sid_err, sid_warn)
|