当前位置: 首页 > news >正文

Swift如何使用Vision来识别获取图片中的文字(OCR),通过SwiftUI视图和终端命令行,以及一系列注意事项

在过去的一年里,我发现苹果系统中的“文字搜图片”功能非常好用,这个功能不光 iPhone/iPad,Mac 也有,找一些图片真的很好用。但是遇到了一个问题:这个功能需要一段时间才能找到新的图片,而且没法手动刷新,这对于外接硬盘里的图片来说不方便。所以就想自己能不能写一个类似的程序来查找一些图片。

这个程序的功能还挺好实现的:就是通过图片中的文字或者物体进行查找,而这两个功能苹果都替我们做好了,我们可以做到苹果演示的文本识别和相册识别所能达到的效果。不过本文只讲述如何使用 Vision 识别图片中的文字,因为识别图像和本文类似,存放这些数据到数据库中我也写过如何使用 Core Data 的博客:SwiftUI——Core Data数据库的使用(在纯SwiftUI生命周期中)。

本文较长,建议通过侧边栏跳转阅读。

简单介绍 Vision

首先简单介绍一下 Vision:

Vision 是一个计算机视觉算法的架构,可以对图像和视频执行多种任务。支持 iOS 11/iPad OS 11/macOS 10.13/tvOS 11 或更新系统。支持 ISO 语言代码中的所有语言。
需要注意由于汉字的复杂性,自定义单词(customWords)功能和语言矫正功能对于中文不可用。

需要注意 Vision 是包含在这些系统中的,而不是程序里,所以编译出来的程序本身并不会很大,并且结果精度和系统版本挂钩,后续会有演示。但很可惜的是对于中文手写的识别不太好,精度不是很好,但是对于英文的识别还是不错的。

比如这样的一张手写英文+汉字的图像:
请添加图片描述

在最新的 iPad OS 16 中识别出来为:

请添加图片描述

中文识别精度可见非常不行。

测试图片

测试图片是一张系统截图,不使用手写图的原因上面你也看到了,中文识别很难说达到了可用的程度。

请添加图片描述

将其命名为info,放在一个你喜欢的位置和放在 Assets 中,方便后续使用,如下:

请添加图片描述

不同平台的代码实现

接下来将会介绍如何在 iOS/iPad OS 和 macOS 上识别获取图像中的文本,将会分为两部分来说。(按需求来说不应该有 iOS/iPad OS,但是想都试试看,万一用的到呢)

分为两部分是因为在 iOS/iPad OS 系统上,使用的图像格式为UIImage,而 macOS 中使用的是NSImage,不过二者只有一小部分不一样。

这里的NS前缀表示“NeXTSTEP”,这是当年乔布斯回到苹果带回来的成果。

iOS/iPad OS

这里使用 SwiftUI 来进行布局。

首先导入框架和库:

import SwiftUI
import Vision

然后新建一个视图,内容如下(为了阅读和复制代码的体验,在注释中解释代码的含义):

struct ContentView: View {//这个字符串数组是为了存放获取的文本@State var textStrings = [String]()//这个name用来指定使用哪个图像,如果想用其他图像修改这个变量就行@State var name = "info"var body: some View {VStack {Image(uiImage: UIImage(named: name)!)//这个循环是显示获取的文本ForEach(textStrings, id: \.self) { testString inText(testString)}}.padding()//这样一打开App就自动识别了.onAppear(perform: {//生成执行需求的CGImage,也就是对这个图片进行OCR文本识别guard let cgImage = UIImage(named: name)?.cgImage else { return }//创建一个新的图像请求处理器let requestHandler = VNImageRequestHandler(cgImage: cgImage)//创建一个新的识别文本请求let request = VNRecognizeTextRequest(completionHandler: handleDetectedText)//使用accurate模式识别,不推荐使用fast模式,因为这是采用传统OCR的,精度太差了request.recognitionLevel = .accurate//设置偏向语言,不加的话会全按照英文和数字识别//中文一起能识别的其他文字只有英文//繁体中文为zh-Hant,其他语言码请见https://www.loc.gov/standards/iso639-2/php/English_list.phprequest.recognitionLanguages = ["zh-Hans"]do {//执行文本识别的请求try requestHandler.perform([request])} catch {print("Unable to perform the requests: \(error).")}})}//这个函数用来处理获取的文本func handleDetectedText(request: VNRequest?, error: Error?) {if let error = error {print("ERROR: \(error)")return}//results就是获取的结果guard let results = request?.results, results.count > 0 else {print("No text found")return}//通过循环将results的结果放到textStrings数组中//你可以在这里进行一些处理,比如说创建一个数据结构来获取获取文本区域的位置和大小,或者一些其他的功能。!!!通过observation的属性就可以获取这些信息!!!for result in results {if let observation = result as? VNRecognizedTextObservation {//topCandidates(1)表示在候选结果里选择第一个,最多有十个,你也可以在这里进行一些处理for text in observation.topCandidates(1) {//将results的结果放到textStrings数组中let string = text.stringtextStrings.append(string)}}}}
}

