OCR 扫描件识别

Model: qwen-plus | ¥0.10/call
OCR文字识别扫描件图片转文字PDF发票识别中英文混排视觉模型扫描 PDF提取文字

OCR 文字识别 ocr-scan:图片、扫描版 PDF 一键转可复制文字,支持中文简体/英文/数字混排。有文本层自动跳过,扫描页强制 OCR。调 AIMS 平台视觉模型,本机无需装 Tesseract。适合发票识别、截图转文字、扫描件转 Word。

Skill Documentation

---

name: ocr-scan

description: OCR 文字识别 ocr-scan:图片、扫描版 PDF 一键转可复制文字,支持中文简体/英文/数字/标点混排。有文本层自动跳过(不浪费扣费),扫描页强制 OCR。调 AIMS 平台视觉模型,本机无需装 Tesseract。适合发票识别、截图转文字、扫描件转 Word、扫描合同提取文字、教材扫描转 Word、扫描 PDF 变可复制文字(Ctrl+F 可搜)。

---

⚠️ chat 模式使用须知(重要)

本 skill 的真实能力依赖平台后端工具。在网页 / chat 端点下,这些工具可能不可用:

此时**请勿轻信**,请改用 **agent / CLI 路径**运行本 skill 以获取真实结果。任何路径下都**严禁谎称「已调用 / 已搜索完成」而实际未执行**。

OCR 扫描件识别(ocr-scan)

把图片和扫描版 PDF 里的文字识别成可复制的文本。识别调用 AIMS 平台的视觉模型(qwen-vl-ocr-latest)完成,本地**无需安装任何 OCR 引擎**(不再需要 Tesseract)。

典型使用场景

**场景 1:财务整理发票** —— 把一堆发票拍照/扫描图批量转文字,按抬头 + 金额 + 日期整理成报销单,免去手敲。

**场景 2:学生扫描教材** —— 把教材扫描版 PDF 转成可复制的 Word 文字,Ctrl+F 直接搜重点段落,笔记效率翻倍。

**场景 3:客服提取截图** —— 用户发的截图含错误信息/地址/订单号,OCR 一键转文字直接复制到工单系统。

适用人群

| 角色 | 典型用途 |

|------|----------|

| 财务 / 会计 | 发票识别、报销单整理 |

| 学生 / 教师 | 教材扫描转 Word、课件截图转文字 |

| 客服 / 运营 | 截图提取文字、订单号识别 |

| 行政 / 文员 | 扫描合同提取文字、扫描通知归档 |

| 律师 / 法务 | 扫描证据材料转文字 |

| 研究员 / 学者 | 扫描文献提取引用信息 |

完整数据示例(实测)

# 1. 图片 OCR
python scripts/ocr.py image --input 发票.png --output 发票文字.txt

# 2. 扫描版 PDF(自动检测有/无文本层)
python scripts/ocr.py pdf --input 扫描件.pdf --output 文字.txt

# 3. 强制全页 OCR(忽略文本层)
python scripts/ocr.py pdf --input 扫描件.pdf --output 文字.txt --force

输出示例:

{"ok": true, "chars": 1234, "preview": "浙江增值税普通发票\n开票日期:2026-09-17\n金额:¥1234.56..."}

为什么选 ocr-scan

| 对比项 | 装 Tesseract | ocr-scan |

|--------|--------------|----------|

| 安装 | 装 Tesseract + 中文包 + 折腾路径 | pip install Pillow pymupdf |

| 中文准确率 | 一般(自己训练语料可达标)| 平台视觉模型,中文友好 |

| 扫描 PDF 混合页 | 需要自己判断哪些页要 OCR | 自动检测,有文本层直接用 |

| 加密 / 复杂排版 | 自己写代码 | 平台模型擅长 |

| 成本 | 免费但费时间 | 按次付费,¥0.10/次起 |

安装依赖

pip install Pillow pymupdf

安装依赖

pip install Pillow pymupdf

使用前准备:配置 AIMS API Key

本 skill 的识别在 AIMS 平台完成,需要您自己的 AIMS API Key(在 aimsgateway.cn 控制台获取):

export AIMS_API_KEY=aims_sk_你的key
# 或每次命令加 --api-key aims_sk_你的key

触发条件

用户要求"图片转文字 / 扫描件识别 / OCR / 提取图片里的文字 / 扫描 PDF 变文字 / 发票识别 / 截图转文字"时触发。

图片识别

