├── README.md ├── efficient illumination compensation techniques for text images.pdf ├── helpers.py ├── main.py └── testing images ├── 10.jpg ├── 11_2.jpg ├── 12.jpg ├── 13.jpg ├── 7.jpg ├── 8.jpg ├── 9.jpg └── page.png /README.md: -------------------------------------------------------------------------------- 1 | # text-image-binarization 2 | An implementation of the paper 'Efficient illumination compensation techniques for text images' 3 | 4 | To use it, all you need to do is put the image file in 5 | the same directory and run the code. 6 | 7 | At the beginning, you need to specify the file name and format 8 | For example, a image called "test.jpg" 9 | FILE_NAME = "test" 10 | FORMAT = ".jpg" 11 | Note: you don't need to type the quote mark. 12 | 13 | The default value for c and bl works well for most of time. 14 | For any images, test it by using the default value first. 15 | 16 | c = 0.4 17 | bl = 260 18 | 19 | You can also change the value for the two threshold. Two functions 20 | are implemented to decide the value for threshold. But they do not 21 | perform as well as the experienced value most of time. 22 | 23 | The default threshold for CEI is TH_c = 60 24 | The default threshold for Edge is TH_e = 30 25 | 26 | For application reasons, the implementation here is a little bit 27 | different from the paper. 28 | 29 | Right now, the last step (Estimate light distribution) takes a long 30 | time. Because this step cannot be optimized using numpy. However, 31 | there must be some other ways to improve the efficiency. I will work 32 | on it later. 33 | -------------------------------------------------------------------------------- /efficient illumination compensation techniques for text images.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fanyirobin/text-image-binarization/dc940fde8ad971319d952551c3f4949f67873d95/efficient illumination compensation techniques for text images.pdf -------------------------------------------------------------------------------- /helpers.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Sun May 27 16:29:48 2018 4 | 5 | @author: Yi (Robin) Fan 6 | """ 7 | 8 | import numpy as np 9 | import cv2 10 | 11 | #get histogram 12 | def get_hist(img): 13 | bins = np.arange(0, 300, 10) 14 | bins[26] = 255 15 | hp = np.histogram(img, bins) 16 | return hp 17 | 18 | #histogram reducing value 19 | def get_hr(hp, sqrt_hw): 20 | for i in range(len(hp[0])): 21 | if hp[0][i] > sqrt_hw: 22 | return i * 10 23 | 24 | #get contrast enhenced image 25 | def get_CEI(img, hr, c): 26 | CEI = (img - (hr + 50 * c)) * 2 27 | CEI[np.where(CEI > 255)] = 255 28 | CEI[np.where(CEI < 0)] = 0 29 | return CEI 30 | 31 | #draw image 32 | def draw(img): 33 | tmp = img.astype(np.uint8) 34 | cv2.imshow('image',tmp) 35 | cv2.waitKey(3000) 36 | cv2.destroyAllWindows() 37 | 38 | 39 | #scale to 0 - 255 40 | def scale(img): 41 | s = np.max(img) - np.min(img) 42 | res = img / s 43 | res -= np.min(res) 44 | res *= 255 45 | return res 46 | 47 | #get threshold for the avg edge image 48 | def get_th(img, bins): 49 | hist = np.histogram(img,bins) 50 | peak_1_index = np.argmax(hist[0]) 51 | peak_2_index = 0 52 | if peak_1_index == 0: 53 | peak_2_index += 1 54 | for i in range(len(hist[0])): 55 | if hist[0][i] > hist[0][peak_2_index] and i != peak_1_index: 56 | peak_2_index = i 57 | peak_1 = hist[1][peak_1_index] 58 | peak_2 = hist[1][peak_2_index] 59 | return ((peak_1 + peak_2) / 2), hist 60 | 61 | def get_th2(img, bins): 62 | num = img.shape[0] * img.shape[1] 63 | hist = np.histogram(img, bins) 64 | cdf = 0 65 | for i in range(len(hist[0])): 66 | cdf += hist[0][i] 67 | if cdf / num > 0.85: 68 | return hist[1][i] 69 | 70 | #threshold the image 71 | def img_threshold(th, img, flag): 72 | h = img.shape[0] 73 | w = img.shape[1] 74 | new_img = np.zeros((h,w)) 75 | if flag == "H2H": 76 | new_img[np.where(img >= th)] = 255 77 | elif flag == "H2L": 78 | new_img[np.where(img < th)] = 255 79 | return new_img 80 | 81 | # merge cei and edge map 82 | def merge(edge, cei): 83 | h = edge.shape[0] 84 | w = edge.shape[1] 85 | new_img = 255 * np.ones((h,w)) 86 | 87 | new_img[np.where(edge == 255)] = 0 88 | new_img[np.where(cei == 255)] = 0 89 | return new_img 90 | 91 | def find_end(tli, x, y): 92 | i = x 93 | while(i < tli.shape[0] and tli[i][y] == 0): 94 | i += 1 95 | return i - 1 96 | 97 | def find_mpv(cei, head, end, y): 98 | h = [] 99 | e = [] 100 | for k in range(5): 101 | if head - k >= 0: 102 | h.append(cei[head-k][y]) 103 | if end + k < cei.shape[0]: 104 | e.append(cei[end + k][y]) 105 | return np.max(h), np.max(e) 106 | 107 | 108 | # set interpolated image 109 | def set_intp_img(img, x, y, tli, cei): 110 | head = x 111 | end = find_end(tli, x, y) 112 | n = end - head + 1 113 | if n > 30: 114 | return end 115 | mpv_h, mpv_e = find_mpv(cei, head, end, y) 116 | for m in range(n): 117 | img[head+m][y] = mpv_h + (m + 1) * ((mpv_e - mpv_h) / n) 118 | return end 119 | -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Wed May 2 15:33:52 2018 4 | 5 | @author: Yi (Robin) Fan 6 | """ 7 | #%% Parameters for user 8 | c = 0.4 # in the range[0, 1] recommend 0.2-0.3 9 | bl = 260 # range[230 - 300] recommend 260 10 | 11 | #%% get File 12 | print("Please Input File Name (Example: image_name)") 13 | FILE_NAME = input("FILE_NAME: ") 14 | print("Please Input File Format (Example: .png)") 15 | FORMAT = input("FORMAT: ") 16 | 17 | #%% Program begin here 18 | import cv2 19 | import numpy as np 20 | from helpers import * 21 | 22 | im = cv2.imread(FILE_NAME + FORMAT) 23 | gray = cv2.cvtColor(im, cv2.COLOR_BGR2GRAY) 24 | gray = gray.astype(np.float32) 25 | 26 | width = gray.shape[1] 27 | height = gray.shape[0] 28 | 29 | #%% STEP: contrast enhancement 30 | print("Enhancing Contrast") 31 | hp = get_hist(im) 32 | sqrt_hw = np.sqrt(height * width) 33 | hr = get_hr(hp, sqrt_hw) 34 | cei = get_CEI(gray, hr, c) 35 | cv2.imwrite(FILE_NAME + "_Cei" + FORMAT, cei) 36 | 37 | #%% STEP: Edge detection 38 | print("Edge Detection") 39 | # build four filters 40 | m1 = np.array([-1,0,1,-2,0,2,-1,0,1]).reshape((3,3)) 41 | m2 = np.array([-2,-1,0,-1,0,1,0,1,2]).reshape((3,3)) 42 | m3 = np.array([-1,-2,-1,0,0,0,1,2,1]).reshape((3,3)) 43 | m4 = np.array([0,1,2,-1,0,1,-2,-1,0]).reshape((3,3)) 44 | 45 | eg1 = np.abs(cv2.filter2D(gray, -1, m1)) 46 | eg2 = np.abs(cv2.filter2D(gray, -1, m2)) 47 | eg3 = np.abs(cv2.filter2D(gray, -1, m3)) 48 | eg4 = np.abs(cv2.filter2D(gray, -1, m4)) 49 | eg_avg = scale((eg1 + eg2 + eg3 + eg4) / 4) 50 | 51 | bins_1 = np.arange(0, 265, 5) 52 | #threshold = get_th2(eg_avg, bins_1) 53 | eg_bin = img_threshold(30, eg_avg,"H2H") #threshold is hard coded to 30 (based 54 | #on the paper). Uncomment above to replace 55 | cv2.imwrite(FILE_NAME + "_EdgeBin" + FORMAT, eg_bin) 56 | 57 | 58 | #%% STEP: Text location 59 | print("Locating the Text") 60 | bins_2 = np.arange(0, 301, 40) 61 | #threshold_c = 255 - get_th2(cei, bins_2) 62 | cei_bin = img_threshold(60, cei, "H2L")#threshold is hard coded to 60 (based 63 | #on the paper). Uncomment above to replace 64 | cv2.imwrite(FILE_NAME + "_CeiBin" + FORMAT, cei_bin) 65 | tli = merge(eg_bin, cei_bin) 66 | cv2.imwrite(FILE_NAME + "_TLI" + FORMAT, tli) 67 | kernel = np.ones((3,3),np.uint8) 68 | erosion = cv2.erode(tli,kernel,iterations = 1) 69 | cv2.imwrite(FILE_NAME + "_TLI_erosion" + FORMAT, erosion) 70 | 71 | 72 | #%% STEP: Light distribution 73 | print("Estimate Light Distribution") 74 | int_img = np.array(cei) 75 | ratio = int(width / 20) 76 | for y in range(width): 77 | if y % ratio == 0 : 78 | print(int(y / width * 100), "%") 79 | for x in range(height): 80 | if erosion[x][y] == 0: 81 | x = set_intp_img(int_img, x, y, erosion, cei) 82 | mean_filter = 1 / 121 * np.ones((11,11), np.uint8) 83 | ldi = cv2.filter2D(scale(int_img), -1, mean_filter) 84 | cv2.imwrite(FILE_NAME + "_LDI" + FORMAT, ldi) 85 | 86 | 87 | #%% STEP: Light Balancing 88 | print("Balancing Light and Generating Result") 89 | result = np.divide(cei, ldi) * bl 90 | result[np.where(erosion != 0)] *= 1.5 91 | 92 | cv2.imwrite(FILE_NAME + "_result" + FORMAT, result) -------------------------------------------------------------------------------- /testing images/10.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fanyirobin/text-image-binarization/dc940fde8ad971319d952551c3f4949f67873d95/testing images/10.jpg -------------------------------------------------------------------------------- /testing images/11_2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fanyirobin/text-image-binarization/dc940fde8ad971319d952551c3f4949f67873d95/testing images/11_2.jpg -------------------------------------------------------------------------------- /testing images/12.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fanyirobin/text-image-binarization/dc940fde8ad971319d952551c3f4949f67873d95/testing images/12.jpg -------------------------------------------------------------------------------- /testing images/13.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fanyirobin/text-image-binarization/dc940fde8ad971319d952551c3f4949f67873d95/testing images/13.jpg -------------------------------------------------------------------------------- /testing images/7.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fanyirobin/text-image-binarization/dc940fde8ad971319d952551c3f4949f67873d95/testing images/7.jpg -------------------------------------------------------------------------------- /testing images/8.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fanyirobin/text-image-binarization/dc940fde8ad971319d952551c3f4949f67873d95/testing images/8.jpg -------------------------------------------------------------------------------- /testing images/9.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fanyirobin/text-image-binarization/dc940fde8ad971319d952551c3f4949f67873d95/testing images/9.jpg -------------------------------------------------------------------------------- /testing images/page.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fanyirobin/text-image-binarization/dc940fde8ad971319d952551c3f4949f67873d95/testing images/page.png --------------------------------------------------------------------------------