Пример #1
0
def text_line_segmentation(binary, scale=None, gray=None, num_col = 1):
    """Given a binary image, compute a complete segmentation into
    lines, computing both columns and text lines."""
    binary = array(binary, 'B')
    if scale is None:
        scale = psegutils.estimate_scale(binary)

    # do the column finding
    if num_col > 1:
        colseps, binary = compute_colseps(binary, scale)
    else:
        colseps = np.zeros(binary.shape)

    # now compute the text line seeds
    bottom, top, boxmap = compute_gradmaps(binary, scale)
    seeds = compute_line_seeds(binary, bottom, top, colseps, scale)

    # spread the text line seeds to all the remaining components
    llabels = morph.propagate_labels(boxmap, seeds, conflict=0)
    spread = morph.spread_labels(seeds, maxdist=scale)
    llabels = where(llabels > 0, llabels, spread * binary)
    segmentation = llabels * binary

    lines = psegutils.compute_lines(segmentation, scale, 0.8)
    line_ims = []

    for l in lines:
        if gray is None:
            binline = psegutils.extract_masked(1-binary, l, pad=0)
        else:
            binline = psegutils.extract_masked(gray, l, pad=0)
        binline = pad_by(binline, 10, invert=False)
        line_ims.append(binline)

    return line_ims, lines
Пример #2
0
def extract(image):

    try:
        binary = ocrolib.read_image_binary(image)
        binary = 1-binary

        scale = psegutils.estimate_scale(binary)
        segmentation = compute_segmentation(binary,scale)

        # ...lines = compute_lines(segmentation,scale)

        # compute the reading order
        lines = psegutils.compute_lines(segmentation,scale)
        order = psegutils.reading_order([l.bounds for l in lines])
        lsort = psegutils.topsort(order)

        # renumber the labels so that they conform to the specs
        nlabels = amax(compute_segmentation)+1
        renumber = zeros(nlabels,'i')
        for i,v in enumerate(lsort): renumber[lines[v].label] = 0x010000+(i+1)
        segmentation = renumber[segmentation]

        outputdir = "http://127.0.0.1:5000/uploads/"
        
        lines = [lines[i] for i in lsort]
        ocrolib.write_page_segmentation("%s.pseg.png"%outputdir,segmentation)


        cleaned = ocrolib.remove_noise(binary,args.noise)
        for i,l in enumerate(lines):
            binline = psegutils.extract_masked(1-cleaned,l,pad=args.pad,expand=args.expand)
            ocrolib.write_image_binary("%s/01%04x.bin.png"%(outputdir,i+1),binline)
        #print "%6d"%i,fname,"%4.1f"%scale,len(lines)
    except:
        print ('error')
Пример #3
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)
Пример #4
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))
Пример #5
0
def analyze_page_layout(binary, gray, rgb=None):
    hscale = 1.0  # Non-standard scaling of horizontal parameters.
    vscale = 1.0  # Non-standard scaling of vertical parameters.
    threshold = 0.2  # baseline threshold.
    usegauss = True  # Use gaussian instead of uniform.
    maxseps = 0  # Maximum black column separators.
    sepwiden = 10  # Widen black separators (to account for warping).
    blackseps = True
    maxcolseps = 3  # Maximum # whitespace column separators.
    csminheight = 10  # Minimum column height (units=scale).
    noise = 8  # Noise threshold for removing small components from lines.
    gray_output = True  # Output grayscale lines as well, which are extracted from the grayscale version of the pages.
    pad = 3  # Padding for extracted lines.
    expand = 3  # Expand mask for grayscale extraction.

    if False:
        bin_image_filepath = './ocropy_test.bin.png'
        gray_image_filepath = './ocropy_test.nrm.png'

        binary = ocrolib.read_image_binary(bin_image_filepath)
        gray = ocrolib.read_image_gray(gray_image_filepath)

    binary = 1 - binary  # Invert.

    scale = psegutils.estimate_scale(binary)
    segmentation = compute_segmentation(binary,
                                        scale,
                                        blackseps,
                                        maxseps,
                                        maxcolseps,
                                        csminheight,
                                        sepwiden,
                                        usegauss,
                                        vscale,
                                        hscale,
                                        threshold,
                                        quiet=True)

    lines = psegutils.compute_lines(segmentation, scale)
    order = psegutils.reading_order([l.bounds for l in lines])
    lsort = psegutils.topsort(order)

    # Renumber the labels so that they conform to the specs.
    nlabels = np.amax(segmentation) + 1
    renumber = np.zeros(nlabels, 'i')
    for i, v in enumerate(lsort):
        renumber[lines[v].label] = 0x010000 + (i + 1)
    segmentation = renumber[segmentation]  # Image.

    lines = [lines[i] for i in lsort]

    # Visualize bounding boxes.
    if False:
        if rgb is not None:
            # REF [function] >> extract_masked() in ${OCROPY_HOME}/ocrolib/psegutils.py.
            for l in lines:
                y0, x0, y1, x1 = [
                    int(x) for x in [
                        l.bounds[0].start, l.bounds[1].start, l.bounds[0].stop,
                        l.bounds[1].stop
                    ]
                ]
                cv2.rectangle(rgb, (x0, y0), (x1, y1), (0, 0, 255), 1,
                              cv2.LINE_AA)
            cv2.imshow('Image', rgb)
            cv2.waitKey(0)

    # Output everything.
    if False:
        if not os.path.exists(outputdir):
            os.mkdir(outputdir)

        ocrolib.write_page_segmentation("%s.pseg.png" % outputdir,
                                        segmentation)
        cleaned = ocrolib.remove_noise(binary, noise)
        for i, l in enumerate(lines):
            binline = psegutils.extract_masked(1 - cleaned,
                                               l,
                                               pad=pad,
                                               expand=expand)  # Image.
            ocrolib.write_image_binary(
                "%s/01%04x.bin.png" % (outputdir, i + 1), binline)
            if gray_output:
                grayline = psegutils.extract_masked(gray,
                                                    l,
                                                    pad=pad,
                                                    expand=expand)  # Image.
                ocrolib.write_image_gray(
                    "%s/01%04x.nrm.png" % (outputdir, i + 1), grayline)
