一、问题与需求

        在图片转文字方面,我从去年4月开始测试过非常多OCR项目,大致分为下面几类

        1)传统卷积OCR模型项目:PP-StructureV3、Umi-OCR(ch_PP-OCRv3_det_infer+ch_ppocr_mobile_v2.0_cls_infer+ch_PP-OCRv3_rec_infer)、unstructured(yolox + tesseract )

        2)多模态模型项目: docext(基于Qwen2.5-VL-3B微调)、qwen3.5系列原生多模态模型

        3)  专门用于OCR识别的多模态项目:PaddleOCR-VL、DeepSeek-OCR

按照时间顺序,可以看我之前的文章。

蓝耘Ubantu服务器测试最新 PP-StructureV3 教程-CSDN博客

Umi-OCR,完美解决企业OCR的核心痛点!!!【史上最全(万字)安装加测评】-CSDN博客

全网首发PaddleOCR-VL的VLLM部署教程【万字血泪史】-CSDN博客

Qwen3.5开源模型实测_encoder cache will be initialized with a budget of-CSDN博客

        实际部署应用的时候,需要考虑下面几点:

        1) 部署成本,显存资源,能不能稳定跑

        2) OCR识别速度和质量

        3) 客户端是否方便调用

二、三大类对比表

对比维度

Umi-OCR (传统卷积OCR)

qwen3.5系列 (通用多模态模型)

PaddleOCR-VL-1.5 (专用多模态OCR)

部署资源

- 最低配置:双核CPU、4GB内存
- 推荐配置:四核CPU、8GB内存+NVIDIA GPU(可选)
- 存储空间:基础版500MB,完整版2GB
- 内存占用:200-500MB

- 7B版本:4bit量化需8GB显存(RTX 3060/4060)
- 14B版本:12GB显存起步
- 27B版本:16GB显存(RTX 4080/3090)
- 35B版本:20GB+显存
- 磁盘空间:7B模型约214GB(4bit量化)

- 推荐配置:8GB+显存,16GB RAM
- 最低配置:RTX 3060(6GB显存)
- 模型大小:仅0.9B参数
- 生产部署:建议24GB+显存

部署难度

- 极简部署:Windows解压即用,双击启动
- 跨平台:支持Windows/Linux/macOS
- Docker支持:提供官方镜像
- 无环境依赖:无需复杂配置

- 中等难度:需要模型量化、环境配置
- 多种方案:llama.cpp、Docker、直接部署
- 技术门槛:需了解大模型部署知识
- 云端API:阿里云百炼提供便捷API

- 中等难度:官方提供Docker Compose方案
- vLLM部署:支持vLLM加速服务
- 昇腾适配:支持国产昇腾芯片
- OpenVINO:支持英特尔平台部署

识别速度

- 单张图片:0.5-2秒(取决于硬件)
- 批量处理:500张票据10分钟内
- A4文档:0.8秒(Intel i5)
- 实时识别:支持截图实时识别

- - 单张图片:10-20秒(取决于硬件)
- 延迟:受模型规模和硬件影响

- 单张图片:1-2秒(取决实践方案)
- vLLM加速:显著提升推理速度
- 多卡并行:支持多GPU加速

识别质量

- 印刷体:99.5%+准确率
- 综合识别:98.7%(ICDAR 2015)
- 复杂背景:92%以上准确率
- 数字/英文:几乎零错误

- 文本识别:支持结构化输出
- 视觉问答:强逻辑推理能力
- 专业术语:识别各学科符号系统

- SOTA性能:超越全球顶尖模型
- 现实场景:扫描、弯折、屏拍等场景领先
- 多语言:支持109种语言

手写体识别

- 手写体识别效果差

- 能力范围:作为多模态功能之一
- 实测表现:Qwen3.5-2B被称"OCR怪兽"
- 无需预处理:支持倾斜、模糊手写体
- 结构化输出:按原格式整理

- 专项优化:强化手写体识别能力
- 古籍文献:专门优化古籍手写识别
- 生僻字:增强生僻字识别性能
- 多语言:支持藏文、孟加拉语等

内容理解

- 不支持内容理解

- 深度理解:强逻辑推理和常识理解
- 图文问答:支持多轮深入问答
- 场景推理:能推测聚餐类型、人数等
- 专业分析:医学图像分析、代码解析

