• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

dynobo / normcap / 18953976799

30 Oct 2025 08:21PM UTC coverage: 80.119% (-1.4%) from 81.509%
18953976799

Pull #798

github

web-flow
Merge a4477b844 into 50be529a6
Pull Request #798: Refactoring

858 of 1175 new or added lines in 51 files covered. (73.02%)

23 existing lines in 5 files now uncovered.

2974 of 3712 relevant lines covered (80.12%)

4.57 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

78.95
/normcap/detection/ocr/recognize.py
1
"""Detect OCR tool & language and perform OCR on selected part of image."""
2

3
import logging
6✔
4
import sys
6✔
5
import tempfile
6✔
6
import time
6✔
7
from collections.abc import Iterable
6✔
8
from os import PathLike
6✔
9
from pathlib import Path
6✔
10

11
from PySide6 import QtGui
6✔
12

13
from normcap.detection.models import DetectionResult, TextDetector, TextType
6✔
14
from normcap.detection.ocr import enhance, tesseract, transformer
6✔
15
from normcap.detection.ocr.models import OEM, PSM, OcrResult, TessArgs
6✔
16

17
logger = logging.getLogger(__name__)
6✔
18

19

20
def _save_image_in_temp_folder(image: QtGui.QImage, postfix: str = "") -> None:
6✔
21
    """For debugging it can be useful to store the cropped image."""
22
    if logger.getEffectiveLevel() != logging.DEBUG:
6✔
23
        return
6✔
24

UNCOV
25
    temp_dir = Path(tempfile.gettempdir()) / "normcap"
×
UNCOV
26
    temp_dir.mkdir(exist_ok=True)
×
27

UNCOV
28
    now_str = time.strftime("%Y-%m-%d_%H-%M-%S", time.gmtime())
×
UNCOV
29
    file_name = f"{now_str}{postfix}.png"
×
30

UNCOV
31
    logger.debug("Save debug image as %s", temp_dir / file_name)
×
UNCOV
32
    image.save(str(temp_dir / file_name))
×
33

34

35
def get_text_from_image(
6✔
36
    languages: str | Iterable[str],
37
    image: QtGui.QImage,
38
    tesseract_bin_path: PathLike,
39
    tessdata_path: PathLike | str | None = None,
40
    parse: bool = True,
41
    resize_factor: float | None = None,
42
    padding_size: int | None = None,
43
) -> list[DetectionResult]:
44
    """Apply OCR on selected image section."""
45
    image = enhance.preprocess(image, resize_factor=resize_factor, padding=padding_size)
6✔
46
    _save_image_in_temp_folder(image, postfix="_enhanced")
6✔
47

48
    # TODO: Improve handling of tesseract_cmd and tessdata_path
49
    if sys.platform == "win32" and tessdata_path:
6✔
50
        tessdata_path = tesseract.get_short_path(str(tessdata_path))
×
51

52
    tess_args = TessArgs(
6✔
53
        tessdata_path=tessdata_path,
54
        lang=languages if isinstance(languages, str) else "+".join(languages),
55
        oem=OEM.DEFAULT,
56
        psm=PSM.AUTO,
57
    )
58
    logger.debug(
6✔
59
        "Run Tesseract on image of size %s with args:\n%s",
60
        (image.width(), image.height()),
61
        tess_args,
62
    )
63
    ocr_result_data = tesseract.perform_ocr(
6✔
64
        tesseract_bin_path=tesseract_bin_path, image=image, args=tess_args.as_list()
65
    )
66
    result = OcrResult(tess_args=tess_args, words=ocr_result_data, image=image)
6✔
67
    logger.debug("OCR detections:\n%s", ",\n".join(str(w) for w in result.words))
6✔
68

69
    if not parse:
6✔
NEW
70
        return [
×
71
            DetectionResult(
72
                text=result.text,
73
                text_type=TextType.SINGLE_LINE,
74
                detector=TextDetector.OCR_RAW,
75
            )
76
        ]
77

78
    result = transformer.apply(result)
6✔
79
    logger.debug("Parsed text:\n%s", result.parsed)
6✔
80
    text_type = (
6✔
81
        TextType[result.best_scored_transformer.value]
82
        if result.best_scored_transformer
83
        else TextType.SINGLE_LINE
84
    )
85

86
    detections = [
6✔
87
        DetectionResult(
88
            text=s,
89
            text_type=text_type,
90
            detector=TextDetector.OCR_PARSED,
91
        )
92
        for s in result.parsed
93
    ]
94
    return detections
6✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc