ocrd_cis/ocropy/common.py
def compute_segmentation(binary,
                         zoom=1.0,
                         fullpage=False,
                         seps=None,
                         maxcolseps=2,
                         maxseps=0,
                         maximages=0,
                         csminheight=4,
                         hlminwidth=10,
                         spread_dist=None,
                         rl=False,
                         bt=False):
    """Find text line segmentation within a region or page.

    Given a binarized (and inverted) image as Numpy array ``image``, compute
    a complete segmentation of it into text lines as a label array.

    If ``fullpage`` is false, then avoid single-line horizontal splits.

    If ``fullpage`` is true, then
    - allow all horizontal splits, and search
    - for up to ``maxcolseps`` multi-line vertical whitespaces
      (as column separators, counted piece-wise) of at least
      ``csminheight`` multiples of ``scale``,
    - for up to ``maxseps`` vertical black lines
      (as column separators, counted piece-wise) of at least
      ``csminheight`` multiples of ``scale``, and
    - for any number of horizontal lines of at least
      ``hlminwidth`` multiples of ``scale``,
    - for anything in ``seps`` if given,
    then suppress these separator components and return them separately.
    
    Labels will be projected ("spread") from the foreground to the
    surrounding background within ``spread_dist`` distance (or half
    the estimated scale).
    
    Respect the given reading order:
    - ``rl``, whether to sort text lines in reverse order (from right to left),
    - ``bt``, whether to sort text lines in reverse order (from bottom to top).

    Return a tuple of:
    - Numpy array of the textline background labels
      (not the foreground or the masked image;
       foreground may remain unlabelled for
       separators and other non-text like small
       noise, or large drop-capitals / images),
    - Numpy array of horizontal foreground lines mask,
    - Numpy array of vertical foreground lines mask,
    - Numpy array of large/non-text foreground component mask,
    - Numpy array of vertical background separators mask,
    - the estimated scale (i.e. median sqrt bbox area of glyph components).
    """
    # TODO generalize to multi-scale (with `scale` as group array instead of float)
    DSAVE("input_binary",binary)
    LOG.debug('estimating glyph scale')
    scale = psegutils.estimate_scale(binary, zoom)
    LOG.debug('height: %d, zoom: %.2f, scale: %d', binary.shape[0], zoom, scale)

    if fullpage:
        LOG.debug('computing images')
        images = compute_images(binary, scale, maximages=maximages)
        LOG.debug('computing horizontal/vertical line separators')
        hlines = compute_hlines(binary, scale, hlminwidth=hlminwidth, images=images)
        vlines = compute_separators_morph(binary, scale, csminheight=csminheight, maxseps=maxseps, images=images)
        binary = np.minimum(binary,1-hlines)
        binary = np.minimum(binary,1-vlines)
        binary = np.minimum(binary,1-images)
        if seps is not None:
            # suppress separators/images for line estimation
            binary = (1-seps) * binary
    else:
        hlines = np.zeros_like(binary, np.bool)
        vlines = np.zeros_like(binary, np.bool)
        images = np.zeros_like(binary, np.bool)

    LOG.debug('computing gradient map')
    bottom, top, boxmap = compute_gradmaps(binary, scale,
                                           usegauss=False,
                                           fullpage=fullpage)
    if fullpage:
        LOG.debug('finding whitespace column separators')
        colseps = compute_colseps_conv(binary, scale,
                                       maxcolseps=maxcolseps,
                                       csminheight=csminheight)
        DSAVE("colseps",0.7*colseps+0.3*binary)
        # get a larger (closed) mask of all separators
        # (both bg boundary and fg line seps, detected
        # and passed in) to separate line/column labels
        sepmask = np.maximum(hlines, vlines)
        sepmask = np.maximum(sepmask, images)
        sepmask = np.maximum(sepmask, colseps)
        if seps is not None:
            sepmask = np.maximum(sepmask, seps)
        sepmask = morph.r_closing(sepmask, (scale, scale))
        DSAVE("sepmask",0.7*sepmask+0.3*binary)
    else:
        colseps = np.zeros(binary.shape, np.uint8)
        sepmask = np.zeros(binary.shape, np.uint8)

    LOG.debug('computing line seeds')
    seeds = compute_line_seeds(binary, bottom, top, sepmask, scale)
    if fullpage:
        # filter labels that have only noise fg (e.g. from split)
        invalid = np.setdiff1d(seeds.flatten(), (seeds*binary*boxmap).flatten())
        relabel = np.arange(np.max(seeds)+1)
        relabel[invalid] = 0
        seeds = relabel[seeds]
        DSAVE("lineseeds_filtered", [seeds,binary])
    else:
        seeds = hmerge_line_seeds(binary, seeds, scale)
    
    LOG.debug('spreading seed labels')
    # spread labels from seeds to bg, but watch fg,
    # voting for majority on bg conflicts,
    # but splitting on seed conflicts
    llabels = morph.propagate_labels_majority(binary, seeds)
    llabels2 = morph.propagate_labels(binary, seeds, conflict=0)
    conflicts = llabels > llabels2
    llabels = np.where(conflicts, seeds, llabels)
    # (protect sepmask as a temporary label)
    seplabel = np.max(seeds)+1
    llabels[sepmask>0] = seplabel
    spread = morph.spread_labels(llabels, maxdist=spread_dist or scale/2)
    DSAVE('lineseeds_spread', [spread,binary])
    llabels2 = morph.propagate_labels_majority(binary, spread)
    llabels = np.where(seeds, seeds, llabels2)
    llabels[sepmask>0] = seplabel
    llabels = morph.spread_labels(llabels, maxdist=spread_dist or scale/2)
    llabels[llabels==seplabel] = 0
    DSAVE('llabels', [llabels,binary])
    
    LOG.debug('sorting labels by reading order')
    llabels = morph.reading_order(llabels,rl,bt)[llabels]
    DSAVE('llabels_ordered', llabels)
    
    #segmentation = llabels*binary
    #return segmentation
    return llabels, hlines, vlines, images, colseps, scale
