ocrd_cis/ocropy/common.py
def estimate_skew_angle(image, angles, min_factor=1.5):
    """Score deskewing angles for image by projection profiling.
    
    Given grayscale-normalized and inverted (fg=1.0) array ``image``,
    and array ``angles`` of angles (degrees counter-cw),
    rotate image by each candidate angle and calculate a score for it
    in the following way.
    
    Aggregate average luminance along pixel lines (horizontally),
    then determine their variance across pixel lines (vertically).
    (The variance will be maximal when largely-black lines within
     textlines and mostly-white lines between textlines are clearly
     separated, i.e. orthogonal.)
    
    Return the angle which achieves a maximum score, if its score
    is larger than the average scores by ``min_factor``, or zero
    otherwise.
    """
    # TODO: make zoomable, i.e. interpolate down to max 300 DPI to be faster
    # TODO: sweep through angles very coarse, then hill climbing for precision
    # TODO: try with shear (i.e. simply numpy shift) instead of true rotation
    # TODO: use square of difference instead of variance as projection score
    #       (more reliable with background noise or multi-column)
    # TODO: offer flip (90°) test (comparing length-normalized projection profiles)
    # TODO: offer mirror (180°, or + vs - 90°) test based on ascender/descender signal
    #       (Latin scripts have more ascenders e.g. bhdkltſ than descenders e.g. qgpyj)
    estimates = np.zeros_like(angles)
    varmax = 0
    for i, a in enumerate(angles):
        #rotated = interpolation.rotate(image, a, order=0, mode='constant')
        # *much* faster (by an order of magnitude):
        rotated = np.array(Image.fromarray(image).rotate(
            # (BILINEAR would be equivalent to above, but only 4x as fast)
            a, expand=True, resample=Image.NEAREST))
        v = np.mean(rotated, axis=1)
        v = np.var(v)
        estimates[i] = v
        if v > varmax:
            varmax = v
    # only return the angle of the largest entropy
    # if it is considerably larger than the average
    varavg = np.mean(estimates)
    LOG.debug('estimate skew angle: min(angle)=%.1f° max(angle)=%.1f° max(var)=%.2g avg(var)=%.2g',
              np.min(angles), np.max(angles), varmax, varavg)
    if varmax and varmax / varavg > min_factor:
        return angles[np.argmax(estimates)]
    else:
        return 0
Score deskewing angles for image by projection profiling.
    
    Given grayscale-normalized and inverted (fg=1.0) array ``image``,
    and array ``angles`` of angles (degrees counter-cw),
    rotate image by each candidate angle and calculate a score for it
    in the following way.
    
    Aggregate average luminance along pixel lines (horizontally),
    then determine their variance across pixel lines (vertically).
    (The variance will be maximal when largely-black lines within
     textlines and mostly-white lines between textlines are clearly
     separated, i.e. orthogonal.)
    
    Return the angle which achieves a maximum score, if its score
    is larger than the average scores by ``min_factor``, or zero
    otherwise.
    
Score deskewing angles for image by projection profiling.

Aggregate average luminance along pixel lines (horizontally),
then determine their variance across pixel lines (vertically).
(The variance will be maximal when largely-black lines within
textlines and mostly-white lines between textlines are clearly
separated, i.e. orthogonal.)

Return the angle which achieves a maximum score, if its score
is larger than the average scores by ``min_factor``, or zero
otherwise.
Score deskewing angles for image by projection profiling . Aggregate average luminance along pixel lines ( horizontally ) then determine their variance across pixel lines ( vertically ) . ( The variance will be maximal when largely - black lines within textlines and mostly - white lines between textlines are clearly separated i . e . orthogonal . ) Return the angle which achieves a maximum score if its score is larger than the average scores by ` ` min_factor ` ` or zero otherwise .
def estimate_skew_angle(image, angles, min_factor=1.5):
    estimates = np.zeros_like(angles)
    varmax = 0
    for i, a in enumerate(angles):
        rotated = np.array(Image.fromarray(image).rotate(
            a, expand=True, resample=Image.NEAREST))
        v = np.mean(rotated, axis=1)
        v = np.var(v)
        estimates[i] = v
        if v > varmax:
            varmax = v
    varavg = np.mean(estimates)
    LOG.debug('estimate skew angle: min(angle)=%.1f° max(angle)=%.1f° max(var)=%.2g avg(var)=%.2g',
              np.min(angles), np.max(angles), varmax, varavg)
    if varmax and varmax / varavg > min_factor:
        return angles[np.argmax(estimates)]
    else:
        return 0
def estimate_skew_angle ( image , angles , min_factor = 1.5 ) : estimates = np . zeros_like ( angles ) varmax = 0 for i , a in enumerate ( angles ) : rotated = np . array ( Image . fromarray ( image ) . rotate ( a , expand = True , resample = Image . NEAREST ) ) v = np . mean ( rotated , axis = 1 ) v = np . var ( v ) estimates [ i ] = v if v > varmax : varmax = v varavg = np . mean ( estimates ) LOG . debug ( 'estimate skew angle: min(angle)=%.1f° max(angle)=%.1f° max(var)=%.2g avg(var)=%.2g', np . min ( angles ) , np . max ( angles ) , varmax , varavg ) if varmax and varmax / varavg > min_factor : return angles [ np . argmax ( estimates ) ] else : return 0
Score deskewing angles for image by projection profiling.
Score deskewing angles for image by projection profiling .
"""Score deskewing angles for image by projection profiling.
    
    Given grayscale-normalized and inverted (fg=1.0) array ``image``,
    and array ``angles`` of angles (degrees counter-cw),
    rotate image by each candidate angle and calculate a score for it
    in the following way.
    
    Aggregate average luminance along pixel lines (horizontally),
    then determine their variance across pixel lines (vertically).
    (The variance will be maximal when largely-black lines within
     textlines and mostly-white lines between textlines are clearly
     separated, i.e. orthogonal.)
    
    Return the angle which achieves a maximum score, if its score
    is larger than the average scores by ``min_factor``, or zero
    otherwise.
    """ # TODO: make zoomable, i.e. interpolate down to max 300 DPI to be faster # TODO: sweep through angles very coarse, then hill climbing for precision # TODO: try with shear (i.e. simply numpy shift) instead of true rotation # TODO: use square of difference instead of variance as projection score #       (more reliable with background noise or multi-column) # TODO: offer flip (90°) test (comparing length-normalized projection profiles) # TODO: offer mirror (180°, or + vs - 90°) test based on ascender/descender signal #       (Latin scripts have more ascenders e.g. bhdkltſ than descenders e.g. qgpyj) #rotated = interpolation.rotate(image, a, order=0, mode='constant') # *much* faster (by an order of magnitude): # (BILINEAR would be equivalent to above, but only 4x as fast) # only return the angle of the largest entropy # if it is considerably larger than the average
image
angles
min_factor