From 6fde5fba87aaf2f2ad8c4af08a59454f06d8dc30 Mon Sep 17 00:00:00 2001
From: Samo Penic <samo.penic@gmail.com>
Date: Sat, 17 Nov 2018 12:28:39 +0000
Subject: [PATCH] Improving the robustness of all three algorithms. Again...
---
aoiOcr.py | 8 +-
sid_process.py | 226 +++++++++++++++++++++++++++++---------------------------
2 files changed, 120 insertions(+), 114 deletions(-)
diff --git a/aoiOcr.py b/aoiOcr.py
index bb74503..74c7b15 100644
--- a/aoiOcr.py
+++ b/aoiOcr.py
@@ -6,8 +6,8 @@
settings = {"sid_mask": "64xx0xxx", "answer_treshold": 0.25}
classifier = joblib.load("filename.joblib")
-# p = Paper(filename="testpage300dpi_scan1.png")
-#p=Paper(filename='sizif111.tif', sid_classifier=classifier, settings={"sid_mask": "11xx0xxx", "answer_treshold": 0.25})
+#p = Paper(filename="testpage300dpi_scan1.png")
+p=Paper(filename='sizif111.tif', sid_classifier=classifier, settings={"sid_mask": "11xx0xxx", "answer_treshold": 0.25})
#p=Paper(filename='processed_scans/20141016095134535_0006.tif', sid_classifier=classifier, settings=settings)
#p = Paper(filename="processed_scans/20151111080408825_0001.tif",sid_classifier=classifier,settings=settings,)
#p=Paper(filename='processed_scans/20151028145444607_0028.tif', sid_classifier=classifier, settings=settings)
@@ -20,7 +20,7 @@
"processed_scans/20141021095744144_0009.tif",
"processed_scans/20141028095553745_0018.tif",
]
-p=Paper(filename=pa[6], sid_classifier=classifier, settings=settings)
+#p=Paper(filename=pa[6], sid_classifier=classifier, settings=settings)
# print(p.QRData)
# print(p.errors)
@@ -33,7 +33,7 @@
print(p.get_paper_ocr_data())
-
+exit(0)
filelist = glob("processed_scans/*.tif")
for f in sorted(filelist):
print("processing: {}".format(f))
diff --git a/sid_process.py b/sid_process.py
index 1f93d3c..ab8aaa3 100644
--- a/sid_process.py
+++ b/sid_process.py
@@ -3,70 +3,68 @@
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):
+ """
+ Function greates square kernel of size x and y
+ """
return np.ones((x, y), np.uint8)
+
+def find_biggest_blob(image, original_image,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
+ return image
+
def sid_compare(sid_no, sid_mask):
- for s,es in zip(sid_mask,sid_no):
- if s!='x' and s!=es:
+ """
+ Function compares student id number with student id mask if the recognised number is valid according to the mask
+ :param sid_no:
+ :param sid_mask:
+ :return: True if they match, else False
+ """
+ for s, es in zip(sid_mask, sid_no):
+ if s != "x" and s != es:
return False
return True
+def segment_by_contours(image, original_image, classifier,sid_mask):
+ """
+ First algorithm. it segments numerals with contours. It works with numbers where individual numerals does not touch.
+ :param image:
+ :param original_image:
+ :param classifier:
+ :return: student id as a string
+ """
-def segment_by_contours(image, sorted_ctrs, classifier):
sid_no = ""
+ image=find_biggest_blob(image,original_image,sid_mask)
+ cv2.imwrite("sid_contour1.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])
+
for i, ctr in enumerate(sorted_ctrs):
# Get bounding box
x, y, w, h = cv2.boundingRect(ctr)
@@ -85,28 +83,19 @@
def segment_by_sid_len(image, original_image, sid_mask, classifier):
+ """
+ Third algorithm. It trys to get biggest "blob" in the image and then it cuts it into individual numbers by force.
+ It has some problems with finding individual numbers, so some tweaking must be done!
+
+ :param image:
+ :param original_image:
+ :param sid_mask:
+ :param classifier:
+ :return: student id as a string
+ """
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
+ image=find_biggest_blob(image,original_image,sid_mask)
cv2.imwrite("sidblock2.png", image)
imgHeight, imgWidth = image.shape[0:2]
numWidth = int(imgWidth / (sid_len))
@@ -114,35 +103,44 @@
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)
+
+def segment_by_7segments(image, original_image, sid_mask, classifier):
+ """
+ Second attempt. It dilates the image to get all 7 segments wisible as 8888888 then it does pattern matching of 8 with
+ pattern image. It works if the scaned gray level is high enough.
+
+ :param image:
+ :param original_image:
+ :param sid_mask:
+ :param classifier:
+ :return: student id number as a string
+ """
+ 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=[]
+ loc_filtered_x = []
+ loc_filtered_y = []
for pt in zip(*loc[::-1]):
- pt=(pt[0]-10,pt[1]-10)
+ 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):
+ # 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)
+ 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)
@@ -151,10 +149,10 @@
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)
+ 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))
@@ -165,19 +163,34 @@
return sid_no
+
def getSID(image, classifier, sid_mask):
+ """
+ Tries different approaches on image to get student id number. Firstly clears image of noise and then skeletonizes
+ numbers and thickens it until it gets normalized image. It sends it to the segmentation and recognition functions.
+
+ Tweak both MORPH_OPEN lines....
+
+ :param image:
+ :param classifier:
+ :param sid_mask:
+ :return: (student_id, error, warning) student id as a string, list of errors and list of warnings during the recognition
+
+ """
sid_warn = []
- sid_err=[]
+ sid_err = []
image = 255 - image
- image_original=image.copy()
+ 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
+ # 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)
@@ -185,35 +198,28 @@
# 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
+ sid_no = segment_by_contours(image, image_original, classifier, sid_mask)
print(sid_no)
- if len(sid_no) != len(sid_mask) or not sid_compare(sid_no,sid_mask):
+ 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):
+ 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!']
+ sid_err = ["Wrong SID!"]
- return (sid_no, sid_err, sid_warn)
+ return sid_no, sid_err, sid_warn
--
Gitblit v1.8.0