Find text line segmentation within a region or page.

    Given a binarized (and inverted) image as Numpy array ``image``, compute
    a complete segmentation of it into text lines as a label array.

    If ``fullpage`` is false, then avoid single-line horizontal splits.

    If ``fullpage`` is true, then
    - allow all horizontal splits, and search
    - for up to ``maxcolseps`` multi-line vertical whitespaces
      (as column separators, counted piece-wise) of at least
      ``csminheight`` multiples of ``scale``,
    - for up to ``maxseps`` vertical black lines
      (as column separators, counted piece-wise) of at least
      ``csminheight`` multiples of ``scale``, and
    - for any number of horizontal lines of at least
      ``hlminwidth`` multiples of ``scale``,
    - for anything in ``seps`` if given,
    then suppress these separator components and return them separately.
    
    Labels will be projected ("spread") from the foreground to the
    surrounding background within ``spread_dist`` distance (or half
    the estimated scale).
    
    Respect the given reading order:
    - ``rl``, whether to sort text lines in reverse order (from right to left),
    - ``bt``, whether to sort text lines in reverse order (from bottom to top).

    Return a tuple of:
    - Numpy array of the textline background labels
      (not the foreground or the masked image;
       foreground may remain unlabelled for
       separators and other non-text like small
       noise, or large drop-capitals / images),
    - Numpy array of horizontal foreground lines mask,
    - Numpy array of vertical foreground lines mask,
    - Numpy array of large/non-text foreground component mask,
    - Numpy array of vertical background separators mask,
    - the estimated scale (i.e. median sqrt bbox area of glyph components).
    
Find text line segmentation within a region or page.
Given a binarized (and inverted) image as Numpy array ``image``, compute
a complete segmentation of it into text lines as a label array.

If ``fullpage`` is false, then avoid single-line horizontal splits.



Labels will be projected ("spread") from the foreground to the
surrounding background within ``spread_dist`` distance (or half
the estimated scale).



