From 9cf8b6e7704eb87247e28de4832285121cea40e8 Mon Sep 17 00:00:00 2001 From: Max Swain <89113255+maxdswain@users.noreply.github.com> Date: Sun, 22 Mar 2026 20:13:18 +0000 Subject: [PATCH] perf: Use OpenCV over PIL for PNG encoding Signed-off-by: Max Swain <89113255+maxdswain@users.noreply.github.com> --- docling_core/types/doc/document.py | 8 ++++---- pyproject.toml | 1 + uv.lock | 21 +++++++++++++++++++++ 3 files changed, 26 insertions(+), 4 deletions(-) diff --git a/docling_core/types/doc/document.py b/docling_core/types/doc/document.py index 5ee8880a..cac5cb61 100644 --- a/docling_core/types/doc/document.py +++ b/docling_core/types/doc/document.py @@ -12,7 +12,6 @@ import typing import warnings from collections.abc import Sequence -from dataclasses import dataclass from enum import Enum from io import BytesIO from pathlib import Path @@ -26,6 +25,8 @@ ) from urllib.parse import unquote +import cv2 +import numpy as np import pandas as pd import yaml from PIL import Image as PILImage @@ -1109,9 +1110,8 @@ def validate_mimetype(cls, v): @classmethod def from_pil(cls, image: PILImage.Image, dpi: int) -> Self: """Construct ImageRef from a PIL Image.""" - buffered = BytesIO() - image.save(buffered, format="PNG") - img_str = base64.b64encode(buffered.getvalue()).decode("utf-8") + _, buffered = cv2.imencode(".png", cv2.cvtColor(np.array(image), cv2.COLOR_RGBA2BGRA)) + img_str = base64.b64encode(buffered.tobytes()).decode("utf-8") img_uri = f"data:image/png;base64,{img_str}" return cls( mimetype="image/png", diff --git a/pyproject.toml b/pyproject.toml index dca72c0e..ffec69e3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -50,6 +50,7 @@ dependencies = [ 'typer (>=0.12.5,<0.25.0)', 'latex2mathml (>=3.77.0,<4.0.0)', "defusedxml (>=0.7.1, <0.8.0)", + "opencv-python-headless>=4.13.0.92", ] [project.urls] diff --git a/uv.lock b/uv.lock index 8aadd04e..a0248f03 100644 --- a/uv.lock +++ b/uv.lock @@ -961,6 +961,7 @@ dependencies = [ { name = "jsonref" }, { name = "jsonschema" }, { name = "latex2mathml" }, + { name = "opencv-python-headless" }, { name = "pandas", version = "2.3.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, { name = "pandas", version = "3.0.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, { name = "pillow" }, @@ -1032,6 +1033,7 @@ requires-dist = [ { name = "jsonschema", specifier = ">=4.16.0,<5.0.0" }, { name = "latex2mathml", specifier = ">=3.77.0,<4.0.0" }, { name = "matplotlib", marker = "extra == 'examples'", specifier = ">=3.7.0" }, + { name = "opencv-python-headless", specifier = ">=4.13.0.92" }, { name = "openpyxl", marker = "extra == 'examples'", specifier = ">=3.1.5" }, { name = "pandas", specifier = ">=2.1.4,<4.0.0" }, { name = "pillow", specifier = ">=10.0.0,<13.0.0" }, @@ -2588,6 +2590,25 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/57/a7/b35835e278c18b85206834b3aa3abe68e77a98769c59233d1f6300284781/numpy-2.4.3-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:4b42639cdde6d24e732ff823a3fa5b701d8acad89c4142bc1d0bd6dc85200ba5", size = 12504685, upload-time = "2026-03-09T07:58:50.525Z" }, ] +[[package]] +name = "opencv-python-headless" +version = "4.13.0.92" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "numpy", version = "2.4.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/79/42/2310883be3b8826ac58c3f2787b9358a2d46923d61f88fedf930bc59c60c/opencv_python_headless-4.13.0.92-cp37-abi3-macosx_13_0_arm64.whl", hash = "sha256:1a7d040ac656c11b8c38677cc8cccdc149f98535089dbe5b081e80a4e5903209", size = 46247192, upload-time = "2026-02-05T07:01:35.187Z" }, + { url = "https://files.pythonhosted.org/packages/2d/1e/6f9e38005a6f7f22af785df42a43139d0e20f169eb5787ce8be37ee7fcc9/opencv_python_headless-4.13.0.92-cp37-abi3-macosx_14_0_x86_64.whl", hash = "sha256:3e0a6f0a37994ec6ce5f59e936be21d5d6384a4556f2d2da9c2f9c5dc948394c", size = 32568914, upload-time = "2026-02-05T07:01:51.989Z" }, + { url = "https://files.pythonhosted.org/packages/21/76/9417a6aef9def70e467a5bf560579f816148a4c658b7d525581b356eda9e/opencv_python_headless-4.13.0.92-cp37-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:5c8cfc8e87ed452b5cecb9419473ee5560a989859fe1d10d1ce11ae87b09a2cb", size = 33703709, upload-time = "2026-02-05T10:24:46.469Z" }, + { url = "https://files.pythonhosted.org/packages/92/ce/bd17ff5772938267fd49716e94ca24f616ff4cb1ff4c6be13085108037be/opencv_python_headless-4.13.0.92-cp37-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:0525a3d2c0b46c611e2130b5fdebc94cf404845d8fa64d2f3a3b679572a5bd22", size = 56016764, upload-time = "2026-02-05T10:26:48.904Z" }, + { url = "https://files.pythonhosted.org/packages/8f/b4/b7bcbf7c874665825a8c8e1097e93ea25d1f1d210a3e20d4451d01da30aa/opencv_python_headless-4.13.0.92-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:eb60e36b237b1ebd40a912da5384b348df8ed534f6f644d8e0b4f103e272ba7d", size = 35010236, upload-time = "2026-02-05T10:28:11.031Z" }, + { url = "https://files.pythonhosted.org/packages/4b/33/b5db29a6c00eb8f50708110d8d453747ca125c8b805bc437b289dbdcc057/opencv_python_headless-4.13.0.92-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:0bd48544f77c68b2941392fcdf9bcd2b9cdf00e98cb8c29b2455d194763cf99e", size = 60391106, upload-time = "2026-02-05T10:30:14.236Z" }, + { url = "https://files.pythonhosted.org/packages/fb/c3/52cfea47cd33e53e8c0fbd6e7c800b457245c1fda7d61660b4ffe9596a7f/opencv_python_headless-4.13.0.92-cp37-abi3-win32.whl", hash = "sha256:a7cf08e5b191f4ebb530791acc0825a7986e0d0dee2a3c491184bd8599848a4b", size = 30812232, upload-time = "2026-02-05T07:02:29.594Z" }, + { url = "https://files.pythonhosted.org/packages/4a/90/b338326131ccb2aaa3c2c85d00f41822c0050139a4bfe723cfd95455bd2d/opencv_python_headless-4.13.0.92-cp37-abi3-win_amd64.whl", hash = "sha256:77a82fe35ddcec0f62c15f2ba8a12ecc2ed4207c17b0902c7a3151ae29f37fb6", size = 40070414, upload-time = "2026-02-05T07:02:26.448Z" }, +] + [[package]] name = "openpyxl" version = "3.1.5"