这时候运行就能看到结果了:

请添加图片描述

可以看到除了最开始的“展开”符号被识别成v之外,几乎没有识别错误。

macOS

接下来先介绍一下如何在 macOS 上实现这个功能。

首先新建一个空白文本文件ocr.swift,然后输入以下内容:

import SwiftUI
import Vision
import Foundationfunc handleDetectedText(request: VNRequest?, error: Error?) {if let error = error {print("ERROR: \(error)")return}guard let results = request?.results, results.count > 0 else {print("No text found")return}//通过循环将results的结果全部打印//你可以在这里进行一些处理,比如说创建一个数据结构来获取获取文本区域的位置和大小,或者一些其他的功能。!!!通过observation的属性就可以获取这些信息!!!for result in results {if let observation = result as? VNRecognizedTextObservation {//topCandidates(1)表示在候选结果里选择第一个,最多有十个,你也可以在这里进行一些处理for text in observation.topCandidates(1) {//打印识别的文本字符串let string = text.stringprint(string)}}}
}func ocrImage(path: String) {let cgImage = NSImage(byReferencingFile: path)?.ciImage()?.cgImage//创建一个新的图像请求处理器let requestHandler = VNImageRequestHandler(cgImage: cgImage!)//创建一个新的识别文本请求let request = VNRecognizeTextRequest(completionHandler: handleDetectedText)//使用accurate模式识别,不推荐使用fast模式,因为这是采用传统OCR的,精度太差了request.recognitionLevel = .accurate//设置偏向语言,不加的话会全按照英文和数字识别//中文一起能识别的其他文字只有英文//繁体中文为zh-Hant,其他语言码请见https://www.loc.gov/standards/iso639-2/php/English_list.phprequest.recognitionLanguages = ["zh-Hans"]do {//执行文本识别的请求try requestHandler.perform([request])} catch {print("Unable to perform the requests: \(error).")}
}extension NSImage {//NSImage转CIImagefunc ciImage() -> CIImage? {guard let data = self.tiffRepresentation,let bitmap = NSBitmapImageRep(data: data) else {return nil}let ci = CIImage(bitmapImageRep: bitmap)return ci}
}//执行函数,从命令行参数中获取图片的地址
ocrImage(path: CommandLine.arguments[1])

然后编译:

$ swiftc -o ocr ocr.swift

运行就可以看到这样的结果:

$ ./ocr ../info.png 
通用:
种类:宗卷
创建时间:1970年1月1日星期四 08:00
修改时间:1980年1月1日星期二 00:00
格式:EXFAT
容量:511.88 GB
可用:300.78GB
已使用:211,106,529,280字节 (磁盘上的
211.11 GB)

你可能会发现开头的v不见了,这是因为我使用的 macOS 是 12,而不是最新的,所以和 iOS 16 的结果不一样。

这个代码你还可以将其放到 Playground 中,可以看到每一步的状况。

建议你尝试用这个命令识别一些其他的图像,精度还是可以的。

识别对比和测试

上面是最理想的情况下测试,接下来进行一些不同设置或情形的识别结果对比,算是一种实验记录了。

新旧系统对比

macOS 12 对应的是 iOS 15。上文提到了macOS 12 和 iPadOS 16 的对比,这里记录一下手写文本的识别情况。

请添加图片描述

对于上面这张图来说,最新的 iPad OS 16 的结果为:

请添加图片描述

很完美。

而 macOS 12 的结果为:

$ ./ocr ../hand.jpeg 
这王-个不焙的决注
请坚持做下去,别放奔!

可以看到新系统虽然在文章开始的例子表现不是很好,但有时还是很精准的。

多语言测试

介绍 Vision 的时候提到中文只能搭配着英文使用,不能和其他语言套用,那么套用了会如何呢?

请添加图片描述

上图中是中文、英语、日语的“你好”,如果是在 macOS 12,无论是将识别语言设置成中文、日语或者不设置,都无法将日语识别成日语假名,而是将其识别成数字和英文字母或汉字。比如设置为jajpn