Return a tuple of:
Numpy array of the textline background labels
(not the foreground or the masked image;
foreground may remain unlabelled for
separators and other non-text like small
noise, or large drop-capitals / images),
Numpy array of horizontal foreground lines mask,
Numpy array of vertical foreground lines mask,
Numpy array of large/non-text foreground component mask,
Numpy array of vertical background separators mask,
the estimated scale .
Find text line segmentation within a region or page . Given a binarized ( and inverted ) image as Numpy array ` ` image ` ` compute a complete segmentation of it into text lines as a label array . If ` ` fullpage ` ` is false then avoid single - line horizontal splits . Labels will be projected ( " spread " ) from the foreground to the surrounding background within ` ` spread_dist ` ` distance ( or half the estimated scale ) . Return a tuple of : Numpy array of the textline background labels ( not the foreground or the masked image ; foreground may remain unlabelled for separators and other non - text like small noise or large drop - capitals / images ) Numpy array of horizontal foreground lines mask Numpy array of vertical foreground lines mask Numpy array of large / non - text foreground component mask Numpy array of vertical background separators mask the estimated scale .
def compute_segmentation(binary,
                         zoom=1.0,
                         fullpage=False,
                         seps=None,
                         maxcolseps=2,
                         maxseps=0,
                         maximages=0,
                         csminheight=4,
                         hlminwidth=10,
                         spread_dist=None,
                         rl=False,
                         bt=False):
    DSAVE("input_binary",binary)
    LOG.debug('estimating glyph scale')
    scale = psegutils.estimate_scale(binary, zoom)
    LOG.debug('height: %d, zoom: %.2f, scale: %d', binary.shape[0], zoom, scale)
    if fullpage:
        LOG.debug('computing images')
        images = compute_images(binary, scale, maximages=maximages)
        LOG.debug('computing horizontal/vertical line separators')
        hlines = compute_hlines(binary, scale, hlminwidth=hlminwidth, images=images)
        vlines = compute_separators_morph(binary, scale, csminheight=csminheight, maxseps=maxseps, images=images)
        binary = np.minimum(binary,1-hlines)
        binary = np.minimum(binary,1-vlines)
        binary = np.minimum(binary,1-images)
        if seps is not None:
            binary = (1-seps) * binary
    else:
        hlines = np.zeros_like(binary, np.bool)
        vlines = np.zeros_like(binary, np.bool)
        images = np.zeros_like(binary, np.bool)
    LOG.debug('computing gradient map')
    bottom, top, boxmap = compute_gradmaps(binary, scale,
                                           usegauss=False,
                                           fullpage=fullpage)
    if fullpage:
        LOG.debug('finding whitespace column separators')
        colseps = compute_colseps_conv(binary, scale,
                                       maxcolseps=maxcolseps,
                                       csminheight=csminheight)
        DSAVE("colseps",0.7*colseps+0.3*binary)
        sepmask = np.maximum(hlines, vlines)
        sepmask = np.maximum(sepmask, images)
        sepmask = np.maximum(sepmask, colseps)
        if seps is not None:
            sepmask = np.maximum(sepmask, seps)
        sepmask = morph.r_closing(sepmask, (scale, scale))
        DSAVE("sepmask",0.7*sepmask+0.3*binary)
    else:
        colseps = np.zeros(binary.shape, np.uint8)
        sepmask = np.zeros(binary.shape, np.uint8)
    LOG.debug('computing line seeds')
    seeds = compute_line_seeds(binary, bottom, top, sepmask, scale)
    if fullpage:
        invalid = np.setdiff1d(seeds.flatten(), (seeds*binary*boxmap).flatten())
        relabel = np.arange(np.max(seeds)+1)
        relabel[invalid] = 0
        seeds = relabel[seeds]
        DSAVE("lineseeds_filtered", [seeds,binary])
    else:
        seeds = hmerge_line_seeds(binary, seeds, scale)
    LOG.debug('spreading seed labels')
    llabels = morph.propagate_labels_majority(binary, seeds)
    llabels2 = morph.propagate_labels(binary, seeds, conflict=0)
    conflicts = llabels > llabels2
    llabels = np.where(conflicts, seeds, llabels)
    seplabel = np.max(seeds)+1
    llabels[sepmask>0] = seplabel
    spread = morph.spread_labels(llabels, maxdist=spread_dist or scale/2)
    DSAVE('lineseeds_spread', [spread,binary])
    llabels2 = morph.propagate_labels_majority(binary, spread)
    llabels = np.where(seeds, seeds, llabels2)
    llabels[sepmask>0] = seplabel
    llabels = morph.spread_labels(llabels, maxdist=spread_dist or scale/2)
    llabels[llabels==seplabel] = 0
    DSAVE('llabels', [llabels,binary])
    LOG.debug('sorting labels by reading order')
    llabels = morph.reading_order(llabels,rl,bt)[llabels]
    DSAVE('llabels_ordered', llabels)
    return llabels, hlines, vlines, images, colseps, scale
