def crop(im, bw, split=True): im_h, im_w = im.shape[:2] all_letters = algorithm.all_letters(bw) AH = algorithm.dominant_char_height(bw, letters=all_letters) letters = algorithm.filter_size(AH, bw, letters=all_letters) all_lines = collate.collate_lines(AH, letters) combined = algorithm.combine_underlined(AH, bw, all_lines, all_letters) lines = algorithm.remove_stroke_outliers(bw, combined) if not lines: print('WARNING: no lines in image.') return AH, [] lines = filter_position(AH, bw, lines, split) lines = [ line for line in lines if not np.all(line.crop().apply(bw) == 255) ] if not lines: print('WARNING: eliminated all lines.') return AH, [] if split and im_w > im_h: # two pages line_sets = split_lines(lines) else: line_sets = [lines] return AH, line_sets
def get_AH_lines(im): all_letters = algorithm.all_letters(im) # AH == average character height AH = algorithm.dominant_char_height(im, letters=all_letters) if lib.debug: print('AH =', AH) letters = algorithm.filter_size(AH, im, letters=all_letters) all_lines = collate.collate_lines(AH, letters) # From up to down all_lines.sort(key=lambda l: l[0].y) combined = algorithm.combine_underlined(AH, im, all_lines, all_letters) filtered = algorithm.remove_stroke_outliers(im, combined, k=2.0) # filtered = algorithm.filter_spacing_deviation(im, AH, filtered) lines = remove_outliers(im, AH, filtered) # lines = combined # if lib.debug: # debug = cv2.cvtColor(bw, cv2.COLOR_GRAY2BGR) # for l in all_lines: # for l1, l2 in zip(l, l[1:]): # cv2.line(debug, tuple(l1.base_point().astype(int)), # tuple(l2.base_point().astype(int)), RED, 2) # lib.debug_imwrite('all_lines.png', debug) return AH, lines, all_lines
def ntirogiannis2014(im): lib.debug_prefix.append('ng2014') debug_imwrite('input.png', im) im_h, _ = im.shape N, BG_prime = ng2014_normalize(im) O = otsu(N) debug_imwrite('O.png', O) letters = algorithm.all_letters(O) height_map = HeightMap(letters) ratio_sum = 0 for h in range(1, height_map.max_height() + 1): if len(height_map[h]) == 0: continue ratio_sum += height_map.ratio_pixels(h) / height_map.ratio_components( h) if ratio_sum > 1: break min_height = h if lib.debug: print('Accept components only >= height', h) OP = O.copy() for h in range(1, min_height): for letter in height_map[h]: sliced = letter.slice(OP) np.place(sliced, letter.raster(), 255) debug_imwrite('OP.png', OP) strokes = fast_stroke_width(OP) debug_imwrite('strokes.png', normalize_u8(strokes.clip(0, 10))) SW = int(round(strokes.sum() / np.count_nonzero(strokes))) if lib.debug: print('SW =', SW) S = skeleton(OP) debug_imwrite('S.png', S) S_inv = ~S # S_inv_32 = S_inv.astype(np.int32) # FG_count = np.count_nonzero(S_inv) FG_pos = im[S_inv.astype(bool)] FG_avg = FG_pos.mean() FG_std = FG_pos.std() # FG = (S_inv & im).astype(np.int32) # FG_avg = FG.sum() / float(FG_count) # FG_std = np.sqrt(((S_inv_32 & (FG - FG_avg)) ** 2).sum() / float(FG_count)) if lib.debug: print('FG:', FG_avg, FG_std) BG_avg = BG_prime.mean() BG_std = BG_prime.std() if lib.debug: print('BG:', BG_avg, BG_std) if FG_avg + FG_std != 0: C = -50 * np.log10((FG_avg + FG_std) / (BG_avg - BG_std)) k = -0.2 - 0.1 * C / 10 else: # This is the extreme case when the FG is 100% black, check the article explaination page before equation 5 C = -50 * np.log10((2.5) / (BG_avg - BG_std)) k = -0.2 - 0.1 * C / 10 if lib.debug: print('niblack:', C, k) local = niblack(N, window_size=(2 * SW) | 1, k=k) debug_imwrite('local.png', local) local_CCs = algorithm.all_letters(local) # NB: paper uses OP here, which results in neglecting all small components. O_inv = ~O O_inv_32 = O_inv.astype(np.int8, copy=False).astype(np.int32).astype(np.uint32, copy=False) label_map_O_inv = O_inv_32 & local_CCs[0].label_map CO_inv = np.zeros(im.shape, dtype=np.uint8) for cc in local_CCs: if np.count_nonzero(cc.slice(label_map_O_inv) == cc.label) / float( cc.area()) >= C / 100: CO_sliced = cc.slice(CO_inv) np.place(CO_sliced, cc.raster(), 255) CO = ~CO_inv debug_imwrite('CO.png', CO) CO_inv_dilated = cv2.dilate(CO_inv, rect33) FB = ~(CO_inv | ((~O) & CO_inv_dilated)) debug_imwrite('FB.png', FB) lib.debug_prefix.pop() return FB