Files
rapidocr/python/tests/test_infer_argmax.py

101 lines
3.0 KiB
Python

# -*- encoding: utf-8 -*-
# Unit tests for the rec-logits reduction introduced in PR #757.
# These tests deliberately avoid downloading models or requiring a GPU.
import sys
from pathlib import Path
import numpy as np
import pytest
root_dir = Path(__file__).resolve().parent.parent
sys.path.append(str(root_dir))
from rapidocr.ch_ppocr_rec.utils import CTCLabelDecode
from rapidocr.inference_engine.base import InferSession
from rapidocr.utils.utils import import_package
class _ArrayReturningSession(InferSession):
"""Minimal InferSession whose ``__call__`` returns a fixed 3D array."""
def __init__(self):
# bypass the abstract base __init__
class _Cfg:
pass
super().__init__(_Cfg())
self._fake = None
def setup(self, arr: np.ndarray) -> None:
self._fake = arr
def __call__(self, input_content):
return self._fake
def have_key(self, key: str = "character") -> bool:
return False
def get_character_list(self, key: str = "character"):
return []
def test_infer_argmax_reduces_over_last_axis():
arr = np.random.RandomState(0).rand(2, 5, 10).astype(np.float32)
sess = _ArrayReturningSession()
sess.setup(arr)
idx, prob = sess.infer_argmax(np.zeros((1, 1), dtype=np.float32))
assert np.array_equal(idx, arr.argmax(axis=-1))
assert np.array_equal(prob, arr.max(axis=-1))
def test_infer_argmax_rejects_under_two_dims():
arr = np.random.RandomState(1).rand(10).astype(np.float32)
sess = _ArrayReturningSession()
sess.setup(arr)
with pytest.raises(ValueError):
sess.infer_argmax(np.zeros((1,), dtype=np.float32))
def test_ctc_label_decode_accepts_tuple_and_array():
character = list("abcdefghij")
decode = CTCLabelDecode(character=character)
logits = np.random.RandomState(2).rand(2, 6, len(character)).astype(np.float32)
out_from_array = decode(logits)
out_from_tuple = decode((logits.argmax(axis=2), logits.max(axis=2)))
assert out_from_array == out_from_tuple
@pytest.mark.skipif(not import_package("torch"), reason="torch is not installed")
def test_torch_infer_argmax_matches_base_reduction():
from rapidocr.inference_engine.pytorch.main import TorchInferSession
character = list("abcdefghij")
class _FakeTorchSession(TorchInferSession):
def __init__(self):
import torch
self.device = torch.device("cpu")
def predictor(x):
# Deterministic given x so __call__ and infer_argmax agree.
return torch.full(
(x.shape[0], 25, len(character)), float(x.mean().item())
)
self.predictor = predictor
sess = _FakeTorchSession()
x = np.random.RandomState(3).rand(2, 3, 32, 100).astype(np.float32)
full = sess(x) # reduce=False -> (B, T, vocab) ndarray
idx, prob = sess.infer_argmax(x) # reduce=True -> (B, T) tuple
assert np.array_equal(idx, full.argmax(axis=-1))
assert np.array_equal(prob, full.max(axis=-1))