def compute_segmentation ( binary , zoom = 1.0 , fullpage = False , seps = None , maxcolseps = 2 , maxseps = 0 , maximages = 0 , csminheight = 4 , hlminwidth = 10 , spread_dist = None , rl = False , bt = False ) : DSAVE ( "input_binary" , binary ) LOG . debug ( 'estimating glyph scale' ) scale = psegutils . estimate_scale ( binary , zoom ) LOG . debug ( 'height: %d, zoom: %.2f, scale: %d' , binary . shape [ 0 ] , zoom , scale ) if fullpage : LOG . debug ( 'computing images' ) images = compute_images ( binary , scale , maximages = maximages ) LOG . debug ( 'computing horizontal/vertical line separators' ) hlines = compute_hlines ( binary , scale , hlminwidth = hlminwidth , images = images ) vlines = compute_separators_morph ( binary , scale , csminheight = csminheight , maxseps = maxseps , images = images ) binary = np . minimum ( binary , 1 - hlines ) binary = np . minimum ( binary , 1 - vlines ) binary = np . minimum ( binary , 1 - images ) if seps is not None : binary = ( 1 - seps ) * binary else : hlines = np . zeros_like ( binary , np . bool ) vlines = np . zeros_like ( binary , np . bool ) images = np . zeros_like ( binary , np . bool ) LOG . debug ( 'computing gradient map' ) bottom , top , boxmap = compute_gradmaps ( binary , scale , usegauss = False , fullpage = fullpage ) if fullpage : LOG . debug ( 'finding whitespace column separators' ) colseps = compute_colseps_conv ( binary , scale , maxcolseps = maxcolseps , csminheight = csminheight ) DSAVE ( "colseps" , 0.7 * colseps + 0.3 * binary ) sepmask = np . maximum ( hlines , vlines ) sepmask = np . maximum ( sepmask , images ) sepmask = np . maximum ( sepmask , colseps ) if seps is not None : sepmask = np . maximum ( sepmask , seps ) sepmask = morph . r_closing ( sepmask , ( scale , scale ) ) DSAVE ( "sepmask" , 0.7 * sepmask + 0.3 * binary ) else : colseps = np . zeros ( binary . shape , np . uint8 ) sepmask = np . zeros ( binary . shape , np . uint8 ) LOG . debug ( 'computing line seeds' ) seeds = compute_line_seeds ( binary , bottom , top , sepmask , scale ) if fullpage : invalid = np . setdiff1d ( seeds . flatten ( ) , ( seeds * binary * boxmap ) . flatten ( ) ) relabel = np . arange ( np . max ( seeds ) + 1 ) relabel [ invalid ] = 0 seeds = relabel [ seeds ] DSAVE ( "lineseeds_filtered" , [ seeds , binary ] ) else : seeds = hmerge_line_seeds ( binary , seeds , scale ) LOG . debug ( 'spreading seed labels' ) llabels = morph . propagate_labels_majority ( binary , seeds ) llabels2 = morph . propagate_labels ( binary , seeds , conflict = 0 ) conflicts = llabels > llabels2 llabels = np . where ( conflicts , seeds , llabels ) seplabel = np . max ( seeds ) + 1 llabels [ sepmask > 0 ] = seplabel spread = morph . spread_labels ( llabels , maxdist = spread_dist or scale / 2 ) DSAVE ( 'lineseeds_spread' , [ spread , binary ] ) llabels2 = morph . propagate_labels_majority ( binary , spread ) llabels = np . where ( seeds , seeds , llabels2 ) llabels [ sepmask > 0 ] = seplabel llabels = morph . spread_labels ( llabels , maxdist = spread_dist or scale / 2 ) llabels [ llabels == seplabel ] = 0 DSAVE ( 'llabels' , [ llabels , binary ] ) LOG . debug ( 'sorting labels by reading order' ) llabels = morph . reading_order ( llabels , rl , bt ) [ llabels ] DSAVE ( 'llabels_ordered' , llabels ) return llabels , hlines , vlines , images , colseps , scale
Find text line segmentation within a region or page.
Find text line segmentation within a region or page .
"""Find text line segmentation within a region or page.

    Given a binarized (and inverted) image as Numpy array ``image``, compute
    a complete segmentation of it into text lines as a label array.

    If ``fullpage`` is false, then avoid single-line horizontal splits.

    If ``fullpage`` is true, then
    - allow all horizontal splits, and search
    - for up to ``maxcolseps`` multi-line vertical whitespaces
      (as column separators, counted piece-wise) of at least
      ``csminheight`` multiples of ``scale``,
    - for up to ``maxseps`` vertical black lines
      (as column separators, counted piece-wise) of at least
      ``csminheight`` multiples of ``scale``, and
    - for any number of horizontal lines of at least
      ``hlminwidth`` multiples of ``scale``,
    - for anything in ``seps`` if given,
    then suppress these separator components and return them separately.
    
    Labels will be projected ("spread") from the foreground to the
    surrounding background within ``spread_dist`` distance (or half
    the estimated scale).
    
    Respect the given reading order:
    - ``rl``, whether to sort text lines in reverse order (from right to left),
    - ``bt``, whether to sort text lines in reverse order (from bottom to top).

    Return a tuple of:
    - Numpy array of the textline background labels
      (not the foreground or the masked image;
       foreground may remain unlabelled for
       separators and other non-text like small
       noise, or large drop-capitals / images),
    - Numpy array of horizontal foreground lines mask,
    - Numpy array of vertical foreground lines mask,
    - Numpy array of large/non-text foreground component mask,
    - Numpy array of vertical background separators mask,
    - the estimated scale (i.e. median sqrt bbox area of glyph components).
    """ # TODO generalize to multi-scale (with `scale` as group array instead of float) # suppress separators/images for line estimation # get a larger (closed) mask of all separators # (both bg boundary and fg line seps, detected # and passed in) to separate line/column labels # filter labels that have only noise fg (e.g. from split) # spread labels from seeds to bg, but watch fg, # voting for majority on bg conflicts, # but splitting on seed conflicts # (protect sepmask as a temporary label) #segmentation = llabels*binary #return segmentation
binary
zoom
fullpage
seps
maxcolseps
maxseps
maximages
csminheight
hlminwidth
spread_dist
rl
bt