Replace leptonica deskew with tesseract find skew and pillow rotate

Also rebuild the cache.
This commit is contained in:
James R. Barlow
2021-11-12 16:35:08 -08:00
parent 25d046ae95
commit a55ab05d16
135 changed files with 4253 additions and 4016 deletions
+46 -14
View File
@@ -13,7 +13,7 @@ from distutils.version import StrictVersion
from os import fspath
from pathlib import Path
from subprocess import PIPE, STDOUT, CalledProcessError, TimeoutExpired
from typing import List, Optional
from typing import Dict, Iterator, List, Optional
from PIL import Image
@@ -126,6 +126,17 @@ def tess_base_args(langs: List[str], engine_mode: Optional[int]) -> List[str]:
return args
def _parse_tesseract_output(binary_output: bytes) -> Dict[str, str]:
def g():
for line in binary_output.decode().splitlines():
line = line.strip()
parts = line.split(':', maxsplit=2)
if len(parts) == 2:
yield parts[0].strip(), parts[1].strip()
return {k: v for k, v in g()}
def get_orientation(
input_file: Path, engine_mode: Optional[int], timeout: float
) -> OrientationConfidence:
@@ -138,7 +149,6 @@ def get_orientation(
try:
p = run(args_tesseract, stdout=PIPE, stderr=STDOUT, timeout=timeout, check=True)
stdout = p.stdout
except TimeoutExpired:
return OrientationConfidence(angle=0, confidence=0.0)
except CalledProcessError as e:
@@ -150,19 +160,41 @@ def get_orientation(
):
return OrientationConfidence(0, 0)
raise SubprocessOutputError() from e
else:
osd = {}
for line in stdout.decode().splitlines():
line = line.strip()
parts = line.split(':', maxsplit=2)
if len(parts) == 2:
osd[parts[0].strip()] = parts[1].strip()
angle = int(osd.get('Orientation in degrees', 0))
oc = OrientationConfidence(
angle=angle, confidence=float(osd.get('Orientation confidence', 0))
)
return oc
osd = _parse_tesseract_output(p.stdout)
angle = int(osd.get('Orientation in degrees', 0))
oc = OrientationConfidence(
angle=angle, confidence=float(osd.get('Orientation confidence', 0))
)
return oc
def get_deskew(
input_file: Path, languages: List[str], engine_mode: Optional[int], timeout: float
) -> float:
"""Gets angle to deskew this page, in radians."""
args_tesseract = tess_base_args(languages, engine_mode) + [
'--psm',
'2',
fspath(input_file),
'stdout',
]
try:
p = run(args_tesseract, stdout=PIPE, stderr=STDOUT, timeout=timeout, check=True)
except TimeoutExpired:
return 0.0
except CalledProcessError as e:
tesseract_log_output(e.stdout)
tesseract_log_output(e.stderr)
if b'Empty page!!' in e.output: # Not enough info for a skew angle
return 0.0
raise SubprocessOutputError() from e
parsed = _parse_tesseract_output(p.stdout)
deskew = float(parsed.get('Deskew angle', 0))
return deskew
def tesseract_log_output(stream):
+15 -1
View File
@@ -6,6 +6,7 @@
import logging
import math
import os
import re
import sys
@@ -476,7 +477,20 @@ def preprocess_remove_background(input_file: Path, page_context: PageContext):
def preprocess_deskew(input_file: Path, page_context: PageContext):
output_file = page_context.get_path('pp_deskew.png')
dpi = get_page_square_dpi(page_context.pageinfo, page_context.options)
leptonica.deskew(input_file, output_file, dpi.x)
ocr_engine = page_context.plugin_manager.hook.get_ocr_engine()
deskew_angle = ocr_engine.get_deskew(input_file, page_context.options)
deskew_angle_degrees = deskew_angle * 180.0 / math.pi
with Image.open(input_file) as im:
# According to Pillow docs, .rotate() will automatically use Image.NEAREST
# resampling if image is mode '1' or 'P'
deskewed = im.rotate(
deskew_angle_degrees, resample=Image.BICUBIC, fillcolor='white'
)
deskewed.save(output_file, dpi=dpi)
return output_file
@@ -142,6 +142,15 @@ class TesseractOcrEngine(OcrEngine):
timeout=options.tesseract_timeout,
)
@staticmethod
def get_deskew(input_file, options) -> float:
return tesseract.get_deskew(
input_file,
languages=options.languages,
engine_mode=options.tesseract_oem,
timeout=options.tesseract_timeout,
)
@staticmethod
def generate_hocr(input_file, output_hocr, output_text, options):
tesseract.generate_hocr(
+5
View File
@@ -366,6 +366,11 @@ class OcrEngine(ABC):
def get_orientation(input_file: Path, options: Namespace) -> OrientationConfidence:
"""Returns the orientation of the image."""
@staticmethod
def get_deskew(input_file: Path, options: Namespace) -> float:
"""Returns the deskew angle of the image, in radians."""
return 0.0
@staticmethod
@abstractmethod
def generate_hocr(