Revert to older deskew slope calculation — pairing between skewed and original contours was incorrect, so the original pairing logic has been restored. Also restored some original functions to ensure correct reading order detection.

This commit is contained in:
vahidrezanezhad 2025-11-12 18:24:50 +01:00
parent 19b2c3fa42
commit e60b0e5911
3 changed files with 941 additions and 1062 deletions

View file

@ -2482,119 +2482,150 @@ class Eynollah:
self, contours_only_text_parent, contours_only_text_parent_h, boxes, textline_mask_tot): self, contours_only_text_parent, contours_only_text_parent_h, boxes, textline_mask_tot):
self.logger.debug("enter do_order_of_regions") self.logger.debug("enter do_order_of_regions")
contours_only_text_parent = np.array(contours_only_text_parent)
contours_only_text_parent_h = np.array(contours_only_text_parent_h)
boxes = np.array(boxes, dtype=int) # to be on the safe side boxes = np.array(boxes, dtype=int) # to be on the safe side
c_boxes = np.stack((0.5 * boxes[:, 2:4].sum(axis=1), cx_text_only, cy_text_only, x_min_text_only, x_max_text_only, _, _, y_cor_x_min_main = find_new_features_of_contours(
0.5 * boxes[:, 0:2].sum(axis=1)))
cx_main, cy_main, mx_main, Mx_main, my_main, My_main, mxy_main = find_new_features_of_contours(
contours_only_text_parent) contours_only_text_parent)
cx_head, cy_head, mx_head, Mx_head, my_head, My_head, mxy_head = find_new_features_of_contours( cx_text_only_h, cy_text_only_h, x_min_text_only_h, x_max_text_only_h, _, _, y_cor_x_min_main_h = find_new_features_of_contours(
contours_only_text_parent_h) contours_only_text_parent_h)
def match_boxes(only_centers: bool): def match_boxes(only_centers: bool):
arg_text_con_main = np.zeros(len(contours_only_text_parent), dtype=int) arg_text_con = []
for ii in range(len(contours_only_text_parent)): for ii in range(len(cx_text_only)):
check_if_textregion_located_in_a_box = False check_if_textregion_located_in_a_box = False
for jj, box in enumerate(boxes): for jj in range(len(boxes)):
if ((cx_main[ii] >= box[0] and if self.right2left:
cx_main[ii] < box[1] and if ((x_max_text_only[ii] - 80 >= boxes[jj][0] and
cy_main[ii] >= box[2] and x_max_text_only[ii] - 80 < boxes[jj][1] and
cy_main[ii] < box[3]) if only_centers else y_cor_x_min_main[ii] >= boxes[jj][2] and
(mx_main[ii] >= box[0] and y_cor_x_min_main[ii] < boxes[jj][3]) if only_centers else
Mx_main[ii] < box[1] and (cx_text_only[ii] >= boxes[jj][0] and
my_main[ii] >= box[2] and cx_text_only[ii] < boxes[jj][1] and
My_main[ii] < box[3])): cy_text_only[ii] >= boxes[jj][2] and
arg_text_con_main[ii] = jj cy_text_only[ii] < boxes[jj][3])):
arg_text_con.append(jj)
check_if_textregion_located_in_a_box = True
break
else:
if ((x_min_text_only[ii] + 80 >= boxes[jj][0] and
x_min_text_only[ii] + 80 < boxes[jj][1] and
y_cor_x_min_main[ii] >= boxes[jj][2] and
y_cor_x_min_main[ii] < boxes[jj][3]) if only_centers else
(cx_text_only[ii] >= boxes[jj][0] and
cx_text_only[ii] < boxes[jj][1] and
cy_text_only[ii] >= boxes[jj][2] and
cy_text_only[ii] < boxes[jj][3])):
arg_text_con.append(jj)
check_if_textregion_located_in_a_box = True check_if_textregion_located_in_a_box = True
#print("main/matched", (mx_main[ii], Mx_main[ii], my_main[ii], My_main[ii]), "\tin", box, only_centers)
break break
if not check_if_textregion_located_in_a_box: if not check_if_textregion_located_in_a_box:
dists_tr_from_box = np.linalg.norm(c_boxes - np.array([[cy_main[ii]], [cx_main[ii]]]), axis=0) dists_tr_from_box = [math.sqrt((cx_text_only[ii] - boxes[jj][1]) ** 2 +
pcontained_in_box = ((boxes[:, 2] <= cy_main[ii]) & (cy_main[ii] < boxes[:, 3]) & (cy_text_only[ii] - boxes[jj][2]) ** 2)
(boxes[:, 0] <= cx_main[ii]) & (cx_main[ii] < boxes[:, 1])) for jj in range(len(boxes))]
ind_min = np.argmin(np.ma.masked_array(dists_tr_from_box, ~pcontained_in_box)) ind_min = np.argmin(dists_tr_from_box)
arg_text_con_main[ii] = ind_min arg_text_con.append(ind_min)
#print("main/fallback", (mx_main[ii], Mx_main[ii], my_main[ii], My_main[ii]), "\tin", boxes[ind_min], only_centers) args_contours = np.array(range(len(arg_text_con)))
args_contours_main = np.arange(len(contours_only_text_parent)) arg_text_con_h = []
order_by_con_main = np.zeros_like(arg_text_con_main) for ii in range(len(cx_text_only_h)):
check_if_textregion_located_in_a_box = False
for jj in range(len(boxes)):
if self.right2left:
if ((x_max_text_only_h[ii] - 80 >= boxes[jj][0] and
x_max_text_only_h[ii] - 80 < boxes[jj][1] and
y_cor_x_min_main_h[ii] >= boxes[jj][2] and
y_cor_x_min_main_h[ii] < boxes[jj][3]) if only_centers else
(cx_text_only_h[ii] >= boxes[jj][0] and
cx_text_only_h[ii] < boxes[jj][1] and
cy_text_only_h[ii] >= boxes[jj][2] and
cy_text_only_h[ii] < boxes[jj][3])):
arg_text_con_h.append(jj)
check_if_textregion_located_in_a_box = True
break
else:
if ((x_min_text_only_h[ii] + 80 >= boxes[jj][0] and
x_min_text_only_h[ii] + 80 < boxes[jj][1] and
y_cor_x_min_main_h[ii] >= boxes[jj][2] and
y_cor_x_min_main_h[ii] < boxes[jj][3]) if only_centers else
(cx_text_only_h[ii] >= boxes[jj][0] and
cx_text_only_h[ii] < boxes[jj][1] and
cy_text_only_h[ii] >= boxes[jj][2] and
cy_text_only_h[ii] < boxes[jj][3])):
arg_text_con_h.append(jj)
check_if_textregion_located_in_a_box = True
break
if not check_if_textregion_located_in_a_box:
dists_tr_from_box = [math.sqrt((cx_text_only_h[ii] - boxes[jj][1]) ** 2 +
(cy_text_only_h[ii] - boxes[jj][2]) ** 2)
for jj in range(len(boxes))]
ind_min = np.argmin(dists_tr_from_box)
arg_text_con_h.append(ind_min)
args_contours_h = np.array(range(len(arg_text_con_h)))
arg_text_con_head = np.zeros(len(contours_only_text_parent_h), dtype=int) order_by_con_head = np.zeros(len(arg_text_con_h))
for ii in range(len(contours_only_text_parent_h)): order_by_con_main = np.zeros(len(arg_text_con))
check_if_textregion_located_in_a_box = False
for jj, box in enumerate(boxes):
if ((cx_head[ii] >= box[0] and
cx_head[ii] < box[1] and
cy_head[ii] >= box[2] and
cy_head[ii] < box[3]) if only_centers else
(mx_head[ii] >= box[0] and
Mx_head[ii] < box[1] and
my_head[ii] >= box[2] and
My_head[ii] < box[3])):
arg_text_con_head[ii] = jj
check_if_textregion_located_in_a_box = True
#print("head/matched", (mx_head[ii], Mx_head[ii], my_head[ii], My_head[ii]), "\tin", box, only_centers)
break
if not check_if_textregion_located_in_a_box:
dists_tr_from_box = np.linalg.norm(c_boxes - np.array([[cy_head[ii]], [cx_head[ii]]]), axis=0)
pcontained_in_box = ((boxes[:, 2] <= cy_head[ii]) & (cy_head[ii] < boxes[:, 3]) &
(boxes[:, 0] <= cx_head[ii]) & (cx_head[ii] < boxes[:, 1]))
ind_min = np.argmin(np.ma.masked_array(dists_tr_from_box, ~pcontained_in_box))
arg_text_con_head[ii] = ind_min
#print("head/fallback", (mx_head[ii], Mx_head[ii], my_head[ii], My_head[ii]), "\tin", boxes[ind_min], only_centers)
args_contours_head = np.arange(len(contours_only_text_parent_h))
order_by_con_head = np.zeros_like(arg_text_con_head)
ref_point = 0 ref_point = 0
order_of_texts_tot = [] order_of_texts_tot = []
id_of_texts_tot = [] id_of_texts_tot = []
for iij, box in enumerate(boxes): for iij in range(len(boxes)):
ys = slice(*box[2:4]) ys = slice(*boxes[iij][2:4])
xs = slice(*box[0:2]) xs = slice(*boxes[iij][0:2])
args_contours_box_main = args_contours_main[arg_text_con_main == iij] args_contours_box = args_contours[np.array(arg_text_con) == iij]
args_contours_box_head = args_contours_head[arg_text_con_head == iij] args_contours_box_h = args_contours_h[np.array(arg_text_con_h) == iij]
con_inter_box = contours_only_text_parent[args_contours_box_main] con_inter_box = []
con_inter_box_h = contours_only_text_parent_h[args_contours_box_head] con_inter_box_h = []
for box in args_contours_box:
con_inter_box.append(contours_only_text_parent[box])
for box in args_contours_box_h:
con_inter_box_h.append(contours_only_text_parent_h[box])
indexes_sorted, kind_of_texts_sorted, index_by_kind_sorted = order_of_regions( indexes_sorted, kind_of_texts_sorted, index_by_kind_sorted = order_of_regions(
textline_mask_tot[ys, xs], con_inter_box, con_inter_box_h, box[2], box[0]) textline_mask_tot[ys, xs], con_inter_box, con_inter_box_h, boxes[iij][2])
order_of_texts, id_of_texts = order_and_id_of_texts( order_of_texts, id_of_texts = order_and_id_of_texts(
con_inter_box, con_inter_box_h, con_inter_box, con_inter_box_h,
indexes_sorted, index_by_kind_sorted, kind_of_texts_sorted, ref_point) indexes_sorted, index_by_kind_sorted, kind_of_texts_sorted, ref_point)
indexes_sorted_main = indexes_sorted[kind_of_texts_sorted == 1] indexes_sorted_main = np.array(indexes_sorted)[np.array(kind_of_texts_sorted) == 1]
indexes_by_type_main = index_by_kind_sorted[kind_of_texts_sorted == 1] indexes_by_type_main = np.array(index_by_kind_sorted)[np.array(kind_of_texts_sorted) == 1]
indexes_sorted_head = indexes_sorted[kind_of_texts_sorted == 2] indexes_sorted_head = np.array(indexes_sorted)[np.array(kind_of_texts_sorted) == 2]
indexes_by_type_head = index_by_kind_sorted[kind_of_texts_sorted == 2] indexes_by_type_head = np.array(index_by_kind_sorted)[np.array(kind_of_texts_sorted) == 2]
for zahler, _ in enumerate(args_contours_box_main): for zahler, _ in enumerate(args_contours_box):
arg_order_v = indexes_sorted_main[zahler] arg_order_v = indexes_sorted_main[zahler]
order_by_con_main[args_contours_box_main[indexes_by_type_main[zahler]]] = \ order_by_con_main[args_contours_box[indexes_by_type_main[zahler]]] = \
np.flatnonzero(indexes_sorted == arg_order_v) + ref_point np.where(indexes_sorted == arg_order_v)[0][0] + ref_point
for zahler, _ in enumerate(args_contours_box_head): for zahler, _ in enumerate(args_contours_box_h):
arg_order_v = indexes_sorted_head[zahler] arg_order_v = indexes_sorted_head[zahler]
order_by_con_head[args_contours_box_head[indexes_by_type_head[zahler]]] = \ order_by_con_head[args_contours_box_h[indexes_by_type_head[zahler]]] = \
np.flatnonzero(indexes_sorted == arg_order_v) + ref_point np.where(indexes_sorted == arg_order_v)[0][0] + ref_point
for jji in range(len(id_of_texts)): for jji in range(len(id_of_texts)):
order_of_texts_tot.append(order_of_texts[jji] + ref_point) order_of_texts_tot.append(order_of_texts[jji] + ref_point)
id_of_texts_tot.append(id_of_texts[jji]) id_of_texts_tot.append(id_of_texts[jji])
ref_point += len(id_of_texts) ref_point += len(id_of_texts)
order_of_texts_tot = np.concatenate((order_by_con_main, order_of_texts_tot = []
order_by_con_head)) for tj1 in range(len(contours_only_text_parent)):
order_text_new = np.argsort(order_of_texts_tot) order_of_texts_tot.append(int(order_by_con_main[tj1]))
for tj1 in range(len(contours_only_text_parent_h)):
order_of_texts_tot.append(int(order_by_con_head[tj1]))
order_text_new = []
for iii in range(len(order_of_texts_tot)):
order_text_new.append(np.where(np.array(order_of_texts_tot) == iii)[0][0])
return order_text_new, id_of_texts_tot return order_text_new, id_of_texts_tot
try: try:
results = match_boxes(False) results = match_boxes(True)
except Exception as why: except Exception as why:
self.logger.exception(why) self.logger.exception(why)
results = match_boxes(True) results = match_boxes(False)
self.logger.debug("exit do_order_of_regions_full_layout")
self.logger.debug("exit do_order_of_regions")
return results return results
def check_iou_of_bounding_box_and_contour_for_tables( def check_iou_of_bounding_box_and_contour_for_tables(
@ -3088,7 +3119,7 @@ class Eynollah:
def run_deskew(self, textline_mask_tot_ea): def run_deskew(self, textline_mask_tot_ea):
#print(textline_mask_tot_ea.shape, 'textline_mask_tot_ea deskew') #print(textline_mask_tot_ea.shape, 'textline_mask_tot_ea deskew')
slope_deskew = return_deskew_slop(cv2.erode(textline_mask_tot_ea, KERNEL, iterations=2), 2, 30, True, slope_deskew = return_deskew_slop(cv2.erode(textline_mask_tot_ea, KERNEL, iterations=2), 2, 30, True,
map=self.executor.map, logger=self.logger, plotter=self.plotter) logger=self.logger, plotter=self.plotter)
if self.plotter: if self.plotter:
self.plotter.save_deskewed_image(slope_deskew) self.plotter.save_deskewed_image(slope_deskew)
self.logger.info("slope_deskew: %.2f°", slope_deskew) self.logger.info("slope_deskew: %.2f°", slope_deskew)
@ -4419,130 +4450,89 @@ class Eynollah:
###min_con_area = 0.000005 ###min_con_area = 0.000005
contours_only_text, hir_on_text = return_contours_of_image(text_only) contours_only_text, hir_on_text = return_contours_of_image(text_only)
contours_only_text_parent = return_parent_contours(contours_only_text, hir_on_text) contours_only_text_parent = return_parent_contours(contours_only_text, hir_on_text)
contours_only_text_parent_d_ordered = []
contours_only_text_parent_d = []
if len(contours_only_text_parent) > 0: if len(contours_only_text_parent) > 0:
areas_tot_text = np.prod(text_only.shape)
areas_cnt_text = np.array([cv2.contourArea(c) for c in contours_only_text_parent]) areas_cnt_text = np.array([cv2.contourArea(c) for c in contours_only_text_parent])
areas_cnt_text = areas_cnt_text / float(areas_tot_text) areas_cnt_text = areas_cnt_text / float(text_only.shape[0] * text_only.shape[1])
#self.logger.info('areas_cnt_text %s', areas_cnt_text) contours_biggest = contours_only_text_parent[np.argmax(areas_cnt_text)]
contours_only_text_parent = np.array(contours_only_text_parent)[areas_cnt_text > MIN_AREA_REGION] contours_only_text_parent = [c for jz, c in enumerate(contours_only_text_parent)
areas_cnt_text_parent = areas_cnt_text[areas_cnt_text > MIN_AREA_REGION] if areas_cnt_text[jz] > MIN_AREA_REGION]
areas_cnt_text_parent = [area for area in areas_cnt_text if area > MIN_AREA_REGION]
index_con_parents = np.argsort(areas_cnt_text_parent) index_con_parents = np.argsort(areas_cnt_text_parent)
contours_only_text_parent = contours_only_text_parent[index_con_parents]
areas_cnt_text_parent = areas_cnt_text_parent[index_con_parents]
centers = np.stack(find_center_of_contours(contours_only_text_parent)) # [2, N] contours_only_text_parent = self.return_list_of_contours_with_desired_order(
contours_only_text_parent, index_con_parents)
center0 = centers[:, -1:] # [2, 1] areas_cnt_text_parent = self.return_list_of_contours_with_desired_order(
areas_cnt_text_parent, index_con_parents)
cx_bigest_big, cy_biggest_big, _, _, _, _, _ = find_new_features_of_contours([contours_biggest])
cx_bigest, cy_biggest, _, _, _, _, _ = find_new_features_of_contours(contours_only_text_parent)
if np.abs(slope_deskew) >= SLOPE_THRESHOLD: if np.abs(slope_deskew) >= SLOPE_THRESHOLD:
contours_only_text_d, hir_on_text_d = return_contours_of_image(text_only_d) contours_only_text_d, hir_on_text_d = return_contours_of_image(text_only_d)
contours_only_text_parent_d = return_parent_contours(contours_only_text_d, hir_on_text_d) contours_only_text_parent_d = return_parent_contours(contours_only_text_d, hir_on_text_d)
areas_tot_text_d = np.prod(text_only_d.shape)
areas_cnt_text_d = np.array([cv2.contourArea(c) for c in contours_only_text_parent_d]) areas_cnt_text_d = np.array([cv2.contourArea(c) for c in contours_only_text_parent_d])
areas_cnt_text_d = areas_cnt_text_d / float(areas_tot_text_d) areas_cnt_text_d = areas_cnt_text_d / float(text_only_d.shape[0] * text_only_d.shape[1])
contours_only_text_parent_d = np.array(contours_only_text_parent_d)[areas_cnt_text_d > MIN_AREA_REGION] if len(areas_cnt_text_d)>0:
areas_cnt_text_d = areas_cnt_text_d[areas_cnt_text_d > MIN_AREA_REGION] contours_biggest_d = contours_only_text_parent_d[np.argmax(areas_cnt_text_d)]
if len(contours_only_text_parent_d):
index_con_parents_d = np.argsort(areas_cnt_text_d) index_con_parents_d = np.argsort(areas_cnt_text_d)
contours_only_text_parent_d = np.array(contours_only_text_parent_d)[index_con_parents_d] contours_only_text_parent_d = self.return_list_of_contours_with_desired_order(
areas_cnt_text_d = areas_cnt_text_d[index_con_parents_d] contours_only_text_parent_d, index_con_parents_d)
centers_d = np.stack(find_center_of_contours(contours_only_text_parent_d)) # [2, N] areas_cnt_text_d = self.return_list_of_contours_with_desired_order(
areas_cnt_text_d, index_con_parents_d)
center0_d = centers_d[:, -1:].copy() # [2, 1] cx_bigest_d_big, cy_biggest_d_big, _, _, _, _, _ = find_new_features_of_contours([contours_biggest_d])
cx_bigest_d, cy_biggest_d, _, _, _, _, _ = find_new_features_of_contours(contours_only_text_parent_d)
try:
if len(cx_bigest_d) >= 5:
cx_bigest_d_last5 = cx_bigest_d[-5:]
cy_biggest_d_last5 = cy_biggest_d[-5:]
dists_d = [math.sqrt((cx_bigest_big[0] - cx_bigest_d_last5[j]) ** 2 +
(cy_biggest_big[0] - cy_biggest_d_last5[j]) ** 2)
for j in range(len(cy_biggest_d_last5))]
ind_largest = len(cx_bigest_d) -5 + np.argmin(dists_d)
else:
cx_bigest_d_last5 = cx_bigest_d[-len(cx_bigest_d):]
cy_biggest_d_last5 = cy_biggest_d[-len(cx_bigest_d):]
dists_d = [math.sqrt((cx_bigest_big[0]-cx_bigest_d_last5[j])**2 +
(cy_biggest_big[0]-cy_biggest_d_last5[j])**2)
for j in range(len(cy_biggest_d_last5))]
ind_largest = len(cx_bigest_d) - len(cx_bigest_d) + np.argmin(dists_d)
# find the largest among the largest 5 deskewed contours cx_bigest_d_big[0] = cx_bigest_d[ind_largest]
# that is also closest to the largest original contour cy_biggest_d_big[0] = cy_biggest_d[ind_largest]
last5_centers_d = centers_d[:, -5:] except Exception as why:
dists_d = np.linalg.norm(center0 - last5_centers_d, axis=0) self.logger.error(str(why))
ind_largest = len(contours_only_text_parent_d) - last5_centers_d.shape[1] + np.argmin(dists_d)
center0_d[:, 0] = centers_d[:, ind_largest]
# order new contours the same way as the undeskewed contours
# (by calculating the offset of the largest contours, respectively,
# of the new and undeskewed image; then for each contour,
# finding the closest new contour, with proximity calculated
# as distance of their centers modulo offset vector)
(h, w) = text_only.shape[:2] (h, w) = text_only.shape[:2]
center = (w // 2.0, h // 2.0) center = (w // 2.0, h // 2.0)
M = cv2.getRotationMatrix2D(center, slope_deskew, 1.0) M = cv2.getRotationMatrix2D(center, slope_deskew, 1.0)
M_22 = np.array(M)[:2, :2] M_22 = np.array(M)[:2, :2]
center0 = np.dot(M_22, center0) # [2, 1] p_big = np.dot(M_22, [cx_bigest_big, cy_biggest_big])
offset = center0 - center0_d # [2, 1] x_diff = p_big[0] - cx_bigest_d_big
y_diff = p_big[1] - cy_biggest_d_big
centers = np.dot(M_22, centers) - offset # [2,N] contours_only_text_parent_d_ordered = []
# add dimension for area (so only contours of similar size will be considered close) for i in range(len(contours_only_text_parent)):
centers = np.append(centers, areas_cnt_text_parent[np.newaxis], axis=0) p = np.dot(M_22, [cx_bigest[i], cy_biggest[i]])
centers_d = np.append(centers_d, areas_cnt_text_d[np.newaxis], axis=0) p[0] = p[0] - x_diff[0]
p[1] = p[1] - y_diff[0]
dists = [math.sqrt((p[0] - cx_bigest_d[j]) ** 2 +
(p[1] - cy_biggest_d[j]) ** 2)
for j in range(len(cx_bigest_d))]
contours_only_text_parent_d_ordered.append(contours_only_text_parent_d[np.argmin(dists)])
else:
contours_only_text_parent_d_ordered = []
contours_only_text_parent_d = []
contours_only_text_parent = []
dists = np.zeros((len(contours_only_text_parent), len(contours_only_text_parent_d))) else:
for i in range(len(contours_only_text_parent)): contours_only_text_parent_d_ordered = []
dists[i] = np.linalg.norm(centers[:, i:i + 1] - centers_d, axis=0) contours_only_text_parent_d = []
corresp = np.zeros(dists.shape, dtype=bool)
# keep searching next-closest until at least one correspondence on each side
while not np.all(corresp.sum(axis=1)) and not np.all(corresp.sum(axis=0)):
idx = np.nanargmin(dists)
i, j = np.unravel_index(idx, dists.shape)
dists[i, j] = np.nan
corresp[i, j] = True
#print("original/deskewed adjacency", corresp.nonzero())
contours_only_text_parent_d_ordered = np.zeros_like(contours_only_text_parent)
contours_only_text_parent_d_ordered = contours_only_text_parent_d[np.argmax(corresp, axis=1)]
# img1 = np.zeros(text_only_d.shape[:2], dtype=np.uint8)
# for i in range(len(contours_only_text_parent)):
# cv2.fillPoly(img1, pts=[contours_only_text_parent_d_ordered[i]], color=i + 1)
# plt.subplot(2, 2, 1, title="direct corresp contours")
# plt.imshow(img1)
# img2 = np.zeros(text_only_d.shape[:2], dtype=np.uint8)
# join deskewed regions mapping to single original ones
for i in range(len(contours_only_text_parent)):
if np.count_nonzero(corresp[i]) > 1:
indices = np.flatnonzero(corresp[i])
#print("joining", indices)
polygons_d = [contour2polygon(contour)
for contour in contours_only_text_parent_d[indices]]
contour_d = polygon2contour(join_polygons(polygons_d))
contours_only_text_parent_d_ordered[i] = contour_d
# cv2.fillPoly(img2, pts=[contour_d], color=i + 1)
# plt.subplot(2, 2, 3, title="joined contours")
# plt.imshow(img2)
# img3 = np.zeros(text_only_d.shape[:2], dtype=np.uint8)
# split deskewed regions mapping to multiple original ones
def deskew(polygon):
polygon = shapely.affinity.rotate(polygon, -slope_deskew, origin=center)
polygon = shapely.affinity.translate(polygon, *offset.squeeze())
return polygon
for j in range(len(contours_only_text_parent_d)):
if np.count_nonzero(corresp[:, j]) > 1:
indices = np.flatnonzero(corresp[:, j])
#print("splitting along", indices)
polygons = [deskew(contour2polygon(contour))
for contour in contours_only_text_parent[indices]]
polygon_d = contour2polygon(contours_only_text_parent_d[j])
polygons_d = [make_intersection(polygon_d, polygon)
for polygon in polygons]
# ignore where there is no actual overlap
indices = indices[np.flatnonzero(polygons_d)]
contours_d = [polygon2contour(polygon_d)
for polygon_d in polygons_d
if polygon_d]
contours_only_text_parent_d_ordered[indices] = contours_d
# cv2.fillPoly(img3, pts=contours_d, color=j + 1)
# plt.subplot(2, 2, 4, title="split contours")
# plt.imshow(img3)
# img4 = np.zeros(text_only_d.shape[:2], dtype=np.uint8)
# for i in range(len(contours_only_text_parent)):
# cv2.fillPoly(img4, pts=[contours_only_text_parent_d_ordered[i]], color=i + 1)
# plt.subplot(2, 2, 2, title="result contours")
# plt.imshow(img4)
# plt.show()
if not len(contours_only_text_parent): if not len(contours_only_text_parent):
# stop early # stop early

File diff suppressed because it is too large Load diff

View file

@ -1497,21 +1497,23 @@ def separate_lines_new2(img_crop, thetha, num_col, slope_region, logger=None, pl
return img_patch_interest_revised return img_patch_interest_revised
@wrap_ndarray_shared(kw='img') def do_image_rotation(queue_of_all_params,angles_per_process, img_resized, sigma_des):
def do_image_rotation(angle, img=None, sigma_des=1.0, logger=None): vars_per_each_subprocess = []
if logger is None: angles_per_each_subprocess = []
logger = getLogger(__package__) for mv in range(len(angles_per_process)):
img_rot = rotate_image(img, angle) img_rot=rotate_image(img_resized,angles_per_process[mv])
img_rot[img_rot!=0]=1 img_rot[img_rot!=0]=1
try: try:
var = find_num_col_deskew(img_rot, sigma_des, 20.3) var_spectrum=find_num_col_deskew(img_rot,sigma_des,20.3 )
except: except:
logger.exception("cannot determine variance for angle %.2f°", angle) var_spectrum=0
var = 0 vars_per_each_subprocess.append(var_spectrum)
return var angles_per_each_subprocess.append(angles_per_process[mv])
queue_of_all_params.put([vars_per_each_subprocess, angles_per_each_subprocess])
def return_deskew_slop(img_patch_org, sigma_des,n_tot_angles=100, def return_deskew_slop(img_patch_org, sigma_des,n_tot_angles=100,
main_page=False, logger=None, plotter=None, map=None): main_page=False, logger=None, plotter=None):
if main_page and plotter: if main_page and plotter:
plotter.save_plot_of_textline_density(img_patch_org) plotter.save_plot_of_textline_density(img_patch_org)
@ -1524,71 +1526,76 @@ def return_deskew_slop(img_patch_org, sigma_des,n_tot_angles=100,
onset_x=int((img_resized.shape[1]-img_int.shape[1])/2.) onset_x=int((img_resized.shape[1]-img_int.shape[1])/2.)
onset_y=int((img_resized.shape[0]-img_int.shape[0])/2.) onset_y=int((img_resized.shape[0]-img_int.shape[0])/2.)
#img_resized=np.zeros((int( img_int.shape[0]*(1.8) ) , int( img_int.shape[1]*(2.6) ) ))
#img_resized[ int( img_int.shape[0]*(.4)):int( img_int.shape[0]*(.4))+img_int.shape[0],
# int( img_int.shape[1]*(.8)):int( img_int.shape[1]*(.8))+img_int.shape[1] ]=img_int[:,:]
img_resized[ onset_y:onset_y+img_int.shape[0] , onset_x:onset_x+img_int.shape[1] ]=img_int[:,:] img_resized[ onset_y:onset_y+img_int.shape[0] , onset_x:onset_x+img_int.shape[1] ]=img_int[:,:]
if main_page and img_patch_org.shape[1] > img_patch_org.shape[0]: if main_page and img_patch_org.shape[1] > img_patch_org.shape[0]:
angles = np.array([-45, 0, 45, 90,]) angles = np.array([-45, 0, 45, 90,])
angle, _ = get_smallest_skew(img_resized, sigma_des, angles, map=map, logger=logger, plotter=plotter) angle = get_smallest_skew(img_resized, sigma_des, angles, plotter=plotter)
angles = np.linspace(angle - 22.5, angle + 22.5, n_tot_angles) angles = np.linspace(angle - 22.5, angle + 22.5, n_tot_angles)
angle, _ = get_smallest_skew(img_resized, sigma_des, angles, map=map, logger=logger, plotter=plotter) angle = get_smallest_skew(img_resized, sigma_des, angles, plotter=plotter)
elif main_page: elif main_page:
#angles = np.linspace(-12, 12, n_tot_angles)#np.array([0 , 45 , 90 , -45]) angles = np.linspace(-12, 12, n_tot_angles)#np.array([0 , 45 , 90 , -45])
angles = np.concatenate((np.linspace(-12, -7, n_tot_angles // 4), angle = get_smallest_skew(img_resized, sigma_des, angles, plotter=plotter)
np.linspace(-6, 6, n_tot_angles // 2),
np.linspace(7, 12, n_tot_angles // 4)))
angle, var = get_smallest_skew(img_resized, sigma_des, angles, map=map, logger=logger, plotter=plotter)
early_slope_edge=11 early_slope_edge=11
if abs(angle) > early_slope_edge: if abs(angle) > early_slope_edge:
if angle < 0: if angle < 0:
angles2 = np.linspace(-90, -12, n_tot_angles) angles = np.linspace(-90, -12, n_tot_angles)
else: else:
angles2 = np.linspace(90, 12, n_tot_angles) angles = np.linspace(90, 12, n_tot_angles)
angle2, var2 = get_smallest_skew(img_resized, sigma_des, angles2, map=map, logger=logger, plotter=plotter) angle = get_smallest_skew(img_resized, sigma_des, angles, plotter=plotter)
if var2 > var:
angle = angle2
else: else:
angles = np.linspace(-25, 25, int(0.5 * n_tot_angles) + 10) angles = np.linspace(-25, 25, int(0.5 * n_tot_angles) + 10)
angle, var = get_smallest_skew(img_resized, sigma_des, angles, map=map, logger=logger, plotter=plotter) angle = get_smallest_skew(img_resized, sigma_des, angles, plotter=plotter)
early_slope_edge=22 early_slope_edge=22
if abs(angle) > early_slope_edge: if abs(angle) > early_slope_edge:
if angle < 0: if angle < 0:
angles2 = np.linspace(-90, -25, int(0.5 * n_tot_angles) + 10) angles = np.linspace(-90, -25, int(0.5 * n_tot_angles) + 10)
else: else:
angles2 = np.linspace(90, 25, int(0.5 * n_tot_angles) + 10) angles = np.linspace(90, 25, int(0.5 * n_tot_angles) + 10)
angle2, var2 = get_smallest_skew(img_resized, sigma_des, angles2, map=map, logger=logger, plotter=plotter) angle = get_smallest_skew(img_resized, sigma_des, angles, plotter=plotter)
if var2 > var:
angle = angle2
return angle return angle
def get_smallest_skew(img, sigma_des, angles, logger=None, plotter=None, map=map): def get_smallest_skew(img_resized, sigma_des, angles, plotter=None):
if logger is None: num_cores = cpu_count()
logger = getLogger(__package__)
if map is None: queue_of_all_params = Queue()
results = [do_image_rotation.__wrapped__(angle, img=img, sigma_des=sigma_des, logger=logger) processes = []
for angle in angles] nh = np.linspace(0, len(angles), num_cores + 1)
else:
with share_ndarray(img) as img_shared: for i in range(num_cores):
results = list(map(partial(do_image_rotation, img=img_shared, sigma_des=sigma_des, logger=None), angles_per_process = angles[int(nh[i]) : int(nh[i + 1])]
angles)) processes.append(Process(target=do_image_rotation, args=(queue_of_all_params, angles_per_process, img_resized, sigma_des)))
for i in range(num_cores):
processes[i].start()
var_res=[]
all_angles = []
for i in range(num_cores):
list_all_par = queue_of_all_params.get(True)
vars_for_subprocess = list_all_par[0]
angles_sub_process = list_all_par[1]
for j in range(len(vars_for_subprocess)):
var_res.append(vars_for_subprocess[j])
all_angles.append(angles_sub_process[j])
for i in range(num_cores):
processes[i].join()
if plotter: if plotter:
plotter.save_plot_of_rotation_angle(angles, results) plotter.save_plot_of_rotation_angle(all_angles, var_res)
try: try:
var_res = np.array(results) var_res=np.array(var_res)
assert var_res.any() ang_int=all_angles[np.argmax(var_res)]#angels_sorted[arg_final]#angels[arg_sort_early[arg_sort[arg_final]]]#angels[arg_fin]
idx = np.argmax(var_res)
angle = angles[idx]
var = var_res[idx]
except: except:
logger.exception("cannot determine best angle among %s", str(angles)) ang_int=0
angle = 0 return ang_int
var = 0
return angle, var
@wrap_ndarray_shared(kw='textline_mask_tot_ea') @wrap_ndarray_shared(kw='textline_mask_tot_ea')
def do_work_of_slopes_new( def do_work_of_slopes_new(