$ ./ocr ../5.png 
11$7
Hello
Zh-sla

但是在 iPadOS 16 上,如果设置为jajpn,那么三种语言都可以识别到(因为日语中也有汉字,所以这样其实不太对,但是应付可以):
请添加图片描述

但是如果设置为zh_Hans,那么日语部分根本不显示:
请添加图片描述

你可以用俄语ru也做一做测试,可以感觉到中文是被单独拎出来做的,不光不能搭配其他语言,其他语言也不能搭配中文。

倾斜测试

我很好奇文本倾斜还能识别出来吗?因为很多 CV 都是要找一个固定对象的,比如识别猫先定位猫胡子(水平的线)。那么 Vision 面对旋转过的文本还能识别出来吗?如果识别不出来,临界值大概是什么角度呢?

用下面这个图进行测试:

请添加图片描述

测试结果发现在旋转 25 到 30 度的时候,开始出现识别错误。当到达 45 度的时候基本上就不可用了。

这整个项目和后续更新我都放在 https://github.com/ZhongUncle/Swift-Vision-OCR.git,希望能帮到有需要的人~

相关文章:

Swift如何使用Vision来识别获取图片中的文字(OCR),通过SwiftUI视图和终端命令行,以及一系列注意事项

在过去的一年里,我发现苹果系统中的“文字搜图片”功能非常好用,这个功能不光 iPhone/iPad,Mac 也有,找一些图片真的很好用。但是遇到了一个问题:这个功能需要一段时间才能找到新的图片,而且没法手动刷新&a…...

c++ 学习 之 常函数 和 常对象

前言 常函数 成员函数后加 const 我们可以称这个函数为 常函数 常函数内不可以修改成员属性 成员属性声明时加关键字 mutable 后,在常函数中依然可以修改 常对象 常对象 声明对象前加 const 称该对象为常对象 常对象只能调用常函数 正文 常函数 class Person…...

LLM - 批量加载 dataset 并合并

目录 一.引言 二.Dataset 生成 1.数据样式 2.批量加载 ◆ 主函数调用 ◆ 基础变量定义 ◆ 多数据集加载 3.数据集合并 ◆ Concat ◆ interleave ◆ stopping_strategy ◆ interleave_probs 三.总结 一.引言 LLM 模型基于 transformer 进行训练,需要先…...

Debian 初始化命令备忘

本文地址:blog.lucien.ink/archives/541 以 Debian 11 为例,主要用于备忘。 deb https://mirrors.tuna.tsinghua.edu.cn/debian/ bullseye main contrib non-free deb https://mirrors.tuna.tsinghua.edu.cn/debian/ bullseye-updates main contrib non…...

二维矩阵的DFS算法框架

二维矩阵的DFS算法框架 关于岛屿的相似题目: 岛屿数量 – 二维矩阵的dfs算法封闭岛屿数量 – 二维矩阵的dfs算法统计封闭岛屿的数目统计子岛屿不同岛屿的数量 # 二叉树遍历框架 def traverse(root):if not root:return # 前序遍历traverse(root.left)# 中序遍历t…...

pytest实现日志按用例输出到指定文件中

场景 执行自动化用例时,希望日志按用例生成一个文件,并且按用例所在文件生成目录,用例失败时便于查看日志记录 实现方式 pytest.ini文件 在pytest.ini配置文件中设置配置项(定义日志输出级别和格式) log_clitrue l…...

程序员面试逻辑题

红白帽子推理 答案: 这个题有点像数学归纳法,就是假设有 A A A和 B B B两个人是黑色的帽子,这样的话第一次开灯, A A A看到 B B B是黑色的,其他人都是白色的,那么 A A A会觉得 B B B是那个黑色的&#xff0…...

自动创建设备节点udev机制实现

自动创建设备节点udev机制实现过程: 1.当插入设备,内核会向udev发送一个事件,其中包含着设备的信息。 2.udev会根据收到的设备信息匹配相应的规则文件。 3.udev会根据规则文件中的配置,创建一个唯一的设备节点文件。通常存储在/d…...

目标检测YOLO实战应用案例100讲-基于小样本学习和空间约束的濒危动物目标检测

目录 前言 相关技术介绍 2.1 卷积神经网络 2.1.1 基本结构 2.1.2 网络训练...

苹果数据恢复软件:Omni Recover Mac

Omni Recover是一款十分实用的Mac数据恢复软件,为用户提供了简单、安全、快速和高效的数据恢复服务。如果您遇到了Mac或iOS设备中的数据丢失和误删情况,不要着急,不妨尝试一下Omni Recover,相信它一定会给您带来惊喜。 首先&…...

