mirror of
https://github.com/RapidAI/RapidOCR.git
synced 2026-11-11 05:20:49 +08:00
334 lines
9.5 KiB
Python
334 lines
9.5 KiB
Python
# -*- encoding: utf-8 -*-
|
|
# @Author: SWHL
|
|
# @Contact: liekkaskono@163.com
|
|
import ctypes
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
import cv2
|
|
import numpy as np
|
|
import pytest
|
|
from pytest import mark
|
|
|
|
root_dir = Path(__file__).resolve().parent.parent
|
|
sys.path.append(str(root_dir))
|
|
|
|
from rapidocr import (
|
|
EngineType,
|
|
LangRec,
|
|
LoadImageError,
|
|
ModelType,
|
|
OCRVersion,
|
|
RapidOCR,
|
|
)
|
|
from rapidocr.utils.load_image import LoadImage, MemoryImage, RawMemoryImage
|
|
from rapidocr.utils.parse_parameters import ParseParams
|
|
|
|
test_dir = root_dir / "tests" / "test_files"
|
|
img_path = test_dir / "ch_en_num.jpg"
|
|
config_path = root_dir / "rapidocr" / "config.yaml"
|
|
|
|
|
|
@pytest.fixture()
|
|
def engine():
|
|
engine = RapidOCR(
|
|
params={
|
|
"Det.ocr_version": OCRVersion.PPOCRV4,
|
|
"Det.model_type": ModelType.MOBILE,
|
|
"Det.engine_type": EngineType.ONNXRUNTIME,
|
|
"Cls.ocr_version": OCRVersion.PPOCRV4,
|
|
"Cls.model_type": ModelType.MOBILE,
|
|
"Cls.engine_type": EngineType.ONNXRUNTIME,
|
|
"Rec.ocr_version": OCRVersion.PPOCRV4,
|
|
"Rec.model_type": ModelType.MOBILE,
|
|
"Rec.engine_type": EngineType.ONNXRUNTIME,
|
|
"Rec.lang_type": LangRec.CH,
|
|
}
|
|
)
|
|
return engine
|
|
|
|
|
|
def test_exif_transpose(engine):
|
|
img_path = test_dir / "img_exif_orientation.jpg"
|
|
result = engine(img_path, use_cls=False)
|
|
assert result.txts[0] == "我是中国人"
|
|
|
|
|
|
@mark.parametrize(
|
|
"img_name,gt",
|
|
[
|
|
(
|
|
"black_font_color_transparent.png",
|
|
"我是中国人",
|
|
),
|
|
(
|
|
"white_font_color_transparent.png",
|
|
"我是中国人",
|
|
),
|
|
],
|
|
)
|
|
def test_transparent_img(engine, img_name: str, gt: str):
|
|
img_path = test_dir / img_name
|
|
result = engine(img_path)
|
|
assert result.txts[0] == gt
|
|
|
|
|
|
def test_long_img(engine):
|
|
img_url = "https://www.modelscope.cn/models/RapidAI/RapidOCR/resolve/master/resources/test_files/long.jpeg"
|
|
result = engine(img_url)
|
|
|
|
assert result is not None
|
|
assert len(result.boxes) >= 53
|
|
|
|
|
|
def test_full_black_img(engine):
|
|
img_path = test_dir / "empty_black.jpg"
|
|
result = engine(img_path)
|
|
assert result.img is None
|
|
assert result.boxes is None
|
|
|
|
|
|
def test_img_url_input(engine):
|
|
img_url = "https://www.modelscope.cn/models/RapidAI/RapidOCR/resolve/master/resources/test_files/ch_en_num.jpg"
|
|
result = engine(img_url)
|
|
assert result.txts is not None
|
|
assert result.txts[0] == "正品促销"
|
|
|
|
|
|
def test_empty(engine):
|
|
img = None
|
|
with pytest.raises(LoadImageError) as exc_info:
|
|
engine(img)
|
|
raise LoadImageError
|
|
assert exc_info.type is LoadImageError
|
|
|
|
|
|
def test_zeros(engine):
|
|
img = np.zeros([640, 640, 3], np.uint8)
|
|
result = engine(img)
|
|
assert result.boxes is None
|
|
|
|
|
|
def test_input_str(engine):
|
|
result = engine(str(img_path))
|
|
assert len(result) == 18
|
|
assert result.txts[0] == "正品促销"
|
|
|
|
|
|
def test_input_bytes(engine):
|
|
with open(img_path, "rb") as f:
|
|
result = engine(f.read())
|
|
assert len(result) == 18
|
|
assert result.txts[0] == "正品促销"
|
|
|
|
|
|
def test_input_memory_image(engine):
|
|
"""An encoded image can be passed by address and byte length."""
|
|
encoded = img_path.read_bytes()
|
|
buffer = ctypes.create_string_buffer(encoded)
|
|
memory_img = MemoryImage(ctypes.addressof(buffer), len(encoded))
|
|
|
|
result = engine(memory_img)
|
|
|
|
assert len(result) == 18
|
|
assert result.txts[0] == "正品促销"
|
|
|
|
|
|
def test_input_memory_image_matches_bytes(engine):
|
|
encoded = img_path.read_bytes()
|
|
buffer = ctypes.create_string_buffer(encoded)
|
|
memory_img = MemoryImage(ctypes.addressof(buffer), len(encoded))
|
|
|
|
expected = engine(encoded)
|
|
actual = engine(memory_img)
|
|
|
|
assert actual.txts == expected.txts
|
|
assert actual.scores == expected.scores
|
|
np.testing.assert_allclose(actual.boxes, expected.boxes)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"address,length",
|
|
[(0, 1), (-1, 1), (1, 0), (1, -1)],
|
|
)
|
|
def test_input_memory_image_invalid_address_or_length(address, length):
|
|
loader = LoadImage()
|
|
|
|
with pytest.raises(LoadImageError, match="MemoryImage loading failed"):
|
|
loader(MemoryImage(address, length))
|
|
|
|
|
|
def test_input_memory_image_invalid_encoded_data():
|
|
encoded = b"not an image"
|
|
buffer = ctypes.create_string_buffer(encoded)
|
|
loader = LoadImage()
|
|
|
|
with pytest.raises(LoadImageError, match="MemoryImage loading failed"):
|
|
loader(MemoryImage(ctypes.addressof(buffer), len(encoded)))
|
|
|
|
|
|
def make_raw_memory_image(img, pixel_format, stride=None, bottom_up=False):
|
|
height, width = img.shape[:2]
|
|
row_bytes = img.shape[1] * (1 if img.ndim == 2 else img.shape[2])
|
|
stride = row_bytes if stride is None else stride
|
|
rows = img[::-1] if bottom_up else img
|
|
raw = bytearray(stride * height)
|
|
for row_index, row in enumerate(rows):
|
|
row_data = row.tobytes()
|
|
raw[row_index * stride : row_index * stride + row_bytes] = row_data
|
|
buffer = ctypes.create_string_buffer(bytes(raw))
|
|
memory_img = RawMemoryImage(
|
|
ctypes.addressof(buffer),
|
|
len(raw),
|
|
width,
|
|
height,
|
|
pixel_format=pixel_format,
|
|
stride=stride,
|
|
bottom_up=bottom_up,
|
|
)
|
|
return memory_img, buffer
|
|
|
|
|
|
def test_input_raw_memory_image(engine):
|
|
img = cv2.imread(str(img_path))
|
|
memory_img, buffer = make_raw_memory_image(img, "BGR")
|
|
|
|
result = engine(memory_img)
|
|
|
|
assert buffer is not None # Keep the backing memory alive through inference.
|
|
assert len(result) == 18
|
|
assert result.txts[0] == "正品促销"
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"pixel_format,source",
|
|
[
|
|
("BGR", np.array([[[1, 2, 3], [4, 5, 6]]], dtype=np.uint8)),
|
|
("RGB", np.array([[[3, 2, 1], [6, 5, 4]]], dtype=np.uint8)),
|
|
("BGRA", np.array([[[1, 2, 3, 0], [4, 5, 6, 255]]], dtype=np.uint8)),
|
|
("BGRX", np.array([[[1, 2, 3, 8], [4, 5, 6, 9]]], dtype=np.uint8)),
|
|
("RGBA", np.array([[[3, 2, 1, 0], [6, 5, 4, 255]]], dtype=np.uint8)),
|
|
("RGBX", np.array([[[3, 2, 1, 8], [6, 5, 4, 9]]], dtype=np.uint8)),
|
|
],
|
|
)
|
|
def test_raw_memory_image_converts_to_bgr(pixel_format, source):
|
|
memory_img, buffer = make_raw_memory_image(source, pixel_format)
|
|
|
|
result = LoadImage()(memory_img)
|
|
|
|
assert buffer is not None
|
|
np.testing.assert_array_equal(
|
|
result, np.array([[[1, 2, 3], [4, 5, 6]]], dtype=np.uint8)
|
|
)
|
|
|
|
|
|
def test_raw_memory_image_supports_gray_stride_and_bottom_up():
|
|
gray = np.array([[10, 20], [30, 40]], dtype=np.uint8)
|
|
memory_img, buffer = make_raw_memory_image(
|
|
gray, "GRAY8", stride=4, bottom_up=True
|
|
)
|
|
|
|
result = LoadImage()(memory_img)
|
|
|
|
assert buffer is not None
|
|
expected = cv2.cvtColor(gray, cv2.COLOR_GRAY2BGR)
|
|
np.testing.assert_array_equal(result, expected)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"kwargs,error",
|
|
[
|
|
({"address": 0}, "invalid memory address"),
|
|
({"width": 0}, "width and height must be positive"),
|
|
({"pixel_format": "YUV"}, "unsupported pixel format"),
|
|
({"stride": 5}, "stride .* is smaller than row size"),
|
|
({"length": 8}, "buffer is too small"),
|
|
],
|
|
)
|
|
def test_raw_memory_image_rejects_invalid_metadata(kwargs, error):
|
|
raw = bytes(12)
|
|
buffer = ctypes.create_string_buffer(raw)
|
|
params = {
|
|
"address": ctypes.addressof(buffer),
|
|
"length": len(raw),
|
|
"width": 2,
|
|
"height": 2,
|
|
"pixel_format": "BGR",
|
|
"stride": 6,
|
|
}
|
|
params.update(kwargs)
|
|
|
|
with pytest.raises(LoadImageError, match=error):
|
|
LoadImage()(RawMemoryImage(**params))
|
|
|
|
|
|
def test_input_path(engine):
|
|
result = engine(img_path)
|
|
assert len(result) == 18
|
|
assert result.txts[0] == "正品促销"
|
|
|
|
|
|
def test_input_parameters(engine):
|
|
result = engine(img_path, text_score=1.0)
|
|
assert result.boxes is None
|
|
|
|
|
|
@mark.parametrize("params", [{"text_score": 0.5}, {"Global": 0.5}, {"Unknown.key": 1}])
|
|
def test_invalid_init_parameters(params):
|
|
cfg = ParseParams.load(config_path)
|
|
|
|
with pytest.raises(ValueError):
|
|
ParseParams.update_batch(cfg, params)
|
|
|
|
|
|
def test_nested_init_parameters():
|
|
cfg = ParseParams.load(config_path)
|
|
ParseParams.update_batch(cfg, {"EngineConfig.onnxruntime.use_cuda": True})
|
|
|
|
assert cfg.EngineConfig.onnxruntime.use_cuda is True
|
|
|
|
|
|
def test_input_three_ndim_two_channel(engine):
|
|
img_npy = test_dir / "two_dim_image.npy"
|
|
image_array = np.load(str(img_npy))
|
|
result = engine(image_array)
|
|
|
|
assert result is not None
|
|
assert len(result) == 1
|
|
assert result.txts[0] == "TREND PLOT REPORT"
|
|
|
|
|
|
def test_input_three_ndim_one_channel(engine):
|
|
img = cv2.imread(str(img_path))
|
|
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
|
|
img = img[:, :, 0]
|
|
img = img[..., None] # (H, W, 1)
|
|
|
|
result = engine(img)
|
|
assert len(result) >= 17
|
|
|
|
|
|
def test_mode_one_img(engine):
|
|
img_path = test_dir / "issue_170.png"
|
|
result = engine(img_path)
|
|
assert result.txts[0] == "TEST"
|
|
|
|
|
|
@mark.parametrize(
|
|
"img_name,gt_len,gt_first_len",
|
|
[
|
|
(
|
|
"test_letterbox_like.jpg",
|
|
2,
|
|
"A::取决于所使用的执行提供者,它可能没有完全支持模型中的所有操作。回落到CPU操作可能会导致性能速度的下降。此外,即使一个操作是由CUDAeXecution",
|
|
),
|
|
("test_without_det.jpg", 1, "在中国作家协会第三届儿童文学"),
|
|
],
|
|
)
|
|
def test_letterbox_like(engine, img_name, gt_len, gt_first_len):
|
|
img_path = test_dir / img_name
|
|
result = engine(img_path)
|
|
|
|
assert len(result) == gt_len
|
|
assert result.txts[0].lower() == gt_first_len.lower()
|