def compute_checkbox_position(blank_im): binary = convert_to_binary(255 - blank_im) labels, n = morph.label(binary) h, w = binary.shape minsize = 40 # find small dash in img sums = measurements.sum(binary, labels, range(n + 1)) sums = sums[labels] good = minimum(binary, 1 - (sums > 0) * (sums < minsize)) junk_cc = np.bitwise_xor(good, binary) # temporary fix: add bottom line junk_cc[h-1:, :] = np.ones((1, w)) junk_cc = morph.r_dilation(junk_cc, (7,7)) junk_cc = morph.r_closing(junk_cc, (9,9)) # find hole using morphology hole = morph.fill_hole(junk_cc) hole = hole - junk_cc # locate holes position labels, n = morph.label(hole) objects = morph.find_objects(labels) objects = sorted(objects, key=lambda b: sl.center(b)) area_thres = 0.4 * (amax([sl.area(b) for b in objects]) if len(objects) > 0 else 0) boxes = [[b[0].start, b[1].start, b[0].stop, b[1].stop] for b in objects if sl.area(b) > area_thres] return boxes, convert_binary_to_normal_im(hole)
def pre_check_line(oriline): if oriline.shape[0] < 10: return False if 1.0 * oriline.shape[1] / oriline.shape[0] < 1.28: return False if mean(oriline) < 0.35: return False if oriline.shape[0] > 25: line = sauvola(oriline, w=oriline.shape[0] * 3 / 4, k=0.05, reverse=True, scaledown=20.0 / oriline.shape[0]) else: line = sauvola(oriline, w=oriline.shape[0] * 3 / 4, k=0.05, reverse=True) if mean(line) < 0.15: return False _, n = morph.label(line) n = 1.0 * n / oriline.shape[1] * oriline.shape[0] if n > 15: return False return True
def remove_hlines(self, binary, scale, maxsize=10): labels, _ = morph.label(binary) objects = morph.find_objects(labels) for i, b in enumerate(objects): if sl.width(b) > maxsize * scale: labels[b][labels[b] == i + 1] = 0 return np.array(labels != 0, 'B')
def remove_hlines(binary,scale,maxsize=10): labels,_ = morph.label(binary) objects = morph.find_objects(labels) for i,b in enumerate(objects): if sl.width(b)>maxsize*scale: labels[b][labels[b]==i+1] = 0 return array(labels!=0,'B')
def firstAnalyse(binary): binaryary = morph.r_closing(binary.astype(bool), (1,1)) labels,_ = morph.label(binaryary) objects = morph.find_objects(labels) ### <<<==== objects here bysize = sorted(range(len(objects)), key=lambda k: sl.area(objects[k])) # bysize = sorted(objects,key=sl.area) scalemap = zeros(binaryary.shape) smalldot = zeros(binaryary.shape, dtype=binary.dtype) for i in bysize: o = objects[i] if amax(scalemap[o])>0: # mask = where(labels[o] != (i+1),uint8(255),uint8(0)) # binary[o] = cv2.bitwise_and(binary[o],binary[o],mask=mask) continue scalemap[o] = sl.area(o)**0.5 scale = median(scalemap[(scalemap>3)&(scalemap<100)]) ### <<<==== scale here for i,o in enumerate(objects): if (sl.width(o) < scale/2) or (sl.height(o) < scale/2): smalldot[o] = binary[o] if sl.dim0(o) > 3*scale: mask = where(labels[o] != (i+1),uint8(255),uint8(0)) binary[o] = cv2.bitwise_and(binary[o],binary[o],mask=mask) continue return objects, smalldot, scale
def compute_line_seeds(binary,bottom,top,colseps,scale,threshold=0.2,vscale=1.0): """Base on gradient maps, computes candidates for baselines and xheights. Then, it marks the regions between the two as a line seed.""" t = threshold vrange = int(vscale*scale) bmarked = maximum_filter(bottom==maximum_filter(bottom,(vrange,0)),(2,2)) bmarked *= (bottom>t*amax(bottom)*t)*(1-colseps) tmarked = maximum_filter(top==maximum_filter(top,(vrange,0)),(2,2)) tmarked *= (top>t*amax(top)*t/2)*(1-colseps) tmarked = maximum_filter(tmarked,(1,20)) seeds = zeros(binary.shape,'i') delta = max(3,int(scale/2)) for x in range(bmarked.shape[1]): transitions = sorted([(y,1) for y in find(bmarked[:,x])]+[(y,0) for y in find(tmarked[:,x])])[::-1] transitions += [(0,0)] for l in range(len(transitions)-1): y0,s0 = transitions[l] if s0==0: continue seeds[y0-delta:y0,x] = 1 y1,s1 = transitions[l+1] if s1==0 and (y0-y1)<5*scale: seeds[y1:y0,x] = 1 seeds = maximum_filter(seeds,(1,int(1+scale))) seeds *= (1-colseps) #DSAVE("lineseeds",[seeds,0.3*tmarked+0.7*bmarked,binary]) seeds,_ = morph.label(seeds) return seeds
def extract_region_from_image_cc(im, rect): pad = 10 y0, x0, y1, x1 = rect local_im = im[y0-pad:y1+pad, x0-pad:x1+pad].copy() h, w = local_im.shape box = (slice(pad, h - pad, None), slice(pad, w - pad, None)) binary = convert_to_binary(255 - local_im) labels, _ = morph.label(binary) objects = morph.find_objects(labels) region_objs = [] for i, b in enumerate(objects): if 1.0 * sl.xoverlap(b, box) * sl.yoverlap(b, box) / sl.area(b) > 0.55: region_objs.append(b) else: binary[b][labels[b]==i+1] = 0 x2 = min([obj[1].start for obj in region_objs] + [pad]) x3 = max([obj[1].stop for obj in region_objs] + [w - pad]) y2 = min([obj[0].start for obj in region_objs] + [pad]) y3 = max([obj[0].stop for obj in region_objs] + [h - pad]) return convert_binary_to_normal_im(binary[y2:y3, x2:x3]), (pad-x2, x3-w+pad, pad-y2, y3-h+pad)
def compute_line_seeds(binary, bottom, top, colseps, scale, threshold, vscale): """Base on gradient maps, computes candidates for baselines and xheights. Then, it marks the regions between the two as a line seed.""" t = threshold vrange = int(vscale * scale) bmarked = maximum_filter(bottom == maximum_filter(bottom, (vrange, 0)), (2, 2)) bmarked = bmarked * (bottom > t * np.amax(bottom) * t) * (1 - colseps) tmarked = maximum_filter(top == maximum_filter(top, (vrange, 0)), (2, 2)) tmarked = tmarked * (top > t * np.amax(top) * t / 2) * (1 - colseps) tmarked = maximum_filter(tmarked, (1, 20)) seeds = np.zeros(binary.shape, 'i') delta = max(3, int(scale / 2)) for x in range(bmarked.shape[1]): transitions = sorted([(y, 1) for y in find(bmarked[:, x])] + [(y, 0) for y in find(tmarked[:, x])])[::-1] transitions += [(0, 0)] for l in range(len(transitions) - 1): y0, s0 = transitions[l] if s0 == 0: continue seeds[y0 - delta:y0, x] = 1 y1, s1 = transitions[l + 1] if s1 == 0 and (y0 - y1) < 5 * scale: seeds[y1:y0, x] = 1 seeds = maximum_filter(seeds, (1, int(1 + scale))) seeds = seeds * (1 - colseps) DSAVE("lineseeds", [seeds, 0.3 * tmarked + 0.7 * bmarked, binary]) seeds, _ = morph.label(seeds) return seeds
def calc_line(oriline): line = sauvola(oriline, w=oriline.shape[0] / 2, k=0.05, reverse=True) oridense = '{:3.3f}'.format(mean(oriline)) dens = '{:3.3f}'.format(mean(line)) # rati = '{:3.3f}'.format(1.0*line.shape[1]/line.shape[0]) _, n = morph.label(line) n = '{:3.3f}'.format(1.0 * n / oriline.shape[1] * oriline.shape[0]) return dens + '_' + n + '_' + oridense, line
def remove_vlines(binary, gray, scale, maxsize=10): labels, _ = morph.label(binary) objects = morph.find_objects(labels) for i, b in enumerate(objects): if (sl.width(b) <= 20 and sl.height(b) > 200) or (sl.width(b) <= 45 and sl.height(b) > 500): gray[b][labels[b] == i + 1] = 140 # gray[:,b[1].start:b[1].stop]=140 labels[b][labels[b] == i + 1] = 0 return array(labels != 0, "B")
def remove_noise(line,minsize=8): """Remove small pixels from an image.""" if minsize==0: return line bin = (line>0.5*amax(line)) labels,n = morph.label(bin) sums = measurements.sum(bin,labels,range(n+1)) sums = sums[labels] good = minimum(bin,1-(sums>0)*(sums<minsize)) return good
def text_line_segmentation_NN(image, scale, mask=None, use_binary=False, debug_image=None, offset=(0, 0)): h, w = image.shape[:2] if debug_image is None: debug_image = image if len(debug_image.shape) < 3: debug_image = cv2.cvtColor(image, cv2.COLOR_GRAY2BGR) if len(image.shape) > 2: image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) if use_binary: image = morph.thresh_sauvola(image, k=0.15) * 255 if mask is not None: image = (image + mask * 255).astype('uint8') labels, _ = morph.label(image == 0) objects = morph.find_objects(labels) height_map = [ sl.height(o) for o in objects if sl.height(o) > 6 and sl.height(o) < 100 and sl.aspect_normalized(o) < 8 ] avg_h = max(np.nan_to_num(np.mean(height_map)), scale * 0.6) block = Block(image, avg_h) words = block.getWordBoundingBoxes() lines = filter_and_merge_overlap_boxes(words, max(avg_h, scale * 1.2) * 0.3) lines = filter_and_merge_overlap_boxes(lines, max(avg_h, scale * 1.2) * 0.3, use_merge_same_line_only=True) offset_x, offset_y = offset # filter line by size lines = [ l for l in lines if l[3] - l[1] > avg_h * 0.3 and l[3] - l[1] < avg_h * 2.5 and l[2] - l[0] > avg_h * 0.5 ] lines = [sl.pad_box(l, 0, (h, w)) for l in lines] lines = [[ l[0] + offset_x, l[1] + offset_y, l[2] + offset_x, l[3] + offset_y ] for l in lines] debug_image = block.paint(None) return lines, debug_image
def remove_vlines(binary,gray,scale,maxsize=10): labels,_ = morph.label(binary) objects = morph.find_objects(labels) for i,b in enumerate(objects): if (sl.width(b)<=20 and sl.height(b)>200) or (sl.width(b)<=45 and sl.height(b)>500): gray[b][labels[b]==i+1] = 140 #gray[:,b[1].start:b[1].stop]=140 labels[b][labels[b]==i+1] = 0 return array(labels!=0, 'B')
def calc(self, objects, scale): if self.binpage is None: return tt = time() bottom, top, boxmap = compute_gradmaps(self.binpage, scale) # DSHOW('hihi', [0.5*bottom+0.5*top,self.binpage, boxmap]) seeds0 = compute_line_seeds(self.binpage, bottom, top, scale) seeds, _ = morph.label(seeds0) llabels = morph.propagate_labels(boxmap, seeds, conflict=0) spread = spread_labels(seeds, maxdist=scale) llabels = where(llabels > 0, llabels, spread * self.binpage) segmentation = llabels * self.binpage self.binpage = ocrolib.remove_noise(self.binpage, args.noise) lines = psegutils.compute_lines(segmentation, scale) binpage_reversed = 1 - self.binpage # print 'pre line ', time() - tt tt = time() self.lines = [] for i, l in enumerate(lines): tt = time() binline = psegutils.extract_masked( binpage_reversed, l, pad=args.pad, expand=args.expand) # black text binline = (1 - binline) le = lineest.CenterNormalizer(binline.shape[0]) # white text binline = binline.astype(float) le.measure(binline) binline = le.normalize(binline) binline = where(binline > 0.5, 0, 1) # black text # print 'line time ', time()-tt print '-----------------------' pilimg = Image.fromarray((binline * 255).astype(uint8)) pred_legacy = pytesseract.image_to_string(pilimg, lang='eng', config='--oem 0 --psm 7') print '00', pred_legacy pred_lstm = pytesseract.image_to_string(pilimg, lang='eng', config='--oem 1 --psm 7') print '11', pred_lstm # ASHOW('line',binline, scale=2.0) ## pred_both = pytesseract.image_to_string(pilimg,lang='vie', config='--oem 2 --psm 7') ## print '22', pred_both result = psegutils.record(bounds=l.bounds, text1=pred_legacy, text2=pred_lstm, img=binline) self.lines.append(result)
def simplefirstAnalyse(binary): binaryary = morph.r_closing(binary.astype(bool), (1,1)) labels,_ = morph.label(binaryary) objects = morph.find_objects(labels) ### <<<==== objects here smalldot = zeros(binaryary.shape, dtype=binary.dtype) scale = int(binary.shape[0]*0.7) for i,o in enumerate(objects): if (sl.width(o) < scale/2) or (sl.height(o) < scale/2): smalldot[o] = binary[o] if sl.dim0(o) > 3*scale: mask = where(labels[o] != (i+1),uint8(255),uint8(0)) binary[o] = cv2.bitwise_and(binary[o],binary[o],mask=mask) continue return objects, smalldot, scale
def remove_small_noise(binary, minsize = 50): labels, n = morph.label(binary) h, w = binary.shape objects = morph.find_objects(labels) space_to_edge = 10 sums = measurements.sum(binary, labels, range(n + 1)) sums = sums[labels] good = minimum(binary, 1 - (sums > 0) * (sums < minsize)) for i, b in enumerate(objects): cy, cx = sl.center(b) # if component is small and close to edge if (sl.area(b) < minsize * 1.2 and ((cx < space_to_edge or cx > w - space_to_edge) or (cy < space_to_edge or cy > h - space_to_edge))): good[b][labels[b] == i+1] = 0 return good
def compute_boxmap(binary, scale, oriimg, threshold=(.5, 4), dtype='i'): labels, n = morph.label(binary) objects = morph.find_objects(labels) boxmap = zeros(binary.shape, dtype) for i, o in enumerate(objects): h = sl.dim0(o) w = sl.dim1(o) ratio = float(h) / w if h > w else float(w) / h if h > 2 * scale or h < scale / 3: continue if ratio > 8: continue # if sl.area(o)**.5<threshold[0]*scale: continue # if sl.area(o)**.5>threshold[1]*scale: continue boxmap[o] = 1 return boxmap
def filter_junk_cc(binary, scale, maxsize): junk_cc = np.zeros(binary.shape, dtype='B') text_like = np.zeros(binary.shape, dtype='B') labels, _ = morph.label(binary) objects = morph.find_objects(labels) for i, b in enumerate(objects): if sl.width(b) > maxsize * scale or sl.area(b) > scale * scale * 8 or \ sl.aspect_normalized(b) > 8 or sl.min_dim(b) < scale * 0.35: junk_cc[b][labels[b] == i + 1] = 1 else: if sl.width(b) > 0.3 * scale and sl.height(b) > 0.3 * scale: text_like[b][labels[b] == i + 1] = 1 return junk_cc, text_like
def calc_typo_metric(binline): labels,n = morph.label(binline) objects = morph.find_objects(labels) filtered = [] max_h = 0 for o in objects: h = sl.dim0(o) w = sl.dim1(o) if h > binline.shape[0]*0.98: continue if h < 3 or w < 3: continue if (h > binline.shape[0]*0.2 and w > binline.shape[0]*0.2) or \ (o[0].start > binline.shape[0]/2 and o[1].stop > binline.shape[1]/4 and o[1].stop < 3*binline.shape[1]/4 and o[0].stop < binline.shape[0]*0.98): filtered.append(o) if h > max_h: max_h = h filtered.sort(key=lambda x:x[1].start) prech = None zoomforsee = 4 infoheight=50 info = np.zeros((infoheight*2+binline.shape[0]*zoomforsee,binline.shape[1]*zoomforsee)) for ch in filtered: h = sl.dim0(ch) w = sl.dim1(ch) if prech is not None and ch[1].start < (prech[1].start + prech[1].stop)/2: continue cv2.putText(info,'{:3.2f}'.format(1.0*w/max_h),\ ((ch[1].start)*zoomforsee, int(infoheight*0.4)), cv2.FONT_HERSHEY_SIMPLEX, \ 0.5,1.0,1) if prech is None: cv2.putText(info,'{:3d}'.format(max_h),\ ((ch[1].start)*zoomforsee, int(infoheight*0.9)), cv2.FONT_HERSHEY_SIMPLEX, \ 0.5,1.0,1) else: space = ch[1].start - prech[1].stop dist = ch[1].stop - prech[1].stop cv2.putText(info,'{:3.2f}'.format(1.0*space/max_h),\ ((prech[1].stop)*zoomforsee, int(infoheight*0.9)), cv2.FONT_HERSHEY_SIMPLEX, \ 0.5,1.0,1) cv2.putText(info,'({:3.2f})'.format(1.0*dist/max_h),\ ((prech[1].stop)*zoomforsee, int(infoheight*1.4)), cv2.FONT_HERSHEY_SIMPLEX, \ 0.5,1.0,1) prech = ch info[infoheight*2:,:] = cv2.resize(binline, (zoomforsee*binline.shape[1], zoomforsee*binline.shape[0])) return (info*250).astype(np.uint8), filtered
def compute_line_seeds(binary, bottom, top, colseps, scale): """Base on gradient maps, computes candidates for baselines and xheights. Then, it marks the regions between the two as a line seed.""" t = args.threshold # 0.2###############################################################more focus here for bigger fonts.!!! # print "SbU", t vrange = int(args.vscale * scale) bmarked = maximum_filter(bottom == maximum_filter(bottom, (vrange, 0)), (2, 2)) bmarked *= array((bottom > t * amax(bottom) * t) * (1 - colseps), dtype=bool) tmarked = maximum_filter(top == maximum_filter(top, (vrange, 0)), (2, 2)) tmarked *= array((top > t * amax(top) * t / 2) * (1 - colseps), dtype=bool) tmarked = maximum_filter(tmarked, (1, 20)) testseeds = zeros(binary.shape, 'i') seeds = zeros(binary.shape, 'i') delta = max(3, int(scale / 2)) for x in range(bmarked.shape[1]): transitions = sorted([(y, 1) for y in psegutils.find(bmarked[:, x])] + [(y, 0) for y in psegutils.find(tmarked[:, x])])[::-1] transitions += [(0, 0)] for l in range(len(transitions) - 1): y0, s0 = transitions[l] if s0 == 0: continue seeds[y0 - delta:y0, x] = 1 y1, s1 = transitions[l + 1] if s1 == 0 and (y0 - y1) < 5 * scale: seeds[y1:y0, x] = 1 seeds = maximum_filter(seeds, (1, int(1 + scale))) seeds *= (1 - colseps) ### ####imsave('/home/gupta/Documents/seeds.png', seeds) ####imsave('/home/gupta/Documents/top_bottom.png', [testseeds,0.3*tmarked+0.7*bmarked,binary]) ### ####imsave('/home/gupta/Documents/lineseeds.png', [seeds,0.3*tmarked+0.7*bmarked,binary]) # DSAVE("lineseeds",[seeds,0.3*tmarked+0.7*bmarked,binary]) seeds, _ = morph.label(seeds) return seeds
def compute_line_seeds(self, binary, bottom, top, colseps, scale): """Base on gradient maps, computes candidates for baselines and xheights. Then, it marks the regions between the two as a line seed.""" t = self.parameter['threshold'] vrange = int(self.parameter['vscale'] * scale) bmarked = maximum_filter(bottom == maximum_filter(bottom, (vrange, 0)), (2, 2)) bmarked *= np.array((bottom > t * np.amax(bottom) * t) * (1 - colseps), dtype=bool) tmarked = maximum_filter(top == maximum_filter(top, (vrange, 0)), (2, 2)) tmarked *= np.array((top > t * np.amax(top) * t / 2) * (1 - colseps), dtype=bool) tmarked = maximum_filter(tmarked, (1, 20)) testseeds = np.zeros(binary.shape, 'i') seeds = np.zeros(binary.shape, 'i') delta = max(3, int(scale / 2)) for x in range(bmarked.shape[1]): transitions = sorted([(y, 1) for y in psegutils.find(bmarked[:, x])] + [(y, 0) for y in psegutils.find(tmarked[:, x])])[::-1] transitions += [(0, 0)] for l in range(len(transitions) - 1): y0, s0 = transitions[l] if s0 == 0: continue seeds[y0 - delta:y0, x] = 1 y1, s1 = transitions[l + 1] if s1 == 0 and (y0 - y1) < 5 * scale: seeds[y1:y0, x] = 1 seeds = maximum_filter(seeds, (1, int(1 + scale))) seeds *= (1 - colseps) seeds, _ = morph.label(seeds) return seeds
def cut_dash_line(im, num_cells): binary = convert_to_binary(255-im, thres=0.5) labels, _ = morph.label(binary) objects = morph.find_objects(labels) scale = int(round(1.0 * binary.shape[1] / num_cells + 0.2)) h = binary.shape[0] - 1 # list to store objects for each cell cells = [[] for _ in range(num_cells)] cell_ims = [] for i, b in enumerate(objects): # only process object with width < 2 x scale if sl.width(b) < 2 * scale: x1, x2 = b[1].start, b[1].stop mid_x = (x1 + x2) // 2 cell_index = np.median([x1 // scale, x2 // scale, mid_x // scale]).astype(int) #print(cell_index) # handle case where digit from 2 cells connected if x2 - (cell_index + 1) * scale > 0.3 * scale: temp_b = (b[0], slice(b[1].start, (cell_index + 1) * (scale + 1), None)) print("2 char connected!!!") else: temp_b = b cells[cell_index].append(temp_b) for i, c in enumerate(cells): if len(c) > 0: x1 = min([obj[1].start for obj in c]) x2 = max([obj[1].stop for obj in c]) cell_ims.append(normalize_cell_img(im[0:h, x1:x2])) else: blank = np.zeros((h, scale)) cell_ims.append(normalize_cell_img(convert_binary_to_normal_im(blank))) return cell_ims
def printResult(self, outputfile): # Some pre-process # print 'text area before' # cv2.imshow('patch', self.patch) # cv2.waitKey(-1) if self.name == 'CMND cu - 9 so': k = 0.45 else: k = 0.33 patch = sharpen(self.patch) binary = sauvola(patch, w=int(self.template.shape[1] / 24.5 * 2), k=k, scaledown=0.5, reverse=True) binary = cv2.bitwise_and(binary, binary, mask=self.patch_mask) # print 'text area after' # cv2.imshow('patch', binary*255) # cv2.waitKey(-1) dotremoved = binary scale = self.scale # Line extraction copied from Ocropus source code bottom, top, boxmap = compute_gradmaps(dotremoved, scale) seeds0 = compute_line_seeds(dotremoved, bottom, top, scale) seeds, _ = morph.label(seeds0) llabels = morph.propagate_labels(boxmap, seeds, conflict=0) spread = spread_labels(seeds, maxdist=scale) llabels = where(llabels > 0, llabels, spread * dotremoved) segmentation = llabels * dotremoved dotremoved = ocrolib.remove_noise(dotremoved, 8) lines = psegutils.compute_lines(segmentation, scale / 2) binpage_reversed = 1 - dotremoved self.lines = [] readrs = dict.fromkeys(self.linepos1.keys(), u'') lines = sorted(lines, key=lambda x: x.bounds[1].start) for i, l in enumerate(lines): # Line extraction copied from Ocropus source code binline = psegutils.extract_masked(binpage_reversed, l, pad=int(scale / 2), expand=0) # black text binline = (1 - binline) le = lineest.CenterNormalizer(binline.shape[0]) # white text binline = binline.astype(float) le.measure(binline) binline = le.normalize(binline) # print 'normalized' # cv2.imshow('line', binline) # cv2.waitKey(-1) binline = cv2.resize(binline, None, fx=2.0, fy=2.0) # print 'resized' # cv2.imshow('line', binline) # cv2.waitKey(-1) binline = where(binline > 0.5, uint8(0), uint8(255)) # black text # print 'black text' # cv2.imshow('line', binline) # cv2.waitKey(-1) # pilimg = Image.fromarray(binline) pos = l.bounds[0].stop left = (l.bounds[1].start < self.template.shape[1] / 2) # Prediction using Tesseract 4.0 if pos > self.linepos1['idNumber'][0] and pos < self.linepos1[ 'idNumber'][1]: #ID, all numbers pred = ocr( binline, config= '--oem 0 --psm 7 -c tessedit_char_whitelist=0123456789') readrs['idNumber'] += pred + u' ' elif pos > self.linepos1['dateOfBirth'][0] and pos < self.linepos1[ 'dateOfBirth'][1]: # DOB, number, - , / pred = ocr( binline, config= '--oem 1 --psm 7 -c tessedit_char_whitelist=0123456789-/') readrs['dateOfBirth'] += pred + u' ' elif left and pos > self.linepos1['Gender'][ 0] and pos < self.linepos1['Gender'][1]: pred = ocr(binline, config='--oem 1 --psm 7 -l vie') readrs['Gender'] += pred + u' ' elif (not left) and pos > self.linepos1['Dantoc'][ 0] and pos < self.linepos1['Dantoc'][1]: pred = ocr(binline, config='--oem 1 --psm 7 -l vie') readrs['Dantoc'] += pred + u' ' elif pos > self.linepos1['NguyenQuan'][0] and pos < self.linepos1[ 'NguyenQuan'][1]: pred = ocr(binline, config='--oem 1 --psm 7 -l vie') readrs['NguyenQuan'] += pred + u' ' elif pos > self.linepos1['fullName'][0] and pos < self.linepos1[ 'fullName'][1]: pred = ocr(binline, config='--oem 1 --psm 7 -l vie') readrs['fullName'] += pred + u' ' # else: # pred = ocr(binline, config='--oem 1 --psm 7 -l vie') # print 'unknown ', unicode2ascii(pred), 'y:', l.bounds[0], 'x:', l.bounds[1] for k in readrs: readrs[k] = (readrs[k].replace(u'²', u'2').replace(u'º', u'o').replace( u'»', u'-')).strip() if len(readrs[k]) == 0: readrs[k] = None if self.name == 'CMND moi - 12 so': readrs['type'] = 'CMND Mới - 12 Số' elif self.name == 'Can Cuoc Cong Dan': readrs['type'] = 'Căn Cước Công Dân' elif self.name == 'CMND cu - 9 so': readrs['type'] = 'CMND Cũ - 9 Số' readrs['NgayHetHan'] = None outputfile.write(json.dumps(readrs))
def binary_objects(binary): labels,n = morph.label(binary) objects = morph.find_objects(labels) return objects
def detect_table(image, scale, maxsize=10, debug_path=None): h, w = image.shape[:2] if len(image.shape) > 2 and image.shape[2] >= 3: gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) else: gray = image # kernel = np.ones((5,5),np.uint8) # gray = 255-cv2.morphologyEx(255-gray, cv2.MORPH_CLOSE, kernel) binary = 1 - morph.thresh_sauvola(gray, k=0.05) junk_cc, _ = filter_junk_cc(binary, scale, maxsize) junk_cc = morph.r_closing(junk_cc, (5, 5)) print('calculating combine sep...') combine_sep = compute_combine_seps(junk_cc, scale) # using closing morphology to connect disconnected edges close_thes = int(scale * 0.15) closed_sep = morph.r_closing(combine_sep, (close_thes, close_thes)) if debug_path is not None: cv2.imwrite(filename[:-4] + '_bin.png', ((1 - junk_cc) * 255).astype('uint8')) cv2.imwrite(filename[:-4] + '_sep.png', (closed_sep * 255).astype('uint8')) labels, _ = morph.label(closed_sep) objects = morph.find_objects(labels) # result table list boxes = [] for i, b in enumerate(objects): if sl.width(b) > maxsize * scale or sl.area( b) > scale * scale * 10 or (sl.aspect_normalized(b) > 6 and sl.max_dim(b) > scale * 1.5): density = np.sum(combine_sep[b]) density = density / sl.area(b) if (sl.area(b) > scale * scale * 10 and sl.min_dim(b) > scale * 1.0 and sl.max_dim(b) > scale * 8 and density < 0.4): # calculate projection to determine table border w = sl.width(b) h = sl.height(b) region = (labels[b] == i + 1).astype('uint8') border_pad = max(w, h) border_thres = scale * 2 proj_x = np.sum(region, axis=0) proj_y = np.sum(region, axis=1) proj_x[3:] += proj_x[:-3] proj_y[3:] += proj_y[:-3] sep_x = np.sort([j[0] for j in np.argwhere(proj_x > 0.75 * h)]) sep_y = np.sort([j[0] for j in np.argwhere(proj_y > 0.4 * w)]) # skip if sep count < 2 if len(sep_x) < 1 or len(sep_y) < 1: continue border_left, border_right, border_top, border_bottom = None, None, None, None if sep_x[0] < border_pad: border_left = sep_x[0] if sep_x[-1] > w - border_pad: border_right = sep_x[-1] if sep_y[0] < border_pad: border_top = sep_y[0] if sep_y[-1] > h - border_pad: border_bottom = sep_y[-1] # print_info(border_top, border_bottom, border_left, border_right) if all([ j is not None for j in [border_top, border_bottom, border_left, border_right] ]): border_right = b[1].stop - b[1].start boxes.append([ b[1].start + border_left, b[0].start + border_top, b[1].start + border_right, b[0].start + border_bottom ]) # boxes.append(([b[1].start, b[0].start, b[1].stop, b[0].stop])) return boxes
def extractLines(imgpath, param): img_grey = ocrolib.read_image_gray(imgpath) (h, w) = img_grey.shape[:2] img00 = cv2.resize(img_grey[h / 4:3 * h / 4, w / 4:3 * w / 4], None, fx=0.5, fy=0.5) angle = estimate_skew_angle(img00, linspace(-5, 5, 42)) print 'goc', angle rotM = cv2.getRotationMatrix2D((w / 2, h / 2), angle, 1) img_grey = cv2.warpAffine(img_grey, rotM, (w, h)) h, w = img_grey.shape img_grey = cv2.normalize(img_grey.astype(float32), None, 0.0, 0.999, cv2.NORM_MINMAX) binary = sauvola(img_grey, w=param.w, k=param.k, scaledown=0.2, reverse=True) ### PARAM binary = morph.r_closing(binary.astype(bool), (args.connect, 1)) binaryary = binary[h / 4:3 * h / 4, w / 4:3 * w / 4] binary = binary.astype(np.uint8) labels, n = morph.label(binaryary) objects = morph.find_objects(labels) bysize = sorted(objects, key=sl.area) scalemap = zeros(binaryary.shape) for o in bysize: if amax(scalemap[o]) > 0: continue scalemap[o] = sl.area(o)**0.5 scale = median(scalemap[(scalemap > 3) & (scalemap < 100)]) objects = psegutils.binary_objects(binary) boxmap = zeros(binary.shape, dtype=np.uint8) imgwidth = binary.shape[1] imgheight = binary.shape[0] cellwidth = 6 * scale cellheight = 2.5 * scale N_x = int(round(imgwidth / cellwidth)) cellwidth = int(round(imgwidth / N_x)) N_y = int(round(imgheight / cellheight)) cellheight = int(round(imgheight / N_y)) cells_list = [{}, {}, {}, {}] def pixel2cell2id(pixel_x, pixel_y, CELLTYPE): dx = 0 dy = 0 if CELLTYPE == 3: pixel_x -= cellwidth / 2 pixel_y -= cellheight / 2 dx = cellwidth / 2 dy = cellheight / 2 if CELLTYPE == 2: pixel_x -= cellwidth / 2 dx = cellwidth / 2 if CELLTYPE == 1: pixel_y -= cellheight / 2 dy = cellheight / 2 if pixel_x <= 0 or pixel_y <= 0: return None, None cellcoord = (pixel_x / cellwidth, pixel_y / cellheight) cellid = cellcoord[0] + cellcoord[1] * N_x cellcoord = (cellcoord[0] * cellwidth + dx, cellcoord[1] * cellheight + dy) return cellcoord, cellid def id2cell2pixel(cellid, x, y, CELLTYPE): cellcoord = (cellid % N_x, cellid / N_x) pixel_x = cellcoord[0] * cellwidth + x pixel_y = cellcoord[1] * cellheight + y if CELLTYPE == 3: pixel_x += cellwidth / 2 pixel_y += cellheight / 2 return cellcoord, pixel_x, pixel_y img_grey = (cv2.cvtColor(img_grey, cv2.COLOR_GRAY2BGR) * 255).astype( np.uint8) for o in objects: h = sl.dim0(o) w = sl.dim1(o) ratio = float(w) / h ### Dirty cheat if ratio > 1 and ratio < 6: recommended_width = max(int(0.6 * (o[0].stop - o[0].start)), int(scale * 0.6), 5) for pos in range(o[1].start + recommended_width, o[1].stop, recommended_width): binary[o[0].start:o[0].stop, pos:pos + 1] = np.uint8(0) objects = psegutils.binary_objects(binary) for o in objects: h = sl.dim0(o) w = sl.dim1(o) a = h * w # black = float(sum(binary[o]))/a # if sl.area(o)**.5<threshold[0]*scale: continue # if sl.area(o)**.5>threshold[1]*scale: continue if h > 5 * scale: continue # if h < 0.4*scale: continue if w > 4 * scale and (h > 2 * scale or h < 0.5 * scale): continue if a < 0.25 * scale * scale: continue if float(h) / w > 10: continue ratio = float(w) / h if ratio > 10: continue ### Add object as candidate character pixel_x, pixel_y = (o[1].start + o[1].stop) / 2, o[0].stop for celltype in range(4): cellcoord, cellid = pixel2cell2id(pixel_x, pixel_y, CELLTYPE=celltype) if cellcoord is None or cellid is None: continue cellbound = slice(cellcoord[1], cellcoord[1] + cellheight, None), slice(cellcoord[0], cellcoord[0] + cellwidth, None) if cellid not in cells_list[celltype]: cells_list[celltype][cellid] = SubLineFinder( window_size=max(3, scale / 6), cellbound=cellbound, initChar=o) else: cells_list[celltype][cellid].addChar(o) y0 = o[0].start y1 = o[0].stop - 3 if o[0].stop - o[0].start > 8 else o[0].start + 5 x0 = o[1].start x1 = o[1].stop - 3 if o[1].stop - o[1].start > 8 else o[1].start + 5 boxmap[y0:y1, x0:x1] = 1 for celltype in range(4): if celltype == 0: col = (255, 0, 0) if celltype == 1: col = (0, 255, 0) if celltype == 2: col = (255, 255, 0) if celltype == 3: col = (0, 0, 255) for cellid, subline in cells_list[celltype].iteritems(): # cv2.rectangle(img_grey, (subline.cellbound[1].start+celltype, subline.cellbound[0].start+celltype), (subline.cellbound[1].stop+celltype, subline.cellbound[0].stop+celltype), col,1) line = subline.subline() if line is not None: pos1 = (int(line[0][0]), int(line[0][1])) pos2 = (int(line[1][0]), int(line[1][1])) # print cellid, pos1, pos2 cv2.line(img_grey, pos1, pos2, col, 1) ### illustrate/debug first round return binary, cv2.add(img_grey, (boxmap[:, :, np.newaxis] * np.array([0, 50, 50])).astype(np.uint8))
def remove_small_noise_and_seps(im, num_cells, minsize = 30): im = 255 - im binary = np.array(im>0.5*(np.amin(im)+np.amax(im))).astype('uint8') # invert h, w = im.shape scale = int(w / num_cells) labels, n = morph.label(binary) objects = morph.find_objects(labels) # remove small noise using connected components sums = measurements.sum(binary, labels, range(n + 1)) sums = sums[labels] good = minimum(binary, 1 - (sums > 0) * (sums < minsize)) # calculate sep bar positions from junk cc junk_cc = np.bitwise_xor(binary, good) # remove long connected component (solid separators) proj_x = np.sum(binary, axis=0) mask_x = np.tile((proj_x > h * 0.8).astype('B'), h) solid_sep_pos = [j[0] for j in np.argwhere(proj_x > h * 0.6)] good[mask_x] = 0 '''for i, b in enumerate(objects): if sl.width(b) < 6 and sl.height(b) > h * 0.9: good[b][labels[b] == i + 1] = 0 ''' if np.sum(junk_cc) > 140: # only detect sep bars if has enough pixels proj_x = np.sum(junk_cc, axis=0) mask_x = proj_x > np.amax(proj_x) * 0.2 sep_pos = np.array([i[0] for i in np.argwhere(mask_x)]) start = [True] + [True if abs(sep_pos[i] - sep_pos[i-1] - scale) < 5 or abs(sep_pos[i] - sep_pos[i-1] - 2 * scale) < 5 else False for i in range(1,len(sep_pos))] else: sep_pos = [] if len(sep_pos) > 0: start_sep_pos = sep_pos[start] #print(start_sep_pos) # fill-in missing pos '''for i in range(1,len(start_sep_pos)): if start_sep_pos[i] - start_sep_pos[i-1] > scale + 4: mid = (start_sep_pos[i] + start_sep_pos[i-1]) // 2 good[0:h, mid:mid + 5] = 0 ''' # fill seps start from begin sep with scale space if len(start_sep_pos) > 0 and len(solid_sep_pos) > 0: pos_x = start_sep_pos[0] scale = int(round(1.0 * w / num_cells) + 0.1) while pos_x < w: if any(x in solid_sep_pos for x in range(pos_x-3,pos_x+4)): pos_x = min([x for x in range(pos_x-3,pos_x+4) if x in solid_sep_pos]) good[0:h,pos_x:pos_x+5] = 0 pos_x += scale else: # handle special case for 2 cells if w / scale > 1.5 and w / scale < 2.6: mid = w // 2 good[0:h, mid:mid + 5] = 0 else: # fill seps start from solid sep with scale space proj_x = np.sum(good, axis=0) mask_x = proj_x > h * 0.9 sep_pos = np.array([i[0] for i in np.argwhere(mask_x)]) pos_x = scale if len(sep_pos) == 0 else sep_pos[0] while pos_x < w: good[0:h, pos_x:pos_x + 5] = 0 pos_x += scale + 1 return np.array((1-good) * 255).astype('uint8')