python scripts/ocr.py image --input 截图.png --output 文字.txt

扫描版 PDF 识别

python scripts/ocr.py pdf --input 扫描件.pdf --output 文字.txt [--force]

处理规则

1. 支持中文简体、英文、数字、标点混排

2. 输出 UTF-8 文本;`--output` 缺省仅打印预览

3. 输出 JSON:`{"ok": true, "chars": N, "preview": "..."}`

常见问题

报错"缺少 AIMS API Key"怎么办?

先设置环境变量 `AIMS_API_KEY=aims_sk_你的key`,或在命令后加 `--api-key`。这是您本人在 AIMS 平台的密钥,识别请求会带上它调用视觉模型。

识别不准或漏字怎么办?

扫描 PDF 可加 `--force` 强制全页 OCR;图片模糊、倾斜、低分辨率会降低准确率,建议提供清晰原图。复杂表格、手写体、艺术字准确率有限,属正常情况。

失败处理

| 情况 | 处理 |

|---|---|

| 未配置 AIMS_API_KEY | 报错并提示设置方式 |

| 图片损坏/格式不支持 | 明确报错 |

| PDF 无文本层且无图片 | 报错提示该文件无内容可识别 |

| 平台调用失败 | 报错并回显原因 |

安全

<!-- ===== 以下为内嵌脚本代码(agent 安装时按需落盘为 scripts/<name> 并 chmod +x) ===== -->

文件:scripts/ocr.py

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""OCR 识别脚本(平台视觉模型版):图片 / 扫描版 PDF 文字识别。

原理:把图片用 base64 传给 AIMS 平台的视觉模型(qwen-vl-ocr-latest)做 OCR,
      识别完全在平台完成,本地不安装任何 OCR 引擎(无需 Tesseract)。

用法:
  python ocr.py image --input scan.png --output out.txt
  python ocr.py pdf   --input scan.pdf --output out.txt

鉴权:需 AIMS API Key,二选一:
  1) 环境变量  AIMS_API_KEY=aims_sk_xxx
  2) 命令行    --api-key aims_sk_xxx