Пример #6
0
def processPngFile(outRoot, origFile, fileNum):
    baseName = os.path.basename(origFile)
    baseBase, _ = os.path.splitext(baseName)
    outDir = os.path.join(outRoot, "%s.%03d" % (baseBase, fileNum))
    inFile = os.path.join(outDir, baseName)

    os.makedirs(outDir, exist_ok=True)
    shutil.copy(origFile, inFile)

    inBase, _ = ocrolib.allsplitext(inFile)
    print("**  inBase=%s" % inBase)
    # print("** binBase=%s" % binBase)

    fname = inFile
    outputdir = inBase
    binFile = inBase + ".bin.png"
    outFile = inBase + ".out.png"
    outRoot2, outDir2 = os.path.split(outRoot)
    outFile2 = os.path.join(outRoot2, "%s.out" % outDir2, baseName)
    print("outFile2=%s" % outFile2)
    # assert False
    grayFile = inBase + ".nrm.png"
    psegFile = inBase + ".pseg.png"
    print("  inFile=%s" % inFile)
    print(" binFile=%s" % binFile)
    print("grayFile=%s" % grayFile)
    print(" outFile=%s" % outFile)
    assert inFile and binFile
    assert outFile != inFile
    assert outFile != binFile

    if not binarize(inFile, binFile, grayFile):
        binExists = os.path.exists(binFile)
        print("Couldn't binarize inFile=%s binFile=%s exists=%s" %
              (inFile, binFile, binExists))
        return False

    binary = ocrolib.read_image_binary(binFile)
    print("$$ %s=%s" % (binFile, desc(binary)))
    height, width = binary.shape
    checktype(binary, ABINARY2)
    check = check_page(np.amax(binary) - binary)
    if check is not None:
        print("%s SKIPPED %s (use -n to disable this check)" % (inFile, check))
        return False

    # if args.gray:
    #     if os.path.exists(base+".nrm.png"):
    #         gray = ocrolib.read_image_gray(base+".nrm.png")
    #         checktype(gray, GRAYSCALE)
    #     else:
    #         print_error("Grayscale version %s.nrm.png not found. Use ocropus-nlbin for creating " +
    #                     "normalized grayscale version of the pages as well." % base)
    #         return

    binary = 1 - binary  # invert

    scale = psegutils.estimate_scale(binary)
    print("scale %f" % scale)
    if np.isnan(scale) or scale > 1000.0:
        print("%s: bad scale (%g); skipping\n" % (fname, scale))
        return False

    # find columns and text lines
    print("computing segmentation")
    segmentation = compute_segmentation(binary, scale)
    if np.amax(segmentation) > maxlines:
        print("%s: too many lines %g" % (fname, np.amax(segmentation)))
        return False

    print("segmentation=%s" % desc(segmentation))
    print("number of lines %g" % np.amax(segmentation))

    # compute the reading order
    print("finding reading order")
    lines = psegutils.compute_lines(segmentation, scale)
    order = psegutils.reading_order([l.bounds for l in lines])
    lsort = psegutils.topsort(order)
    print("$$ lsort = %d = %s...%s" % (len(lsort), lsort[:10], lsort[-10:]))

    # renumber the labels so that they conform to the specs
    nlabels = np.amax(segmentation) + 1
    renumber = np.zeros(nlabels, 'i')
    for i, v in enumerate(lsort):
        renumber[lines[v].label] = 0x010000 + (i + 1)
    segmentation = renumber[segmentation]

    # finally, output everything
    print("writing lines")
    if not os.path.exists(outputdir):
        os.mkdir(outputdir)
    lines = [lines[i] for i in lsort]
    ocrolib.write_page_segmentation("%s.pseg.png" % outputdir, segmentation)
    cleaned = ocrolib.remove_noise(binary, noise)
    for i, l in enumerate(lines):
        binline = psegutils.extract_masked(1 - cleaned,
                                           l,
                                           pad=pad,
                                           expand=expand)
        ocrolib.write_image_binary("%s/01%04x.bin.png" % (outputdir, i + 1),
                                   binline)
        # if args.gray:
        #     grayline = psegutils.extract_masked(
        #         gray, l, pad=args.pad, expand=args.expand)
        #     ocrolib.write_image_gray("%s/01%04x.nrm.png" % (outputdir, i+1), grayline)
    print("%6d  %s %4.1f %d" % (i, fname, scale, len(lines)))

    # to proceed, we need a pseg file and a subdirectory containing text lines
    assert os.path.exists(psegFile), "%s: no such file" % psegFile
    assert os.path.isdir(inBase), "%s: no such directory" % inBase

    # iterate through the text lines in reading order, based on the page segmentation file
    pseg = ocrolib.read_page_segmentation(psegFile)
    print("$$ %s=%s" % (psegFile, desc(pseg)))

    regions = ocrolib.RegionExtractor()
    print("$$ regions=%s" % regions)
    regions.setPageLines(pseg)

    im = Image.open(inFile)
    print("~~%s %s" % (inFile, im.size))
    print("$$ regions=%s=%s" % (regions, sorted(regions.__dict__)))
    print("$$ regions.length=%s" % regions.length())

    n = regions.length()
    for i in range(1, n):

        id = regions.id(i)
        y0, x0, y1, x1 = regions.bbox(i)
        # print("%5d: 0x%05X %s %d x %d" %
        #       (i, id, [y0, x0, y1, x1], y1 - y0, x1 - x0))

        draw = ImageDraw.Draw(im)
        draw.rectangle((x0, y0, x1, y1), outline=(255, 0, 0), width=3)
        draw.rectangle((x0, y0, x1, y1), outline=(0, 0, 255), width=0)
        # draw.rectangle((x0, y0, x1, y1), outline=255, width=5)
        # draw.rectangle((x0, y0, x1, y1), outline=10,  width=1)
        del draw

    # write output files
    print("outFile=%s" % outFile)
    im.save(outFile, "PNG")
    print("outFile2=%s" % outFile2)
    outDir2 = os.path.dirname(outFile2)
    os.makedirs(outDir2, exist_ok=True)
    im.save(outFile2, "PNG")
    assert os.path.exists(outFile2)
    # outFile3, _ = os.path.splitext(outFile)
    # outFile3 = "%s.jpg" % outFile3
    # print("outFile3=%s" % outFile3)
    # im.save(outFile3, "JPEG")
    # assert os.path.exists(outFile3)
    return True
