Beispiel #1
0
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')
Beispiel #4
0
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')
Beispiel #5
0
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
Beispiel #6
0
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
Beispiel #7
0
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)
Beispiel #8
0
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
Beispiel #10
0
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")
Beispiel #11
0
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
Beispiel #12
0
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
Beispiel #13
0
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')
Beispiel #14
0
    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)
Beispiel #15
0
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
Beispiel #16
0
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
Beispiel #17
0
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
Beispiel #18
0
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
Beispiel #19
0
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
Beispiel #20
0
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
Beispiel #22
0
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
Beispiel #23
0
    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))
Beispiel #24
0
def binary_objects(binary):
    labels,n = morph.label(binary)
    objects = morph.find_objects(labels)
    return objects
Beispiel #25
0
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))
Beispiel #27
0
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')