def adaptive_otsu(im): im_h, _ = im.shape s = (im_h // 200) | 1 ellipse = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (s, s)) background = cv2.morphologyEx(im, cv2.MORPH_DILATE, ellipse) bg_float = background.astype(np.float64) debug_imwrite('bg.png', background) C = np.percentile(im, 30) normalized = clip_u8(C / (bg_float + 1e-10) * im) debug_imwrite('norm.png', normalized) return otsu(normalized)
def lu2010(im): im_h, im_w = im.shape # im_bg_row = polynomial_background_easy(im) # TODO: implement full # im_bg = polynomial_background_easy(im_bg_row.T).T # im_bg = im_bg.clip(0.1, 255) IM = cv2.erode(niblack(im, window_size=61, k=0.2), rect33) inpainted, modified = inpaint.inpaint_ng14(im, -IM) im_bg = (inpainted & ~IM) | (modified & IM) im_bg = im_bg.astype(np.float32).clip(0.1, 255) debug_imwrite('bg.png', im_bg) C = np.percentile(im, 30) I_bar = clip_u8(C / im_bg * im) debug_imwrite('ibar.png', I_bar) I_bar_padded = np.pad(I_bar, (1, 1), 'edge') V_h = cv2.absdiff(I_bar_padded[2:, 1:-1], I_bar_padded[:-2, 1:-1]) V_v = cv2.absdiff(I_bar_padded[1:-1, 2:], I_bar_padded[1:-1, :-2]) V = V_h + V_v V[V < V_h] = 255 # clip overflow _, stroke_edges = cv2.threshold(V, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU) # 1 if high contrast, 0 otherwise. E_inv = stroke_edges & 1 E_inv_255 = E_inv * 255 debug_imwrite('e_inv.png', E_inv_255) im_high = im & E_inv_255 debug_imwrite('im_high.png', im_high) # calculate stroke width H, _ = np.histogram(nonzero_distances_row(E_inv), np.arange(im_h / 100)) H[1] = 0 # don't use adjacent pix W = H.argmax() print('stroke width:', W) size = 2 * W N_min = W if size >= 16: E_inv = E_inv.astype(np.uint16) window = (size | 1, size | 1) N_e = cv2.boxFilter(E_inv, -1, window, normalize=False) debug_imwrite('n_e.png', bool_to_u8(N_e >= N_min)) E_mean = cv2.boxFilter(im_high, cv2.CV_32S, window, normalize=False) / (N_e.astype(np.float64) + 0.1) debug_imwrite('i_bar_e_mean.png', bool_to_u8(I_bar <= E_mean)) out = ~bool_to_u8((N_e >= N_min) & (I_bar <= E_mean)) debug_imwrite('lu2010.png', out) return out
def print_dict(filename, D_T): K, W_sq = D_T.shape W = int(np.sqrt(W_sq)) assert W_sq == W**2 D_T_s = D_T - np.percentile(D_T, 5) ratio = 255 / np.percentile(D_T_s, 95) patches = lib.clip_u8(ratio * D_T_s.reshape(K, W, W)) sqrtK = int(np.ceil(np.sqrt(K))) padding = ((0, sqrtK**2 - K), (1, 1), (1, 1)) patches_padded = np.pad(patches, padding, 'constant', constant_values=127) dict_square = patches_padded.reshape(sqrtK, sqrtK, W + 2, W + 2) \ .transpose(0, 2, 1, 3).reshape(sqrtK * (W + 2), sqrtK * (W + 2)) lib.debug_imwrite(filename, dict_square)
def ng2014_normalize(im): IM = niblack(im, window_size=61, k=-0.2) debug_imwrite('niblack.png', IM) IM = cv2.erode(IM, rect33) debug_imwrite('dilated.png', IM) inpainted_min, inpainted_avg, modified = inpaint.inpaint_ng14(im, -IM) debug_imwrite('inpainted_min.png', inpainted_min) debug_imwrite('inpainted_avg.png', inpainted_avg) bg = (inpainted_min & ~IM) | (modified & IM) debug_imwrite('bg.png', bg) bgp = (inpainted_avg & ~IM) | (modified & IM) debug_imwrite('bgp.png', bg) im_f = im.astype(float) + 1 bg_f = bg.astype(float) + 1 F = im_f / bg_f N = clip_u8(255 * (F - F.min())) debug_imwrite('N.png', N) return N, bgp
out_dir = sys.argv[3] N_IMG = 1000 theta_range = [-np.pi / 45, np.pi / 45] # +/- 4 deg KERNELS = [(1, 1), (3, 3), (5, 1), (3, 7)] NOISE = 15 imgs_base = [fn for fn in os.listdir(sys.argv[1]) if fn.endswith('.png')] for i in range(N_IMG): fn_base = np.random.choice(imgs_base) print(i, fn_base) im = cv2.imread(os.path.join(hi_dir, fn_base), cv2.IMREAD_UNCHANGED) theta = (theta_range[1] - theta_range[0]) * np.random.random() \ + theta_range[0] rotated = algorithm.safe_rotate(im, theta) cv2.imwrite(os.path.join(out_dir, 'im{}.png'.format(i)), rotated) kernel_std = KERNELS[np.random.choice(len(KERNELS))] blurred = cv2.GaussianBlur(rotated, (0, 0), kernel_std[0], kernel_std[1]) noisy = lib.clip_u8( blurred.astype(np.float64) + 10 * np.random.randn(*blurred.shape)) downsampled = cv2.resize(noisy, (0, 0), None, 0.5, 0.5, interpolation=cv2.INTER_AREA) # _, binarized = cv2.threshold(downsampled, 140, 255, cv2.THRESH_BINARY) cv2.imwrite(os.path.join(in_dir, 'im{}.png'.format(i)), downsampled)