Пример #7
0
def process(job):
    imagepath, i = job
    global base
    base, _ = ocrolib.allsplitext(imagepath)
    outputdir = base
    imagename_base = os.path.basename(os.path.normpath(base))

    try:
        binary = ocrolib.read_image_binary(imagepath)
    except IOError:
        if ocrolib.trace: traceback.print_exc()
        print_error("cannot open either %s.bin.png or %s" % (base, imagepath))
        return

    checktype(binary, ABINARY2)

    if not args['nocheck']:
        check = check_page(amax(binary) - binary)
        if check is not None:
            print_error("%s SKIPPED %s (use -n to disable this check)" %
                        (imagepath, check))
            return

    binary = 1 - binary  # invert

    if args['scale'] == 0:
        scale = psegutils.estimate_scale(binary)
    else:
        scale = args['scale']
    print_info("scale %f" % (scale))
    if isnan(scale) or scale > 1000.0:
        print_error("%s: bad scale (%g); skipping\n" % (imagepath, scale))
        return
    if scale < args['minscale']:
        print_error("%s: scale (%g) less than --minscale; skipping\n" %
                    (imagepath, scale))
        return

    # find columns and text lines

    if not args['quiet']: print_info("computing segmentation")
    segmentation = compute_segmentation(binary, scale)
    if amax(segmentation) > args['maxlines']:
        print_error("%s: too many lines %g" % (imagepath, amax(segmentation)))
        return
    if not args['quiet']: print_info("number of lines %g" % amax(segmentation))

    # compute the reading order

    if not args['quiet']: print_info("finding reading order")
    lines = psegutils.compute_lines(segmentation, scale)
    order = psegutils.reading_order([l.bounds for l in lines])
    lsort = psegutils.topsort(order)

    # renumber the labels so that they conform to the specs

    nlabels = amax(segmentation) + 1
    renumber = zeros(nlabels, 'i')
    for i, v in enumerate(lsort):
        renumber[lines[v].label] = 0x010000 + (i + 1)
    segmentation = renumber[segmentation]

    # finally, output everything
    if not args['quiet']: print_info("writing lines")
    if not os.path.exists(outputdir):
        os.mkdir(outputdir)
    lines = [lines[i] for i in lsort]
    ocrolib.write_page_segmentation("%s.pseg.png" % outputdir, segmentation)
    cleaned = ocrolib.remove_noise(binary, args['noise'])
    for i, l in enumerate(lines):
        binline = psegutils.extract_masked(1 - cleaned,
                                           l,
                                           pad=args['pad'],
                                           expand=args['expand'])
        ocrolib.write_image_binary(
            "%s/%s_01%04x.bin.png" % (outputdir, imagename_base, i + 1),
            binline)
    print_info("%6d  %s %4.1f %d" % (i, imagepath, scale, len(lines)))
    return outputdir