树回归CART

之前线性回归创建的模型需要拟合所有的样本点,但数据特征众多,关系复杂时,构建全局模型就很困难。之前构建决策树使用的算法是ID3。 ID3 的做法是每次选取当前最佳的特征来分割数据,并按照该特征的所有可能取值来切分。也就是说&…...

zemax色差与消色差

色差,颜色像差 轴向色差:不同波长的光束通过透镜后焦点位于沿轴的不同位置 垂轴色差:每个波长成像的放大率不同 单透镜为例: 输入需要设置为多波长 观察光线光扇图: 不同波长的光之间差异较大(不同颜色…...

成绩定级脚本(Python)

成绩评定脚本 写一个成绩评定的python脚本,实现用户输入成绩,由脚本来为成绩评级: #成绩评定脚本.pyscoreinput("please input your score:") if int(score)> 90:print("A") elif int(score)> 80:print("B&…...

骨传导耳机的危害有哪些?会损害听力吗?

如果正常的使用,骨传导耳机是没有危害的,由于骨传导耳机独特的传声方式,所以并不会对人体造成损伤,还可以在一定程度上保护听力。 如果想更具体知道骨传导耳机有什么危害,就要先了解什么是骨传导耳机,骨传…...

Redis模块二:缓存分类 + Redis模块三:常见缓存(应用)

缓存大致可以分为两大类:1)本地缓存 2)分布式缓存 目录 本地缓存 分布式缓存 常见缓存的使用 本地缓存:Spring Cache 分布式缓存:Redis 本地缓存 本地缓存也叫单机缓存,也就是说可以应⽤在单机环…...

Revit SDK 内容摘要: 8.0 -8.1

