Gracefully recover from tesseract's failure to process very large images

And test cases to check this
This commit is contained in:
James R. Barlow
2016-02-20 04:53:02 -08:00
parent ac71c3be63
commit 6f3ac46b1c
5 changed files with 89 additions and 5 deletions
+17 -5
View File
@@ -98,6 +98,8 @@ def get_orientation(input_file, language: list, timeout: float, log):
return OrientationConfidence(angle=0, confidence=0.0)
except CalledProcessError as e:
tesseract_log_output(log, e.output, input_file)
if 'Image too large' in e.output:
return OrientationConfidence(0, 0)
raise e from e
else:
osd = {}
@@ -148,6 +150,13 @@ def page_timedout(log, input_file):
log.warning(prefix + " took too long to OCR - skipping")
def _generate_null_hocr(output_hocr, pageinfo):
with open(output_hocr, 'w', encoding="utf-8") as f:
f.write(HOCR_TEMPLATE.format(
pageinfo['width_pixels'],
pageinfo['height_pixels']))
def generate_hocr(input_file, output_hocr, language: list, tessconfig: list,
timeout: float, pageinfo_getter, pagesegmode: int, log):
@@ -175,13 +184,13 @@ def generate_hocr(input_file, output_hocr, language: list, tessconfig: list,
# Temporary workaround to hocrTransform not being able to function if
# it does not have a valid hOCR file.
page_timedout(log, input_file)
with open(output_hocr, 'w', encoding="utf-8") as f:
pageinfo = pageinfo_getter()
f.write(HOCR_TEMPLATE.format(
pageinfo['width_pixels'],
pageinfo['height_pixels']))
_generate_null_hocr(output_hocr, pageinfo_getter())
except CalledProcessError as e:
tesseract_log_output(log, e.output, input_file)
if 'Image too large' in e.output:
_generate_null_hocr(output_hocr, pageinfo_getter())
return
raise e from e
else:
tesseract_log_output(log, stdout, input_file)
@@ -242,6 +251,9 @@ def generate_pdf(input_image, skip_pdf, output_pdf, language: list,
shutil.copy(skip_pdf, output_pdf)
except CalledProcessError as e:
tesseract_log_output(log, e.output, input_image)
if 'Image too large' in e.output:
shutil.copy(skip_pdf, output_pdf)
return
raise e from e
else:
tesseract_log_output(log, stdout, input_image)
Binary file not shown.
+50
View File
@@ -0,0 +1,50 @@
#!/usr/bin/env python3
import sys
VERSION_STRING = '''tesseract 3.04.00
leptonica-1.72
libjpeg 8d : libpng 1.6.19 : libtiff 4.0.6 : zlib 1.2.5
SPOOFED: return error claiming image too big
'''
"""Simulates a Tesseract crash
It isn't strictly necessary to crash the process and that has unwanted
side effects like triggering core dumps or error reporting, logging and such.
It's enough to dump some text to stderr and return an error code.
Follows the POSIX? convention of returning 128 + signal number.
"""
def main():
if sys.argv[1] == '--version':
print(VERSION_STRING, file=sys.stderr)
sys.exit(0)
elif sys.argv[1] == '--list-langs':
print('List of available languages (1):\neng', file=sys.stderr)
sys.exit(0)
elif sys.argv[-1] == 'hocr':
print("Image too large: (33830, 14959)\n"
"Error during processing.", file=sys.stderr)
sys.exit(1)
elif sys.argv[-1] == 'pdf':
print("Image too large: (33830, 14959)\n"
"Error during processing.", file=sys.stderr)
sys.exit(1)
elif sys.argv[-1] == 'stdout':
print("Image too large: (33830, 14959)\n"
"Error during processing.", file=sys.stderr)
sys.exit(1)
else:
print("Spoof doesn't understand arguments", file=sys.stderr)
print(sys.argv, file=sys.stderr)
sys.exit(1)
sys.exit(0)
if __name__ == '__main__':
main()
+7
View File
@@ -90,6 +90,13 @@ def main():
universal_newlines=True)
stdout, stderr = p.communicate()
if p.returncode != 0:
# Do not cache errors or crashes
print("Tesseract error", file=sys.stderr)
print(stdout, end='')
print(stderr, end='', file=sys.stderr)
return p.returncode
with open(cache_name + '.stdout', 'w') as f:
f.write(stdout)
with open(cache_name + '.stderr', 'w') as f:
+15
View File
@@ -113,6 +113,11 @@ def spoof_tesseract_crash():
return spoof('tesseract', 'tesseract_crash.py')
@pytest.fixture
def spoof_tesseract_big_image_error():
return spoof('tesseract', 'tesseract_big_image_error.py')
def test_quick(spoof_tesseract_noop):
check_ocrmypdf('c02-22.pdf', 'test_quick.pdf', env=spoof_tesseract_noop)
@@ -477,3 +482,13 @@ def test_tesseract_crash_autorotate(spoof_tesseract_crash):
assert sh.returncode == ExitCode.child_process_error
assert not os.path.exists(_outfile('wontwork.pdf'))
assert "ERROR" in err
@pytest.mark.parametrize('renderer', [
'hocr',
'tesseract',
])
def test_tesseract_image_too_big(renderer, spoof_tesseract_big_image_error):
check_ocrmypdf(
'hugemono.pdf', 'hugemono_%s.pdf' % renderer, '-r',
'--pdf-renderer', renderer, env=spoof_tesseract_big_image_error)