Replace leptonica deskew with tesseract find skew and pillow rotate
Also rebuild the cache.
This commit is contained in:
@@ -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):
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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(
|
||||
|
||||
Reference in New Issue
Block a user