前提 不包含已单独写博客部分。 Revit SDK Samples 8.0 AnalyticalViewer 分析模型,VB,略。 namespace Autodesk.Revit.DB.Structure {public class AnalyticalModel : Element{public AnalyticalRigidLinksOption RigidLinksOption { get; set; }p…...

列表和字典练习

定义四个学生信息 在Python环境下,用列表定义: >>> stu1[xiaoming,True,21,79.9] >>> stu1[lihong,False,22,69.9] >>> stu1[zhangqiang,True,20,89.9] >>> stu1[EMT,True,23,99.9]如图,定义了四个列表…...

iwebsec靶场 文件包含漏洞通关笔记2-文件包含绕过(截断法)

目录 前言 1.%00截断 2.文件字符长度截断法(又名超长文件截断) 方法1(路径截断法) 方法2(点号截断法) 第02关 文件包含绕过 1.打开靶场 2.源码分析 3.00文件截断原理 4.00截断的条件 5.文件包含00截断绕过 …...

【基于Cocos Creator实现的赛车游戏】9.实现汽车节点的控制逻辑

转载知识星球 | 深度连接铁杆粉丝,运营高品质社群,知识变现的工具 项目地址:赛车小游戏-基于Cocos Creator 3.5版本实现: 课程的源码,基于Cocos Creator 3.5版本实现 在上一节的课程中,您已经实现了通过触控给刚体施…...

蓝蓝设计为教育行业提供软件UI交互设计服务

在教育行业,软件的用户体验设计对于提供优质教育体验至关重要。教育行业软件用户体验设计需要考虑到学生和教师的需求,以及教育环境的特殊性。为了确保设计的成功,选择一家专业的设计公司是至关重要的,而北京蓝蓝设计公司就是您的…...

Java从入门到精通-类和对象(二)

0. 类和对象 3. 类的构造方法 构造方法是一种特殊的方法,用于创建和初始化对象。构造方法的名称必须与类名相同,它没有返回值,并且在创建对象时自动调用。构造方法的主要作用是确保对象在创建时具有合适的初始状态。 以下是构造方法的基本概…...

Python解析MDX词典数据并保存到Excel

原始数据和处理结果: https://gitcode.net/as604049322/blog_data/-/tree/master/mdx 下载help.mdx词典后,我们无法直接查看,我们可以使用readmdict库来完成对mdx文件的读取。 安装库: pip install readmdict对于Windows平台还…...

线性代数的本质(四)

文章目录 行列式二阶行列式 n n n 阶行列式行列式的性质克拉默法则行列式的几何理解 行列式 二阶行列式 行列式引自对线性方程组的求解。考虑两个方程的二元线性方程组 { a 11 x 1 a 12 x 2 b 1 a 21 x 1 a 22 x 2 b 2 \begin{cases} a_{11}x_1a_{12}x_2b_1 \\ a_{21}x_…...

FreeMarker详细介绍

FreeMarker详细介绍 FreeMarker FreeMarker概述 FreeMarker概念 FreeMarker 是一款 模板引擎: 即一种基于模板和要改变的数据, 并用来生成输出文本(HTML网页,电子邮件,配置文件,源代码等)的通用工具。 是一个Java类库…...

房地产小程序 | 小程序赋能,房地产业务数字化升级

随着科技的不断发展,房地产行业正逐渐向数字化转型。在这个过程中,房地产小程序成为了一种重要的工具,可以帮助房地产企业提供更好的购房体验、增加销售额,并实现管理的便捷化。 优点 便捷购房体验:房地产小程序为用户…...

Databend 开源周报第 110 期

Databend 是一款现代云数仓。专为弹性和高效设计,为您的大规模分析需求保驾护航。自由且开源。即刻体验云服务:https://app.databend.cn 。 Whats On In Databend 探索 Databend 本周新进展,遇到更贴近你心意的 Databend 。 使用 BendSQL 管…...

开源大模型ChatGLM2-6B 1. 租一台GPU服务器测试下

0. 环境 租用了1台GPU服务器,系统 ubuntu20,GeForce RTX 3090 24G。过程略。本人测试了ai-galaxy的,今天发现网友也有推荐autodl的。 (GPU服务器已经关闭,因此这些信息已经失效) SSH地址:* 端…...

SQL10 用where过滤空值练习

描述 题目:现在运营想要对用户的年龄分布开展分析,在分析时想要剔除没有获取到年龄的用户,请你取出所有年龄值不为空的用户的设备ID,性别,年龄,学校的信息。 示例:user_profile iddevice_idge…...

JVM--Hotspot Architecture 详解

一、Java Virtual Machine (JVM)概述 Java Virtual Machine 虚拟机 (JVM) 是一种抽象的计算机。JVM本身也是一个程序,但是对于编写在其中执行的程序来说,它看起来像一台机器。对于特定的操作系统&#xff…...

ThreadLocal功能实现

模拟ThreadLocal功能实现 当前线程任意方法内操作连接对象 一个栈对应一个线程 , 一个方法调用另一个方法都是在一个线程内 , 只有执行了线程的start方法才会创建一个线程 定义一个Map集合 , key是当前线程(Thread.currentThread) , value是要绑定的数据(Connection对象) 以…...

杭州做网站哪里好/武汉seo和网络推广

•参考资料 [1]:挑战程序设计竞赛(第二版) [2]:http://www.hankcs.com/program/m-n-recursive-division.html(注意看评论) •自学笔记 •对参考资料[2]的理解 定义dp[ i ][ j ] : 将 j 个物品划分成 i 组的总方案数 •1.对“我们定义 n 的 m 划分具体为一…...

怎么做一元购物网站/真正免费建站

上篇 SpringBootMongoDB实现一物流订单系统(上) 本文收录在公众号:bigsai第三步 订单更新(追加订单)创建完订单之后,无数配送公司和人员便开始配送物流,而我们在查询的时候,物流状态信息也能够时不时的刷新,具体物流信…...

建设英文商城网站/网络营销的含义的理解

九章算法官网-原文网址 http://www.jiuzhang.com/problem/55/ 题目 给一个字符串和一个旋转的偏移量offset,将字符串循环右移offset位。 如:"abcdefg" 循环右移 4位之后变为了:"defgabc" 要求做到O(1)的额外空间耗费…...

ui界面设计师/网站seo优化的目的

网络工程师成长日记371-卡夫食品中国有限公司项目回忆录 这是我的第371篇原创文章,记录网络工程师行业的点点滴滴,结交IT行业有缘之人 今天,我与老大一起去中大国际,为卡夫食品中国有限公司做项目尽管是项目不大,但第一…...

泰安网站的建设/海口百度seo公司

1. 前言 说起 Github,大家可能都觉得那只是程序员的聚集地。今天就要告诉大家,Github 不仅仅是 Coder 们的专属,它同时也是一个巨大的资源宝库! 由于我也算是个半吊子的程序员了,所以用 Github 也算久的了&#xff0…...

网站开发学哪种语言/今日最新消息新闻报道

用CSS的expression判断表达式设置input样式,简单,轻量级。缺点在于expression判断表达式FireFox是不支持的。致命的是只能区分出一个(例如例子中就只能区分出text文本框),不要试图设置多个…代码:复制代码代码如下:www.52CSS.comi…...