"""
import argparse
import base64
import io
import json
import os
import sys
import time
import urllib.request
from pathlib import Path

AIMS_URL = "https://aimsgateway.cn/mcp/"
OCR_MODEL = "qwen-vl-ocr-latest"
OCR_PROMPT = (
    "请逐行转录这张图片里的全部文字(含中文、英文、数字、标点),"
    "保持原顺序,只输出识别到的文字,不要解释、不要加编号。"
)


def _get_key(args):
    key = getattr(args, "api_key", None) or os.environ.get("AIMS_API_KEY")
    if not key:
        raise RuntimeError(
            "缺少 AIMS API Key:请先设置环境变量 AIMS_API_KEY,或在命令后加 --api-key aims_sk_xxx"
        )
    return key


def _rpc(url, key, method, params, mid, tries=3):
    body = json.dumps(
        {"jsonrpc": "2.0", "id": mid, "method": method, "params": params}
    ).encode()
    headers = {
        "Content-Type": "application/json",
        "Authorization": f"Bearer {key}",
    }
    last = None
    for _ in range(tries):
        try:
            req = urllib.request.Request(url, data=body, method="POST", headers=headers)
            with urllib.request.urlopen(req, timeout=180) as r:
                return json.loads(r.read().decode())
        except Exception as e:  # noqa: BLE001
            last = e
            time.sleep(2)
    raise RuntimeError(f"MCP 调用失败: {last}")


def _handshake(key):
    _rpc(
        AIMS_URL,
        key,
        "initialize",
        {
            "protocolVersion": "2025-03-26",
            "capabilities": {},
            "clientInfo": {"name": "ocr-scan", "version": "1.0"},
        },
        1,
    )
    try:
        _rpc(AIMS_URL, key, "notifications/initialized", {}, 2)
    except Exception:  # noqa: BLE001
        pass


def _chat(key, data_url):
    resp = _rpc(
        AIMS_URL,
        key,
        "tools/call",
        {
            "name": "aims.chat",
            "arguments": {
                "model": OCR_MODEL,
                "messages": [
                    {
                        "role": "user",
                        "content": [
                            {"type": "image_url", "image_url": {"url": data_url}},
                            {"type": "text", "text": OCR_PROMPT},
                        ],
                    }
                ],
                "max_tokens": 4000,
                "temperature": 0,
                "no_rag": True,  # 2026-09-16 补丁:防止 RAG 知识库干扰视觉模型
            },
        },
        7,
    )
    try:
        txt = resp["result"]["content"][0]["text"]
    except Exception:  # noqa: BLE001
        raise RuntimeError("平台返回格式异常: " + json.dumps(resp, ensure_ascii=False)[:300])
    # 兼容 OpenAI 格式 JSON 字符串
    try:
        obj = json.loads(txt)
        txt = obj["choices"][0]["message"]["content"]
    except Exception:  # noqa: BLE001
        pass
    return txt.strip()


def _image_to_data_url(path):
    p = Path(path)
    if not p.exists():
        raise FileNotFoundError(path)
    b64 = base64.b64encode(p.read_bytes()).decode()
    ext = p.suffix.lower()
    mime = {
        "png": "image/png",
        "jpg": "image/jpeg",
        "jpeg": "image/jpeg",
        "gif": "image/gif",
        "webp": "image/webp",
        "bmp": "image/bmp",
    }.get(ext, "image/png")
    return f"data:{mime};base64,{b64}"


def cmd_image(args):
    key = _get_key(args)
    _handshake(key)
    data_url = _image_to_data_url(args.input)
    text = _chat(key, data_url)
    _save(text, args.output)
    return {"ok": True, "output": args.output, "chars": len(text), "preview": text[:300]}


def cmd_pdf(args):
    key = _get_key(args)
    try:
        import fitz  # PyMuPDF
    except ImportError:
        raise RuntimeError("PDF 识别需安装 PyMuPDF:pip install pymupdf")
    _handshake(key)
    doc = fitz.open(args.input)
    parts = []
    chars = 0
    zoom = 2.0  # 提高分辨率提升 OCR 准确率
    for i, page in enumerate(doc, 1):
        if not args.force:
            txt0 = page.get_text().strip()
            if txt0:
                parts.append(f"---- 第 {i} 页(文本层)----\n{txt0}")
                chars += len(txt0)
                continue
        pix = page.get_pixmap(matrix=fitz.Matrix(zoom, zoom))
        raw = pix.tobytes("png")
        b64 = base64.b64encode(raw).decode()
        data_url = f"data:image/png;base64,{b64}"
        text = _chat(key, data_url)
        parts.append(f"---- 第 {i} 页 ----\n{text}")
        chars += len(text)
    body = "\n\n".join(parts)
    _save(body, args.output)
    return {
        "ok": True,
        "output": args.output,
        "pages": doc.page_count,
        "chars": chars,
        "preview": body[:300],
    }


def _save(text, output):
    if output:
        Path(output).parent.mkdir(parents=True, exist_ok=True)
        Path(output).write_text(text, encoding="utf-8")


def build_parser():
    ap = argparse.ArgumentParser(description="OCR 识别(平台视觉模型)")
    ap.add_argument(
        "--api-key",
        default=None,
        help="AIMS API Key(也可用环境变量 AIMS_API_KEY)",
    )
    sub = ap.add_subparsers(dest="command", required=True)

    p = sub.add_parser("image", help="图片 OCR")
    p.add_argument("--input", required=True)
    p.add_argument("--output", default=None)
    p.set_defaults(func=cmd_image)

    p = sub.add_parser("pdf", help="扫描版 PDF OCR(无文本层页面)")
    p.add_argument("--input", required=True)
    p.add_argument("--output", default=None)
    p.add_argument("--force", action="store_true", help="忽略文本层,强制 OCR 全部页面")
    p.set_defaults(func=cmd_pdf)
    return ap


def main():
    args = build_parser().parse_args()
    try:
        result = args.func(args)
    except Exception as e:  # noqa: BLE001
        print(json.dumps({"ok": False, "error": str(e)}, ensure_ascii=False))
        sys.exit(1)
    print(json.dumps(result, ensure_ascii=False, indent=2))


if __name__ == "__main__":
    main()

FAQ

报错"缺少 AIMS API Key"怎么办?

先设置环境变量 `AIMS_API_KEY=aims_sk_你的key`,或在命令后加 `--api-key`。这是您本人在 AIMS 平台的密钥,识别请求会带上它调用视觉模型。

识别不准或漏字怎么办?

扫描 PDF 可加 `--force` 强制全页 OCR;图片模糊、倾斜、低分辨率会降低准确率,建议提供清晰原图。复杂表格、手写体、艺术字准确率有限,属正常情况。