From ce97af5a7971136a17d1972a920139ee50520993 Mon Sep 17 00:00:00 2001 From: "James R. Barlow" Date: Fri, 17 Jan 2020 03:10:27 -0800 Subject: [PATCH] Add OCR quality measurement API --- src/ocrmypdf/__init__.py | 1 + src/ocrmypdf/_sync.py | 1 + src/ocrmypdf/api.py | 3 +- src/ocrmypdf/quality.py | 60 ++++++++++++++++++++++++++++++++++++++++ tests/test_quality.py | 35 +++++++++++++++++++++++ 5 files changed, 98 insertions(+), 2 deletions(-) create mode 100644 src/ocrmypdf/quality.py create mode 100644 tests/test_quality.py diff --git a/src/ocrmypdf/__init__.py b/src/ocrmypdf/__init__.py index 8d779a95..2f76bf4f 100644 --- a/src/ocrmypdf/__init__.py +++ b/src/ocrmypdf/__init__.py @@ -23,6 +23,7 @@ from .exceptions import ( DpiError, EncryptedPdfError, ExitCode, + ExitCodeException, InputFileError, MissingDependencyError, OutputFileAccessError, diff --git a/src/ocrmypdf/_sync.py b/src/ocrmypdf/_sync.py index 960e3220..493664a0 100644 --- a/src/ocrmypdf/_sync.py +++ b/src/ocrmypdf/_sync.py @@ -381,6 +381,7 @@ def run_pipeline(options, api=False): detailed_page_analysis=options.redo_ocr, progbar=options.progress_bar, ) + context = PDFContext(options, work_folder, origin_pdf, pdfinfo) # Validate options are okay for this pdf diff --git a/src/ocrmypdf/api.py b/src/ocrmypdf/api.py index bad68732..e48320b5 100644 --- a/src/ocrmypdf/api.py +++ b/src/ocrmypdf/api.py @@ -18,11 +18,10 @@ import logging import os import sys -import warnings from contextlib import suppress from enum import IntEnum from pathlib import Path -from typing import Dict, List, Optional +from typing import Dict, List from tqdm import tqdm diff --git a/src/ocrmypdf/quality.py b/src/ocrmypdf/quality.py new file mode 100644 index 00000000..293173bf --- /dev/null +++ b/src/ocrmypdf/quality.py @@ -0,0 +1,60 @@ +# © 2020 James R. Barlow: github.com/jbarlow83 +# +# This file is part of OCRmyPDF. +# +# OCRmyPDF is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# OCRmyPDF is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with OCRmyPDF. If not, see . + +import re +from typing import Iterable + +"""Utilities to measure OCR quality""" + + +class OcrQualityDictionary: + """Manages a dictionary for simple OCR quality checks.""" + + def __init__(self, *, wordlist: Iterable[str] = []): + """Construct a dictionary from a list of words. + + Words for which capitalization is important should be capitalized in the + dictionary. Words that contain spaces or other punctuation will never match. + """ + self.dictionary = set() + self.dictionary.update(w for w in wordlist) + + def measure_words_matched(self, ocr_text: str) -> float: + """Check how many unique words in the OCR text match a dictionary. + + Words with mixed capitalized are only considered a match if the test word + matches that capitalization. + + Returns: + number of words that match / number + """ + text = re.sub(r"[0-9_]+", ' ', ocr_text) + text = re.sub(r'\W+', ' ', text) + text_words_list = re.split(r'\s+', text) + text_words = {w for w in text_words_list if len(w) >= 3} + + matches = 0 + for w in text_words: + if w in self.dictionary or ( + w != w.lower() and w.lower() in self.dictionary + ): + matches += 1 + if matches > 0: + hit_ratio = matches / len(text_words) + else: + hit_ratio = 0.0 + return hit_ratio diff --git a/tests/test_quality.py b/tests/test_quality.py new file mode 100644 index 00000000..99f15653 --- /dev/null +++ b/tests/test_quality.py @@ -0,0 +1,35 @@ +# © 2020 James R. Barlow: github.com/jbarlow83 +# +# This file is part of OCRmyPDF. +# +# OCRmyPDF is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# OCRmyPDF is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with OCRmyPDF. If not, see . + +import pytest + +import ocrmypdf.quality as qual + + +def test_quality_measurement(): + oqd = qual.OcrQualityDictionary( + wordlist=["words", "words", "quick", "brown", "fox", "dog", "lazy"] + ) + assert len(oqd.dictionary) == 6 # 6 unique + + assert ( + oqd.measure_words_matched("The quick brown fox jumps quickly over the lazy dog") + == 0.5 + ) + assert oqd.measure_words_matched("12345 10% _f 7fox -brown | words") == 1.0 + + assert oqd.measure_words_matched("quick quick quick") == 1.0