Пример #8
0
def process1(job):
    fname, i = job
    global base
    base, _ = ocrolib.allsplitext(fname)
    outputdir = base

    try:
        binary = ocrolib.read_image_binary(base + ".bin.png")
    except IOError:
        try:
            binary = ocrolib.read_image_binary(fname)
        except IOError:
            if ocrolib.trace:
                traceback.print_exc()
            print("cannot open either", base + ".bin.png", "or", fname)
            return

    checktype(binary, ABINARY2)

    if not args.nocheck:
        check = check_page(amax(binary) - binary)
        if check is not None:
            print(fname, "SKIPPED", check, "(use -n to disable this check)")
            return

    if args.gray:
        if os.path.exists(base + ".nrm.png"):
            gray = ocrolib.read_image_gray(base + ".nrm.png")
        checktype(gray, GRAYSCALE)

    binary = 1 - binary  # invert

    if args.scale == 0:
        scale = psegutils.estimate_scale(binary)
    else:
        scale = args.scale
    print("scale", scale)
    if isnan(scale) or scale > 1000.0:
        sys.stderr.write("%s: bad scale (%g); skipping\n" % (fname, scale))
        return
    if scale < args.minscale:
        sys.stderr.write("%s: scale (%g) less than --minscale; skipping\n" %
                         (fname, scale))
        return

    # find columns and text lines

    if not args.quiet:
        print("computing segmentation")
    segmentation = compute_segmentation(binary, scale)
    if amax(segmentation) > args.maxlines:
        print(fname, ": too many lines", amax(segmentation))
        return
    if not args.quiet:
        print("number of lines", amax(segmentation))

    # compute the reading order

    if not args.quiet:
        print("finding reading order")
    lines = psegutils.compute_lines(segmentation, scale)
    order = psegutils.reading_order([l.bounds for l in lines])
    lsort = psegutils.topsort(order)

    # renumber the labels so that they conform to the specs

    nlabels = amax(segmentation) + 1
    renumber = zeros(nlabels, 'i')
    for i, v in enumerate(lsort):
        renumber[lines[v].label] = 0x010000 + (i + 1)
    segmentation = renumber[segmentation]

    # finally, output everything

    if not args.quiet:
        print("writing lines")
    if not os.path.exists(outputdir):
        os.mkdir(outputdir)
    lines = [lines[i] for i in lsort]
    ocrolib.write_page_segmentation("%s.pseg.png" % outputdir, segmentation)
    cleaned = ocrolib.remove_noise(binary, args.noise)
    for i, l in enumerate(lines):
        binline = psegutils.extract_masked(1 - cleaned,
                                           l,
                                           pad=args.pad,
                                           expand=args.expand)
        ocrolib.write_image_binary("%s/01%04x.bin.png" % (outputdir, i + 1),
                                   binline)
        if args.gray:
            grayline = psegutils.extract_masked(gray,
                                                l,
                                                pad=args.pad,
                                                expand=args.expand)
            ocrolib.write_image_gray("%s/01%04x.nrm.png" % (outputdir, i + 1),
                                     grayline)
    print("%6d" % i, fname, "%4.1f" % scale, len(lines))