- 不支持内容理解

边识别边抽取

- 不支持边识别边抽取

- 智能抽取:基于指令的信息抽取
- 结构化解析:菜单菜品价格自动提取
- 条件筛选:能区分相似但不完全符合条件
- 多轮交互:支持追问和细化

- 不支持边识别边抽取

精准识别

- 高精度:常规印刷体超95%
- 错误率:较传统工具降低62%
- 置信度:支持实时查看置信度
- 质量控制:后处理规则优化

- 精准定位:视觉定位能力强大
- 细节捕捉:捕捉角落服务费提示等细节
- 格式保留:保留价格对齐格式
- 专业级:在MathVison等评测中最佳

- 异形框定位:支持多边形检测框
- 贴合检测:复杂采集下稳定返回贴合框
- 文本行定位:新增文本行精确定位
- 印章识别:专项印章识别能力

旋转图片识别

- 旋转图片识别效果差

- 无需预处理:直接识别各种角度文本
- 倾斜支持:支持45度以上倾斜文本
- 动态适应:自动适应图像方向
- 鲁棒性:对模糊、歪角度图都能识别

- 无需预处理:直接识别各种角度文本
- 倾斜支持:支持45度以上倾斜文本
- 动态适应:自动适应图像方向
- 鲁棒性:对模糊、歪角度图都能识别

公式识别

- 公式识别效果一般

- 数学推理:专业级公式处理能力
- LaTeX输出:输出标准LaTeX格式
- 解题步骤:能生成详细解题步骤
- 学科覆盖:微积分、矩阵、统计公式等

- 专项强化:复杂公式识别能力提升
- 学术文档:准确识别数学、物理公式
- 可编辑格式:转换为可编辑电子格式
- STEM优化:强化STEM领域公式识别

印章识别

- 印章识别效果差

- 多模态能力:作为视觉理解的一部分
- 元素识别:能识别图片中的印章元素
- 位置描述:支持印章位置大致描述
- 专业场景:适合证件、公文印章识别

- 专项能力:新增印章识别专项任务
- SOTA指标:在印章识别任务中创SOTA
- 遮挡处理:能处理印章遮挡文字情况
- 真实场景:营业执照等印章识别效果好

调用方法

- 图形界面:拖拽式操作,双击启动
- 命令行:支持--batch参数批量处理
- Python API:完整SDK支持
- REST API:企业级集成,HTTP调用
- Docker服务:容器化部署

- 云端API:阿里云百炼API服务
- Python SDK:官方Python接口
- vLLM服务:HTTP API服务调用
- 多语言:支持多种编程语言调用

- Python库:pip install paddleocr
- CLI工具:命令行快速验证
- vLLM服务:HTTP API服务调用
- Docker Compose:一键启动服务

总的来说

部署成本 多模态 >  专用多模态OCR > Umi-OCR

识别速度: Umi-OCR速度最快(0.5-1s),专用多模态OCR速度也很快(1-2.5s) 可以达到实时转化,多模态速度慢

纯文字无旋转图片识别效果   多模态  =  专用多模态OCR = Umi-OCR

手写、印章、表格、旋转图片识别效果:  多模态  =  专用多模态OCR  效果都很好;Umi-OCR不支持或识别效果很差

文档理解:只有多模态模型可以理解图片文档内容,然后输出Json信息

三、模型介绍

3.1 模型解释

2026 年 1 月 29 日,百度 PaddlePaddle 团队发布了 PaddleOCR-VL-1.5,一个仅有 0.9B(9 亿)参数的多任务视觉语言模型(VLM),在 OmniDocBench v1.5 基准测试中取得 94.5% 的准确率,创造了新的 SOTA(State-of-the-Art)记录。

更令人瞩目的是,这个轻量级模型在真实场景鲁棒性测试中超越了参数量大得多的通用 VLM,如 Qwen3-VL-235B 和 Gemini-3 Pro。

PaddleOCR-VL-1.5 代表了文档解析技术的范式转变:从单纯的文本识别到表格、公式、图表和印章的统一解析;从理想条件下的识别到处理扫描、倾斜、变形和屏幕拍照等真实场景。这标志着文档解析技术正式进入"实用性"和"智能化"的新时代。

