def post(upper_base, lower_base, MFV, word_img, no_dots_copy, SR): last_seg = word_img[:, 0: SR[1]] last_seg_no_dots = no_dots_copy[:, 0:SR[1]] plt.imshow(last_seg, 'gray') plt.show() # upper_base, lower_base, MFV = baseline_detection(last_seg_no_dots) MTI = horizontal_transitions(no_dots_copy, upper_base) # MTI = upper_base-1 VP = projection(last_seg, 'vertical') SRL, wrong = cut_points(last_seg, VP, MFV, MTI, upper_base) valid = filter_regions(last_seg, SRL, VP, upper_base, lower_base, MTI, MFV, 13) print(valid) l = last_seg.copy() V = np.dstack([l*255, l*255, l*255]) V[MTI, :, :] = [255, 0, 0] plt.imshow(V, 'gray') plt.show() breakpoint()
def bound_box(img_char): HP = projection(img_char, 'horizontal') VP = projection(img_char, 'vertical') top = -1 down = -1 left = -1 right = -1 i = 0 while i < len(HP): if HP[i] != 0: top = i break i += 1 i = len(HP) - 1 while i >= 0: if HP[i] != 0: down = i break i -= 1 i = 0 while i < len(VP): if VP[i] != 0: left = i break i += 1 i = len(VP) - 1 while i >= 0: if VP[i] != 0: right = i break i -= 1 return img_char[top:down + 1, left:right + 1]
def baseline_detection(word_img): '''Get baseline index of a given word''' HP = projection(word_img, 'horizontal') peak = np.amax(HP) # Array of indices of max element baseline_idx = np.where(HP == peak)[0] # Get first or last index upper_base = baseline_idx[0] lower_base = baseline_idx[-1] thickness = abs(lower_base - upper_base) + 1 return upper_base, lower_base, thickness
def projection_segmentation(clean_img, axis, cut=3): segments = [] start = -1 cnt = 0 projection_bins = projection(clean_img, axis) for idx, projection_bin in enumerate(projection_bins): if projection_bin != 0: cnt = 0 if projection_bin != 0 and start == -1: start = idx if projection_bin == 0 and start != -1: cnt += 1 if cnt >= cut: if axis == 'horizontal': segments.append(clean_img[max(start - 1, 0):idx, :]) elif axis == 'vertical': segments.append(clean_img[:, max(start - 1, 0):idx]) cnt = 0 start = -1 return segments
def segment(line, word_img): # binary_word = binarize(word_img) binary_word = word_img//255 no_dots_copy = remove_dots(binary_word) # l = binary_word.copy() VP_no_dots = projection(no_dots_copy, 'vertical') VP = projection(binary_word, 'vertical') binary_word = fill(binary_word, VP_no_dots) no_dots_copy = remove_dots(binary_word) # sk = skeletonize(no_dots_copy) upper_base, lower_base, MFV = baseline_detection(remove_dots(line)) MTI = horizontal_transitions(no_dots_copy, upper_base) # if MTI == 0: # plt.imshow(l, 'gray') # plt.show() SRL, wrong = cut_points(binary_word, VP, MFV, MTI, upper_base) if wrong: MTI -= 1 SRL.clear() SRL, wrong = cut_points(binary_word, VP, MFV, MTI, upper_base) HP = projection(line, 'horizontal') top_line = -1 # for i, proj in enumerate(HP): # if proj != 0: # top_line = i # break # print(f'MFV: {MFV}') # print(f'upper: {upper_base}') # print(f'lower: {lower_base}') # print(f'MTI: {MTI}') # plt.imshow(no_dots_copy, 'gray') # plt.show() valid = filter_regions(binary_word, no_dots_copy, SRL, VP, upper_base, lower_base, MTI, MFV, top_line) # post(upper_base, lower_base, MFV, binary_word, no_dots_copy ,valid[-1]) # V = np.dstack([l*255, l*255, l*255]) # V[MTI, :, :] = [255, 0, 0] # for region in valid: # V[:, region[1], :] = [255, 0, 0] # print(SRL) # print(valid) # plt.imshow(V, 'gray') # plt.show() # breakpoint() chars = extract_char(binary_word, valid) return chars
def filter_regions(word_img, no_dots_copy, SRL:list, VP:list, upper_base:int, lower_base:int, MTI:int, MFV:int, top_line:int): valid_separation_regions = [] overlap = [] T = 1 components, labels= cv.connectedComponents(word_img[:lower_base+5, :], connectivity=8) SR_idx = 0 while SR_idx < len(SRL): SR = SRL[SR_idx] end_idx, cut_idx, start_idx = SR # Case 1 : Vertical Projection = 0 if VP[cut_idx] == 0: valid_separation_regions.append(SR) SR_idx += 1 continue # Case 2 : no connected path between start and end # components, labels= cv.connectedComponents(word_img[:, end_idx:start_idx+1], connectivity=8) if labels[MTI, end_idx] != labels[MTI, start_idx]: valid_separation_regions.append(SR) overlap.append(SR) SR_idx += 1 continue # Case 3 : Contain Holes # if check_hole(no_dots_copy[:, end_idx: cut_idx]) and inside_hole(no_dots_copy, end_idx, start_idx): cc, l = cv.connectedComponents(1-(no_dots_copy[:, end_idx:start_idx+1]), connectivity=4) if cc-1 >= 3 and inside_hole(no_dots_copy, end_idx, start_idx): SR_idx += 1 continue # Case 4 : No baseline between start and end segment = no_dots_copy[:, end_idx+1: start_idx] segment_width = start_idx-end_idx-1 j = end_idx+1 cnt = 0 while j < start_idx: # Black pixel (Discontinuity) base = upper_base-T while base <= lower_base+T: pixel = no_dots_copy[base][j] cnt += pixel base += 1 j += 1 if cnt < segment_width-2 and segment_width > 4: segment_HP = projection(segment, 'horizontal') SHPA = np.sum(segment_HP[:upper_base]) SHPB = np.sum(segment_HP[lower_base+T+1:]) if (int(SHPB) - int(SHPA)) >= 0: SR_idx += 1 continue elif VP[cut_idx] <= MFV + T: valid_separation_regions.append(SR) SR_idx += 1 continue else: SR_idx += 1 continue # if SR_idx == 0: # breakpoint() # Case 5 : Last region or next VP[nextcut] = 0 if SR_idx == len(SRL) - 1 or VP[SRL[SR_idx+1][1]] == 0: if SR_idx == len(SRL) - 1: segment_dots = word_img[:, :SRL[SR_idx][1]+1] segment = no_dots_copy[:, :SRL[SR_idx][1]+1] next_cut = 0 else: next_cut = SRL[SR_idx+1][1] segment_dots = word_img[:, next_cut:SRL[SR_idx][1]+1] segment = no_dots_copy[:, next_cut:SRL[SR_idx][1]+1] segment_HP = projection(segment, 'horizontal') (h, w) = segment.shape top = -1 for i, proj in enumerate(segment_HP): if proj != 0: top = i break height = upper_base - top # if SR_idx == len(SRL) - 1: # breakpoint() SHPA = np.sum(segment_HP[:upper_base]) SHPB = np.sum(segment_HP[lower_base+T+1:]) sk = skeletonize(segment).astype(np.uint8) # if ((1 <= upper_base - top_left_pixel <= (upper_base-top_line)/2 and upper_base - top_left_pixel >= 0)\ # or (int(SHPB) - int(SHPA)) > 4 \ # or ((0 <= upper_base - top_left_pixel <= 2) and ((cut_idx - (dist+next_cut)) <= 5)))\ # and not check_hole(segment): # SR_idx += 1 # continue seg_VP = projection(segment, 'vertical') non_zero = np.nonzero(seg_VP)[0] cnt = 0 # for k in range(0, (len(non_zero)//2)+(len(non_zero)%2)): for k in range(0, 3): if k >= len(non_zero): break index = non_zero[k] if seg_VP[index] >= height: cnt += 1 # if SR_idx == 0: # breakpoint() # if ((-2 <= (upper_base - top_left_pixel) < 0)\ # or (0 <= (upper_base - top) <= 2) and (0 <=(upper_base - top_left_pixel) <= 1)\ # or ((1 < (upper_base - top) <= 6) and ((upper_base - top_left_pixel) >= 0) and cnt >= 2))\ # and not check_hole(segment): # SR_idx += 1 # continue if (SHPB <= 5 and cnt > 0 and height <= 6) or (len(non_zero) >= 10 and SHPB > SHPA and not check_dots(segment_dots)): SR_idx += 1 continue # else: # valid_separation_regions.append(SR) # SR_idx += 1 # continue # else: # if 0 <= upper_base - top_left_pixel <= 2 and cut_idx - (dist+next_cut) <= 5: # SR_idx += 1 # continue # Strokes SEGP = (-1, -1) SEG = (-1, -1) SEGN = (-1, -1) SEGNN = (-1, -1) SEGP_SR1 = (0, 0) SEGP_SR2 = (0, 0) SEG_SR1 = (0, 0) SEG_SR2 = (0, 0) SEGN_SR1 = (0, 0) SEGN_SR2 = (0, 0) SEGNN_SR1 = (0, 0) SEGNN_SR2 = (0, 0) current_cut = SR[1] if SR_idx == 0: SEGP = (SRL[SR_idx][1], word_img.shape[1]-1) SEGP_SR1 = (SRL[SR_idx][0], SRL[SR_idx][2]) SEGP_SR2 = (SRL[SR_idx][1], word_img.shape[1]-1) if SR_idx > 0: SEGP = (SRL[SR_idx][1], SRL[SR_idx-1][1]) SEGP_SR1 = (SRL[SR_idx][0], SRL[SR_idx][2]) SEGP_SR2 = (SRL[SR_idx-1][0], SRL[SR_idx-1][2]) if SR_idx < len(SRL)-1: SEG = (SRL[SR_idx+1][1], SRL[SR_idx][1]) SEG_SR1 = (SRL[SR_idx][0], SRL[SR_idx][2]) SEG_SR2 = (SRL[SR_idx+1][0], SRL[SR_idx+1][2]) if SR_idx < len(SRL)-2: SEGN = (SRL[SR_idx+2][1], SRL[SR_idx+1][1]) SEGN_SR1 = (SRL[SR_idx+1][0], SRL[SR_idx+1][2]) SEGN_SR2 = (SRL[SR_idx+2][0], SRL[SR_idx+2][2]) elif SR_idx == len(SRL)-2: SEGN = (0, SRL[SR_idx+1][1]) SEGN_SR1 = (SRL[SR_idx+1][0], SRL[SR_idx+1][2]) SEGN_SR2 = (0, SRL[SR_idx+1][2]) if SR_idx < len(SRL)-3: SEGNN = (SRL[SR_idx+3][1], SRL[SR_idx+2][1]) SEGNN_SR1 = (SRL[SR_idx+2][0], SRL[SR_idx+2][2]) SEGNN_SR2 = (SRL[SR_idx+3][0], SRL[SR_idx+3][2]) # if SR_idx == 6: # breakpoint() # SEG is stroke with dots if SEG[0] != -1 and\ (check_stroke(no_dots_copy, no_dots_copy[:, SEG[0]:SEG[1]], upper_base, lower_base, SEG_SR1, SEG_SR2) \ and check_dots(word_img[:, SEG[0]:SEG[1]])): # breakpoint() # Case when starts with ش if SEGP[0] != -1 and \ ((check_stroke(no_dots_copy, no_dots_copy[:, SEGP[0]:SEGP[1]], upper_base, lower_base, SEGP_SR1, SEGP_SR2) \ and not check_dots(word_img[:, SEGP[0]:SEGP[1]]))\ and (SR_idx == 0 or VP[SRL[SR_idx-1][1]] == 0 or (VP[SRL[SR_idx-1][1]] == 0 and SRL[SR_idx-1] in overlap))): SR_idx += 2 continue else: valid_separation_regions.append(SR) SR_idx += 1 continue # SEG is stroke without dots elif SEG[0] != -1\ and (check_stroke(no_dots_copy, no_dots_copy[:, SEG[0]:SEG[1]], upper_base, lower_base, SEG_SR1, SEG_SR2) \ and not check_dots(word_img[:, SEG[0]:SEG[1]])): # Case starts with س if SEGP[0] != -1\ and (check_stroke(no_dots_copy, no_dots_copy[:, SEGP[0]:SEGP[1]], upper_base, lower_base, SEGP_SR1, SEGP_SR2) \ and not check_dots(word_img[:, SEGP[0]:SEGP[1]])): SR_idx += 2 continue # SEGN is stroke without dots if SEGN[0] != -1 \ and (check_stroke(no_dots_copy, no_dots_copy[:, SEGN[0]:SEGN[1]], upper_base, lower_base, SEGN_SR1, SEGN_SR2) \ and not check_dots(word_img[:, SEGN[0]:SEGN[1]])): valid_separation_regions.append(SR) SR_idx += 3 continue # SEGN stroke with Dots and SEGNN stroke without Dots if SEGN[0] != -1\ and (check_stroke(no_dots_copy, no_dots_copy[:, SEGN[0]:SEGN[1]], upper_base, lower_base, SEGN_SR1, SEGN_SR2) \ and check_dots(word_img[:, SEGN[0]:SEGN[1]])) \ and ((SEGNN[0] != -1 \ and (check_stroke(no_dots_copy, no_dots_copy[:, SEGNN[0]:SEGNN[1]], upper_base, lower_base, SEGNN_SR1, SEGNN_SR2) \ and not check_dots(word_img[:, SEGNN[0]:SEGNN[1]]))) or (len(SRL)-1-SR_idx == 2) or (len(SRL)-1-SR_idx == 3)): valid_separation_regions.append(SR) SR_idx += 3 continue # SEGN is not stroke or Stroke with Dots if SEGN[0] != -1 \ and ((not check_stroke(no_dots_copy, no_dots_copy[:, SEGN[0]:SEGN[1]], upper_base, lower_base, SEGN_SR1, SEGN_SR2)) \ or (check_stroke(no_dots_copy, no_dots_copy[:, SEGN[0]:SEGN[1]], upper_base, lower_base, SEGN_SR1, SEGN_SR2) \ and check_dots(word_img[:, SEGN[0]:SEGN[1]]))): SR_idx += 1 continue SR_idx += 1 continue if (len(valid_separation_regions) == 0 or\ len(valid_separation_regions) > 0 and abs(cut_idx-valid_separation_regions[-1][1]) > 2): valid_separation_regions.append(SR) SR_idx += 1 return valid_separation_regions
def check_stroke(no_dots_copy, segment, upper_base, lower_base, SR1, SR2): T = 1 components, labels, stats, cen= cv.connectedComponentsWithStats(segment, connectivity=8) skeleton = skeletonize(segment.copy()).astype(np.uint8) (h, w) = segment.shape cnt = 0 for label in range(1, components): if stats[label][4] > 3: cnt += 1 else: segment[labels==label] = 0 if cnt > 2 or cnt == 0: return False if check_hole(segment) or inside_hole(no_dots_copy, SR1[0], SR1[1]) or inside_hole(no_dots_copy, SR2[0], SR2[1]): return False HP = projection(skeleton, 'horizontal') VP = projection(segment, 'vertical') seg_l = -1 seg_r = -1 for i in range(0, len(VP)): if VP[i] != 0: seg_l = i break for i in range(len(VP)-1, -1, -1): if VP[i] != 0: seg_r = i break seg_width = seg_r - seg_l + 1 SHPA = np.sum(HP[:upper_base]) SHPB = np.sum(HP[lower_base+T+1:]) MFV_HP = np.argmax(np.bincount(HP)[1:])+1 MFV = lower_base - upper_base + 1 + T top_pixel = -1 for i, proj in enumerate(HP): if proj != 0: top_pixel = i break height = upper_base-top_pixel VT = 0 for i in range(w): if vertical_transitions(skeleton, i) > 2: VT += 1 cnt = 0 for proj in VP: if proj >= height: cnt += 2 elif proj == height-1: cnt += 1 # abs(MFV - MFV_HP) <= 2 if SHPB == 0 and height <= 6 and VT <= 2 and seg_width <= 6 and cnt >= 2: return True return False
def cut_points(word_img, VP, MFV, MTI, baseline_idx): # flag to know the start of the word f = 0 flag = 0 (h, w) = word_img.shape i = w-1 separation_regions = [] wrong = 0 # loop over the width of the image from right to left while i >= 0: pixel = word_img[MTI, i] if pixel == 1 and f == 0: f = 1 flag = 1 if f == 1: # Get start and end of separation region (both are black pixels <----) if pixel == 0 and flag == 1: start = i+1 flag = 0 elif pixel == 1 and flag == 0: end = i # end maybe = i not i+1 flag = 1 mid = (start + end) // 2 left_zero = -1 left_MFV = -1 right_zero = -1 right_MFV = -1 # threshold for MFV T = 1 j = mid - 1 # loop from mid to end to get nearest VP = 0 and VP = MFV while j >= end: if VP[j] == 0 and left_zero == -1: left_zero = j if VP[j] <= MFV + T and left_MFV == -1: left_MFV = j # if left_zero != -1 and left_MFV != -1: # break j -= 1 j = mid # loop from mid to start to get nearest VP = 0 and VP = MFV while j <= start: if VP[j] == 0 and right_zero == -1: right_zero = j if VP[j] <= MFV + T and right_MFV == -1: right_MFV = j if right_zero != -1 and right_MFV != -1: break j += 1 # Check for VP = 0 first if VP[mid] == 0: cut_index = mid elif left_zero != -1 and right_zero != -1: if abs(left_zero-mid) <= abs(right_zero-mid): cut_index = left_zero else: cut_index = right_zero elif left_zero != -1: cut_index = left_zero elif right_zero != -1: cut_index = right_zero # Check for VP = MFV second # elif VP[mid] <= MFV+T: # cut_index = mid elif left_MFV != -1: cut_index = left_MFV elif right_MFV != -1: cut_index = right_MFV else: cut_index = mid seg = word_img[:, end:start] HP = projection(seg, 'horizontal') SHPA = np.sum(HP[:MTI]) SHPB = np.sum(HP[MTI+1:]) top = 0 for idx, proj in enumerate(HP): if proj != 0: top = idx break # if end == 24: # breakpoint() # # if 9>=(int(SHPA) - int(SHPB)) >= 4 and sum(word_img[baseline_idx, end:start]) <= 1: # # wrong = 1 # breakpoint() cnt = 0 for k in range(end, cut_index+1): if vertical_transitions(word_img, k) > 2: cnt = 1 if SHPB == 0 and (baseline_idx - top) <= 5 and cnt == 1: # breakpoint() wrong = 1 else: separation_regions.append((end, cut_index, start)) i -= 1 return separation_regions, wrong