尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

macOS原生OCR文字复制工具:利用Vision框架实现屏幕取词

macOS原生OCR文字复制工具:利用Vision框架实现屏幕取词 开发 macOS 应用时把屏幕上的文字“抓”下来复制到剪贴板是一个很常见的自动化需求。之前我一直用第三方 OCR 服务或者 Tesseract但配置麻烦、识别中文效果也不理想。后来发现 macOS 系统本身自带了 OCR 能力通过 Vision 框架就能调用不需要联网也不依赖任何第三方 SDK。看到有人分享了一个叫 “Monkey see, monkey do” 的小项目思路就是“眼睛看到什么就能复制什么”本质上就是屏幕截取 原生 OCR。这篇文章我就把这条链路完整拆解一遍带你从零实现一个 macOS 原生 OCR 文字复制工具代码可以直接跑。本文适合 macOS 开发者、自动化脚本爱好者也适合想了解 Vision 框架文本识别的大前端和 AI 方向读者。学完你可以掌握如何调用 macOS 原生 OCR 识别图片如何截取屏幕区域如何把识别结果写入剪贴板并了解权限配置、性能优化和常见坑点。1. 背景macOS 上实现 OCR 有哪些方案1.1 什么是 OCROCROptical Character Recognition光学字符识别的目的是从图像中提取文字。比如一张截图、一张扫描件、甚至摄像头拍到的画面只要里面有文字OCR 就能把它们变成可编辑、可复制的文本。在 macOS 上做文字识别常见方案有方案类型优缺点Tesseract开源本地引擎免费但需要安装语言包中文识别精度一般百度 OCR / 腾讯 OCR云端 API精度高但要联网、有调用配额涉及隐私macOS Vision系统原生框架免费、离线、集成度高支持中文无需额外依赖从工程角度看如果你的工具只在 macOS 上跑且不想把用户截图上传到云端Vision 框架是最合适的选择。它内置在系统里性能和识别率都经过苹果优化尤其是 macOS 12 之后对中文、日文、韩文的支持已经比较完善。1.2 “Monkey see, monkey do” 想表达什么“Monkey see, monkey do” 是一个英文俗语意思是“看到什么就模仿什么”。放到 OCR 工具里就是一个很形象的产品逻辑屏幕上出现什么文字工具就把它识别并复现出来。你可以想象这样的使用场景看视频时发现字幕里有想要保存的句子直接一框选就复制阅读加密 PDF 或图片型 PDF 时无法选中文字用 OCR 工具提取软件界面上的报错信息无法复制直接截屏识别开会时想把幻灯片里的文字快速做成笔记。这些需求都可以用 macOS 原生能力实现不需要购买专业软件。1.3 为什么推荐使用系统原生 OCR使用 Vision 框架的好处很明显离线可用不依赖网络不把数据上传到第三方服务器免费无限量本地计算没有 API 配额和费用隐私安全截图留在本机适合处理敏感文档中文支持好系统语言包已经内置开箱即用与 macOS 系统深度集成可以配合快捷键、Automator、Shortcuts 使用。如果你的应用只面向 macOS优先用系统能力往往是性价比最高的方案。2. 环境准备与版本说明在开始写代码之前先确认你的开发环境和运行环境。2.1 操作系统版本Vision 框架从 macOS 10.15 开始引入文本识别能力但早期的VNRecognizeTextRequest仅支持英文。如果你需要识别中文建议使用 macOS 12 Monterey 及之后版本此时 Live Text 已内置识别质量和语言丰富度提升明显。本文示例基于 macOS 13/14 实测理论兼容 macOS 12。如果你的系统是 macOS 10.15 或 11代码大部分可用但中文识别可能受限。2.2 开发工具Xcode可以使用 Xcode 14 或更高版本命令行工具也可以单独安装Swift示例代码基于 Swift 5Command Line Tools如果你不想建完整 Xcode 工程直接用swift命令编译运行也可以。检查 Command Line Tools 是否安装xcode-select --install查看 Swift 版本swift --version2.3 运行权限如果你的 OCR 工具只识别图片文件不需要额外权限。但如果你要实现屏幕实时取词需要给终端或 App 授权“屏幕录制”权限系统设置 → 隐私与安全性 → 屏幕录制把你运行工具所在的终端 App比如 Terminal、iTerm勾选上。如果你要把工具打包成独立 App还需在 App 的Info.plist中声明keyNSScreenCaptureUsageDescription/key string需要截取屏幕内容以进行 OCR 文字识别/string如果是命令行工具没有 Info.plist但系统仍会在第一次调用截屏 API 时弹出授权提示。2.4 示例项目结构为了让示例尽量简单我们不建 Xcode 工程直接用 Swift 脚本。项目结构如下monkey-see/ ├── main.swift ├── Package.swift # 可选如果使用 SwiftPM └── images/ └── sample.png你可以把main.swift当成独立命令行工具运行。3. 核心原理Vision 框架如何做文字识别Vision 是苹果提供的计算机视觉框架。它并不直接对外暴露 UI而是通过 request-handler 模式工作。3.1 核心概念VNRecognizeTextRequest文本识别请求对象负责配置识别参数VNImageRequestHandler图像请求处理器负责把图片数据交给 Vision 分析VNRecognizedTextObservation识别结果包含文字内容和坐标位置。一个完整的识别流程是把图片转换为CGImage创建VNRecognizeTextRequest创建VNImageRequestHandler并执行请求遍历VNRecognizedTextObservation提取文本。3.2 识别精度设置recognitionLevel有两个选项.fast速度快适合实时截屏.accurate精度高适合从图片中提取文字。对于截图场景建议先用.accurate提高准确率如果性能不够再改为.fast。3.3 语言支持通过recognitionLanguages可以指定识别语言。常用语言代码zh-Hans简体中文en-US英文ja-JP日文ko-KR韩文。如果希望同时识别中英文可以传入多个语言request.recognitionLanguages [zh-Hans, en-US]注意并非所有语言在所有系统版本上都可用建议做一次可用语言查询。3.4 坐标系统Vision 的坐标系统与 UIKit/AppKit 不同。Vision 使用归一化坐标原点在图片左下角y 轴向上。如果你需要把文字框画在截图原图上需要做坐标转换。3.5 核心代码示例下面是一个最小可用的 Swift 函数用来识别一张 PNG 图片中的所有文字import Vision import AppKit func recognizeText(from image: NSImage) - String { guard let cgImage image.cgImage(forProposedRect: nil, context: nil, hints: nil) else { return } var resultText let request VNRecognizeTextRequest { request, error in guard let observations request.results as? [VNRecognizedTextObservation] else { return } for observation in observations { if let candidate observation.topCandidates(1).first { resultText candidate.string \n } } } request.recognitionLevel .accurate request.recognitionLanguages [zh-Hans, en-US] request.usesLanguageCorrection true let handler VNImageRequestHandler(cgImage: cgImage, options: [:]) try? handler.perform([request]) return resultText }这段代码中topCandidates(1)返回置信度最高的一个候选文本usesLanguageCorrection会利用语言模型修正识别结果默认开启try? handler.perform忽略错误实际项目中建议用do-catch处理。4. 完整实战从屏幕截取到复制文字接下来我们实现一个完整的命令行工具用户启动程序后截取当前屏幕自动识别屏幕上的所有文字并复制到剪贴板。4.1 创建 Swift 脚本先创建一个文件夹mkdir monkey-see cd monkey-see touch main.swift用文本编辑器打开main.swift。4.2 获取屏幕截图macOS 截取主屏幕可以使用 Core Graphics 的CGDisplayCreateImage。这个 API 会返回一个CGImage可以直接交给 Vision 处理。import CoreGraphics func captureScreen() - CGImage? { let screenRect CGMainDisplayBounds() guard let image CGDisplayCreateImage(CGMainDisplayID()) else { return nil } return image }CGMainDisplayID()返回主显示器 IDCGDisplayCreateImage会生成该显示器的图像。如果你想截取某个区域可以在生成图片后使用cropping(to:)裁剪。4.3 对截图进行 OCR 识别我们复用上一节的recognizeText函数但输入变成CGImage避免 NSImage 转换开销import Vision func recognizeText(from cgImage: CGImage) - String { var resultText let request VNRecognizeTextRequest { request, error in guard error nil else { print(识别错误: \(error!.localizedDescription)) return } guard let observations request.results as? [VNRecognizedTextObservation] else { return } for observation in observations { if let candidate observation.topCandidates(1).first { resultText candidate.string \n } } } request.recognitionLevel .accurate request.recognitionLanguages [zh-Hans, en-US] request.usesLanguageCorrection true let handler VNImageRequestHandler(cgImage: cgImage, options: [:]) do { try handler.perform([request]) } catch { print(执行失败: \(error.localizedDescription)) } return resultText }4.4 写入剪贴板macOS 里写入剪贴板最简单的方式是使用NSPasteboardimport AppKit func copyToPasteboard(_ text: String) { let pasteboard NSPasteboard.general pasteboard.clearContents() pasteboard.setString(text, forType: .string) }注意命令行工具使用 AppKit 需要保证有图形会话普通终端运行没有问题。4.5 主程序组装在main.swift里把这三步串起来import Foundation import AppKit import Vision import CoreGraphics // ... 上面定义的函数 ... main struct MonkeySee { static func main() { print(正在截取屏幕...) guard let screenImage captureScreen() else { print(截屏失败) exit(1) } print(正在进行 OCR 识别...) let text recognizeText(from: screenImage) guard !text.isEmpty else { print(没有识别到文字) exit(0) } copyToPasteboard(text) print(识别完成已复制以下内容到剪贴板) print(-----------------------------) print(text) } }如果你不想用main也可以直接把顶层代码写在main.swift中。4.6 编译运行由于 Swift 命令行工具可以直接从源文件编译执行swiftc -o monkey-see main.swift -framework Vision -framework AppKit -framework CoreGraphics然后运行./monkey-see首次运行可能会弹出“终端想要截取屏幕图像”的授权提示点击允许后需要重新运行。4.7 测试效果准备一张包含中英文文字的图片或者打开任意网页运行工具后正在截取屏幕... 正在进行 OCR 识别... 识别完成已复制以下内容到剪贴板 ----------------------------- 这个网站使用 Cookie 来增强您的浏览体验。 Hello, welcome to my blog!打开文本编辑器粘贴就能看到完整内容。5. 进阶实现矩形区域 OCR 和快捷键唤起整屏识别虽然简单但实际使用时往往只需要识别某一块区域。比如弹窗中的错误信息、视频字幕、表格中的某一列。下面我们增加区域选择能力。5.1 使用screencapture命令配合区域截图最简单的方式是调用系统自带的screencapture命令行工具。它可以让你手动框选区域。screencapture -i /tmp/ocr_selection.png如果希望截取后光标变成十字准线并允许用户拖动选择区域使用-i交互模式。截图成功后生成的 PNG 文件再交给 OCR 识别。5.2 Swift 调用screencapture在 Swift 中可以用Process执行系统命令import Foundation func captureSelection() - URL? { let url URL(fileURLWithPath: /tmp/ocr_selection.png) let process Process() process.executableURL URL(fileURLWithPath: /usr/sbin/screencapture) process.arguments [-i, url.path] do { try process.run() process.waitUntilExit() if FileManager.default.fileExists(atPath: url.path) { return url } } catch { print(截屏失败: \(error)) } return nil }注意screencapture在较新的 macOS 中路径通常是/usr/sbin/screencapture也可能位于/sbin建议用xcrun --find screencapture查找。5.3 从文件加载图片并识别拿到截图文件后用 NSImage 加载if let url captureSelection() { guard let image NSImage(contentsOf: url), let cgImage image.cgImage(forProposedRect: nil, context: nil, hints: nil) else { print(无法加载截图) exit(1) } let text recognizeText(from: cgImage) copyToPasteboard(text) print(text) }5.4 绑定快捷键如果你希望按下某个快捷键就启动“区域 OCR”流程可以使用全局热键库比如Carbon 的RegisterEventHotKey第三方库HotKeySwiftPMmacOS 自带 Shortcuts 快捷指令。用 Carbon 实现全局快捷键比较繁琐这里给出思路。更推荐使用HotKey这个开源库它封装了事件监听使用起来更现代。import HotKey let hotKey HotKey(key: .space, modifiers: [.control]) hotKey.keyDownHandler { // 执行区域截图和 OCR startRegionCaptureAndRecognize() }注意第三方库需要额外集成如果是个人学习完全可以用 Automator 或者 BetterTouchTool 去绑定快捷键。5.5 权限补充使用screencapture -i时系统同样会要求屏幕录制权限。如果出现黑屏或无法截图请检查权限设置。6. 常见问题与排查思路6.1 问题对照表问题现象常见原因解决思路截屏返回空图没有屏幕录制权限系统设置中勾选终端的屏幕录制权限识别不到任何文字图片中没有文字 / 识别语言未配置换测试图片添加语言参数中文识别乱码recognitionLanguages只设置了en-US添加zh-Hans坐标错位Vision 坐标系与 AppKit 不一致转换坐标y 1 - normalizedY运行效率低使用.accurate且图片过大调整为.fast或缩小图片尺寸命令行工具无法使用 AppKit未导入AppKit框架编译时加-framework AppKit弹出安全提示系统首次请求屏幕录制权限点击允许后重启终端6.2 识别不了中文怎么办首先确认系统版本。macOS 12 以下对中文支持有限建议升级系统。其次在请求中明确指定中文request.recognitionLanguages [zh-Hans]如果仍无效用下面代码查询当前可用的识别语言let supported try? VNRecognizeTextRequest.supportedRecognitionLanguages(for: .accurate, revision: VNRecognizeTextRequestRevision3) print(supported ?? [])输出结果会列出所有支持的语言代码。6.3 截屏权限出现“系统数据占用过大”或无法截取这和 OCR 本身无关但很多用户会遇到。如果你的 macOS 系统提示“系统数据占用过大”可以检查“屏幕录制”权限列表是否有残留的已卸载 App。清理方法重置终端权限或重启系统。如果鼠标光标在screencapture -i交互模式下不显示可能是全屏截图权限未开启或者正在使用虚拟机。虚拟机环境下屏幕捕获行为与真实 macOS 不同请参考虚拟机的显示驱动配置。6.4 OCR 识别结果顺序不对Vision 返回的观察结果并不是绝对按阅读顺序排列的。解决办法是手动排序let sortedObservations observations.sorted { a, b in if abs(a.boundingBox.midY - b.boundingBox.midY) 0.01 { return a.boundingBox.midY b.boundingBox.midY } else { return a.boundingBox.minX b.boundingBox.minX } }由于 Vision 坐标原点在左下角midY越大表示位置越靠上。按从下到上不这里我们是要按阅读顺序先按 y 降序上到下同一行内按 x 升序左到右。6.5 编译时找不到 Vision 框架确保命令行编译时添加了-framework Visionswiftc -o monkey-see main.swift -framework Vision -framework AppKit -framework CoreGraphics如果你使用 SwiftPM则需要在Package.swift中链接系统框架。7. 最佳实践与工程建议7.1 合理选择识别精度如果你的应用需要实时处理视频帧建议使用.fast并在每帧之间做节流。对于静态截图.accurate更合适。7.2 提高识别准确率识别前对图片做二值化或增强对比度将图片缩放至合适大小过大或过小都会影响效果对旋转的文字设置recognitionLevel .accurate但仍有限度对结果做后处理例如去掉多余空格、修正标点。7.3 隐私与安全不要把截屏图片明文保存在磁盘上用完即删如果网络传输 OCR 结果务必加密如果你的 App 会处理敏感数据在隐私政策中明确说明在用户授权前不要触发截图否则可能被系统拒绝。7.4 处理多语言建议根据用户系统语言动态设置识别语言而不是写死。可以通过Locale.preferredLanguages获取当前语言映射到 Vision 的语言代码。7.5 内存管理Vision 处理大图时会占用较多内存。在命令行工具中识别完成后及时释放对象。如果是 iOS/macOS App建议将识别任务放到后台队列DispatchQueue.global(qos: .userInitiated).async { let text recognizeText(from: cgImage) DispatchQueue.main.async { self.textView.string text } }7.6 日志与错误处理生产工具一定要有完整的错误日志。可以把每次识别耗时、识别语言、候选文字数量记录下来方便后续调优。7.7 打包成 App如果你把工具分发给同事使用可以打包成.app并在Info.plist声明权限。使用osascript或 Automator 也能快速做成“服务”菜单项无需 Xcode。7.8 与最新系统兼容macOS Sequoia 发布后很多用户遇到“若要打开此 App你需要从 macOS 恢复启动并将安全策略更改为完整安全”的问题。这本质上是自定义内核扩展或未签名 App 的安全策略设置。对普通 OCR 工具来说建议使用 Developer ID 签名避免用户被安全策略卡住。7.9 性能优化方向如果你要做“全局 OCR 搜索”这类工具可以考虑监听屏幕变化增量识别使用VNImageRequestHandler的regionOfInterest只识别变化区域使用 GPU 加速但 Vision 默认已经使用系统加速能力建立文字缓存避免重复识别。8. 总结与下一步学习方向通过本文的实战你已经掌握了 macOS 原生 OCR 的核心链路使用VNRecognizeTextRequest识别图片文字使用CGDisplayCreateImage或screencapture截取屏幕使用NSPasteboard写入剪贴板处理屏幕录制权限通过识别语言参数解决中文支持问题。这套方案非常适合做个人效率工具比如“截图 OCR”“屏幕取词”“离线文字提取”。如果想继续深入可以尝试在 iOS 上使用同样的 Vision API 实现拍照取词结合 Core ML 做自定义文字分类利用 Live Text 的底层能力做更聪明的 OCR 应用把 Swift 代码封装成命令行工具通过 Python 调用形成更灵活的自动化脚本。关于 Python 调用可以借助PyObjC框架但需要安装对应 macOS 版本支持的 pyobjc-framework-Vision 包。环境配置时要注意 Python 版本与 PyObjC 的兼容性建议在虚拟环境中安装。最后提醒一点屏幕录制权限在开发调试阶段经常被忽略一旦发现截屏全黑优先检查“隐私与安全性”里的授权列表。调试完成后把权限限定在最小范围保障用户隐私。如果你也打算做一个类似的“看到什么就复制什么”的小工具不妨先从今天的示例改造起。加上一个快捷键再配合区域截图就是一个相当顺手的日常效率工具了。
返回列表