3.2. 统一模型中的六大核心能力

PaddleOCR-VL-1.5 是真正的多任务模型,在单一架构中支持六大核心能力:

  1. OCR(文本识别):支持 100+ 种语言,新增对藏文和孟加拉文的支持,针对稀有字符、古文和文本装饰(下划线、强调标记)进行了优化
  2. 表格识别:支持复杂表格结构,包括自动跨页表格合并、多语言表格和无线表格
  3. 公式识别:支持 LaTeX 格式输出,特别针对扫描、变形和屏幕拍照等物理失真进行了优化
  4. 图表识别:理解并提取图表中的数据和趋势
  5. 印章识别(新增):识别官方印章和戳记,处理弯曲文字、模糊图像和背景干扰
  6. 文本定位(新增):支持精确的文本行定位和识别,使用 4 点四边形表示以适应旋转和倾斜布局

这种统一的多任务架构不仅简化了部署,更重要的是实现了不同任务间的知识共享和协同优化,使每项任务都能获得更好的性能。

四、最佳企业实践(重点)

PaddleOCR-VL-1.5-0.9B 最佳的方案是 vLLM 部署  +  openai API服务调用 这样的处理速度是最快的,而且有多种识别策略可以选择。

4.1 PaddleOCR-VL 部署

可以参考我之前的部署,当时刚刚出来,有较多的不兼容,现在看官网的教程应该好部署很多。

全网首发PaddleOCR-VL的VLLM部署教程【万字血泪史】-CSDN博客

4.2 openai API服务调用

import base64
import requests
from openai import OpenAI
import os
from pathlib import Path
from typing import Union, Optional, Dict, List
import time


class PaddleOCRVLClient:
    """
    调用PaddleOCR-VL vLLM服务的客户端,支持批量处理图片列表或base64列表
    
    支持任务类型:
    - ocr: 文本识别
    - table: 表格识别
    - formula: 公式识别
    - chart: 图表识别
    - spotting: 文本定位(检测+识别)
    - seal: 印章识别
    """
    
    def __init__(self, base_url: str, model_name: str, api_key: str = ""):
        """
        初始化客户端
        
        Args:
            base_url: vLLM服务地址,例如 "http://192.168.10.58:8085/v1"
            model_name: 模型名称,例如 "PaddleOCR-VL-1.5-0.9B"
            api_key: API密钥,如果服务不需要认证可以留空
        """
        self.client = OpenAI(
            base_url=base_url,
            api_key=api_key or "EMPTY",
            timeout=60
        )
        self.base_url = base_url
        self.model_name = model_name
        
        self.TASKS = {
            "ocr": "OCR:",
            "table": "Table Recognition:",
            "formula": "Formula Recognition:",
            "chart": "Chart Recognition:",
            "spotting": "Spotting:",
            "seal": "Seal Recognition:",
        }
    
    def get_available_tasks(self) -> List[str]:
        """获取所有可用的任务类型"""
        return list(self.TASKS.keys())
    
    def _encode_image(self, image_path: str) -> str:
        """将本地图片编码为base64格式的data URL"""
        with open(image_path, "rb") as image_file:
            encoded_image = base64.b64encode(image_file.read()).decode('utf-8')
        return f"data:image/jpeg;base64,{encoded_image}"
    
    def _process_single_image(self, image_input: str, prompt_text: str, temperature: float, max_tokens: int) -> str:
        """
        处理单张图片,返回识别结果字符串
        
        Args:
            image_input: 图片路径、URL或base64字符串
            prompt_text: 提示词
            temperature: 生成温度
            max_tokens: 最大生成token数
            
        Returns:
            识别结果字符串,失败返回空字符串
        """
        if image_input.startswith('http://') or image_input.startswith('https://'):
            image_url = image_input
        elif image_input.startswith('data:image/'):
            image_url = image_input
        else:
            image_url = self._encode_image(image_input)
        
        messages = [
            {
                "role": "user",
                "content": [
                    {"type": "image_url", "image_url": {"url": image_url}},
                    {"type": "text", "text": prompt_text}
                ]
            }
        ]
        
        try:
            response = self.client.chat.completions.create(
                model=self.model_name,
                messages=messages,
                temperature=temperature,
                max_tokens=max_tokens,
            )
            return response.choices[0].message.content
        except Exception:
            return ""
    
    def process_images(
        self,
        images: List[str],
        task_type: str = "ocr",
        custom_prompt: Optional[str] = None,
        temperature: float = 0.0,
        max_tokens: int = 15000
    ) -> List[str]:
        """
        批量处理图片列表或base64列表
        
        Args:
            images: 图片路径列表、URL列表或base64字符串列表
            task_type: 任务类型,可选值:"ocr", "table", "formula", "chart", "spotting", "seal"
            custom_prompt: 自定义提示词,如果提供则覆盖默认任务提示词
            temperature: 生成温度,0.0为确定性输出
            max_tokens: 最大生成token数
            
        Returns:
            识别结果字符串列表,每个元素对应输入列表中对应位置图片的识别结果
        """
        prompt_text = custom_prompt if custom_prompt else self.TASKS.get(task_type, "OCR:")
        
        results = []
        for image_input in images:
            result = self._process_single_image(image_input, prompt_text, temperature, max_tokens)
            results.append(result)
        
        return results
    
    def test_connection(self) -> bool:
        """测试与vLLM服务器的连接"""
        try:
            response = requests.get(f"{self.base_url.rstrip('/')}/models", timeout=5)
            if response.status_code == 200:
                print(f"✓ 连接成功,可用模型: {response.json()}")
                return True
            else:
                print(f"✗ 连接失败,状态码: {response.status_code}")
                return False
        except Exception as e:
            print(f"✗ 连接失败: {e}")
            return False
    
    def get_model_info(self) -> Dict:
        """获取模型信息"""
        return {
            "base_url": self.base_url,
            "model_name": self.model_name,
            "available_tasks": self.get_available_tasks(),
            "tasks_prompts": self.TASKS
        }


if __name__ == "__main__":
    ocr_client = PaddleOCRVLClient(
        base_url="http://192.168.10.58:8085/v1",
        model_name="PaddleOCR-VL-1.5-0.9B",
        api_key=""
    )
    
    print("测试服务器连接...")
    if not ocr_client.test_connection():
        print("请检查服务地址是否正确,或服务是否已启动")
        exit(1)
    
    print("\n=== 模型信息 ===")
    info = ocr_client.get_model_info()
    print(f"服务地址: {info['base_url']}")
    print(f"模型名称: {info['model_name']}")
    print("支持的任务:")
    for task, prompt in info['tasks_prompts'].items():
        print(f"  - {task}: {prompt}")
    
    test_image_path = r"C:\Users\GDZD-BG-202115\Desktop\GdzdExt\gdzdext\cases\营业执照横的.png"
    
    if os.path.exists(test_image_path):
        print("\n=== 测试1: 图片路径列表输入 ===")
        
        image_path_list = [test_image_path, test_image_path]
        
        results = ocr_client.process_images(image_path_list, task_type="ocr")
        
        print(f"处理了 {len(results)} 张图片")
        for i, result in enumerate(results, 1):
            print(f"\n图片 {i} 识别结果:")
            print(result)
        
        print("\n=== 测试2: Base64列表输入 ===")
        
        with open(test_image_path, "rb") as f:
            encoded = base64.b64encode(f.read()).decode('utf-8')
            base64_str = f"data:image/jpeg;base64,{encoded}"
        
        base64_list = [base64_str, base64_str]
        
        results = ocr_client.process_images(base64_list, task_type="ocr")
        
        print(f"处理了 {len(results)} 张图片")
        for i, result in enumerate(results, 1):
            print(f"\nBase64图片 {i} 识别结果:")
            print(result)
    else:
        print(f"测试图片不存在: {test_image_path}")

通过上述代码调用模型速度很快,比官方提供的走PaddleOCR包快很多,不需要在客户端下载模型。

五、测试效果

篇幅有限,直接说结论

1.手写体可以直接识别

2.不同的任务,可以专项识别出那部分内容,比如输入印章的任务,模型就只会输出印章的内容。

3. 整体速度很快,是目前性价比最好的。

六、参考内容

PaddlePaddle/PaddleOCR-VL_多模态模型_PaddlePaddle_FastDeploy-飞桨AI Studio星河社区

PaddleOCR-VL-1.5: Comprehensive Analysis of the 0.9B SOTA Document Parsing Model

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