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,希望能帮到有需要的人~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/76381.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

STM32-HAL库07-软件SPI驱动0.96寸OLED

STM32-HAL库07-软件SPI驱动0.96寸OLED 一、所用材料: STM32VGT6自制控制板 STM32CUBEMX(HAL库软件) MDK5 二、所学内容: 通过HAL库配置四个GPIO输出口,对其进行软件模拟SPI发送规则,进而驱动OLED进行数…

火山引擎边缘云助力智能科技赋予生活更多新意

当下,先进的科学技术使得我们的日常生活变得快捷、舒适。大到上百层智能大厦、高端公共场所、社会智能基础设施,小到智能家居监控、指纹密码锁等,在这个充满想象力的时代,科技以更加智能化的方式改变和守护我们的生活。 引入智能…

IO和进程day05(进程与线程)

今日任务 1.代码 #include <stdio.h> #include <string.h> #include <stdlib.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> #include <sys/wait.h> #include <pthread.h>…

基于springboot2+mybatis-plus+jsp增删改查

概述 编写简单增删改查&#xff0c;理解之后可以自己试着扩展&#xff0c;相信你也可以&#xff0c;加油&#xff0c;我自己懂了的用注释记在下面方便理解 详细 一、需求&#xff08;要做什么&#xff09; 基于现今最流行的技术实现增删改查demo&#xff0c; 便于初学者上手…

2020年12月 C/C++(一级)真题解析#中国电子学会#全国青少年软件编程等级考试

C/C编程&#xff08;1~8级&#xff09;全部真题・点这里 第1题&#xff1a;字符三角形 描述 给定一个字符&#xff0c;用它构造一个底边长5个字符&#xff0c;高3个字符的等腰字符三角形。 输入 输入只有一行&#xff0c; 包含一个字符。 输出 该字符构成的等腰三角形&#xff…

nuxt3项目使用pdfjs-dist预览pdf

使用的包的源代码是 pdfjs - npm 但是我们实际上项目中使用的是pdfjs打包后的dist文件&#xff0c;也就是pdfjs-dist - npm 所以我们需要使用这个命令 npm i pdfjs-dist 我们可以克隆pdfjs这个包来看源代码&#xff0c;里面有使用的例子&#xff0c;也可以根据源代码自己打…

ClickHouse进阶(十三):Clickhouse数据字典-3-文件数据源及Mysql数据源

进入正文前&#xff0c;感谢宝子们订阅专题、点赞、评论、收藏&#xff01;关注IT贫道&#xff0c;获取高质量博客内容&#xff01; &#x1f3e1;个人主页&#xff1a;含各种IT体系技术,IT贫道_大数据OLAP体系技术栈,Apache Doris,Kerberos安全认证-CSDN博客 &#x1f4cc;订阅…

鸿蒙应用程序入口UIAbility详解

一、UIAbility概述 UIAbility是一种包含用户界面的应用组件&#xff0c;主要用于和用户进行交互。UIAbility也是系统调度的单元&#xff0c;为应用提供窗口在其中绘制界面。每一个UIAbility实例&#xff0c;都对应于一个最近任务列表中的任务。一个应用可以有一个UIAbility&am…

LVS + Keepalived群集

文章目录 1. Keepalived工具概述1.1 什么是Keepalived1.2 工作原理1.3 Keepailved实现原理1.4 Keepalived体系主要模块及其作用1.5 keepalived的抢占与非抢占模式 2. 脑裂现象 &#xff08;拓展&#xff09;2.1 什么是脑裂2.2 脑裂的产生原因2.3 如何解决脑裂2.4 如何预防脑裂 …

介绍PHP

PHP是一种流行的服务器端编程语言&#xff0c;用于开发Web应用程序。它是一种开源的编程语言&#xff0c;具有易学易用的语法和强大的功能。PHP支持在服务器上运行的动态网页和Web应用程序的快速开发。 PHP可以与HTML标记语言结合使用&#xff0c;从而能够生成动态的Web页面&a…

关于前端就业前景的一点看法

一、前言 最近&#xff0c;很多人在知乎上鼓吹前端未来会没掉的概念。在此我想说这个说法并不真实&#xff0c;而是一种极端的观点。 事实上&#xff0c;前端开发在当今的互联网行业中扮演着至关重要的角色&#xff0c;它是构建 Web 应用程序所必需的一部分&#xff0c;能够实现…

小白学go基础04-命名惯例对标识符进行命名

计算机科学中只有两件难事&#xff1a;缓存失效和命名。 命名是编程语言的要求&#xff0c;但是好的命名却是为了提高程序的可读性和可维护性。好的命名是什么样子的呢&#xff1f;Go语言的贡献者和布道师Dave Cheney给出了一个说法&#xff1a;“一个好笑话&#xff0c;如果你…

TSINGSEE青犀AI视频分析/边缘计算/AI算法·人脸识别功能——多场景高效运用

旭帆科技AI智能分析网关可提供海量算法供应&#xff0c;涵盖目标监测、分析、抓拍、动作分析、AI识别等&#xff0c;可应用于各行各业的视觉场景中。同时针对小众化场景可快速定制AI算法&#xff0c;主动适配大厂近百款芯片&#xff0c;打通云/边/端灵活部署&#xff0c;算法一…

前端中的事件委托

前端小知识 事 件 委 托 作者&#xff1a;李俊才 &#xff08;jcLee95&#xff09;&#xff1a;https://blog.csdn.net/qq_28550263 邮箱 &#xff1a;291148484163.com 本文地址&#xff1a;https://blog.csdn.net/qq_28550263/article/details/132819265 【介绍】&#xff1…

VM安装RedHat7虚机ens33网络不显示IP问题解决

1、今天在VMware中安装RedHat7.4虚拟机&#xff0c;网络连接使用的是 NAT 连接方式&#xff0c;刚开始安装成功之后输入ifconfig 还能看到ens33自动分配的IP地址&#xff0c;但是当虚机关机重启后&#xff0c;再查看IP发现原来的ens33网络已经没有了&#xff0c;只变成了这两个…

Mybatis -- 读取 DATE 类型字段时可能遇到的问题(夏令时问题)

在使用 MYBATIS 读取数据库字段的时候&#xff0c;我们一般需要为查询字段指定数据类型。特别是当我们使用 mybatis generator 去生成对应的接口代码时&#xff0c;会自动按照数据库字段类型生成响应映射规则的代码。   如下&#xff0c;左侧是 date 类型生成的字段映射规则&…

MySQL数据库——存储引擎(2)-存储引擎特点(InnoDB、MyISAM、Memory)、存储引擎选择

目录 存储引擎特点 InnoDB 介绍 特点 文件 逻辑存储结构 MyISAM 介绍 特点 文件 Memory 介绍 特点 文件 区别及特点 存储引擎选择 存储引擎特点 上面我们介绍了什么是存储引擎&#xff0c;以及如何在建表时如何指定存储引擎&#xff0c;接下来我们就来介绍比较…

JS中 bind()的用法,call(),apply(),bind()异同点及使用,如何手写一个bind()

✨什么是bind() bind()的MDN地址 bind() 方法创建一个新函数&#xff0c;当调用该新函数时&#xff0c;它会调用原始函数并将其 this 关键字设置为给定的值&#xff0c;同时&#xff0c;还可以传入一系列指定的参数&#xff0c;这些参数会插入到调用新函数时传入的参数的前面。…

Map集合

Map中常见的API Map<键值对类型&#xff0c; 键值对对象类型> put&#xff08;K key , V value&#xff09;【可以有返回值】 添加/覆盖元素 在添加数据的时候&#xff0c;如果键不存在&#xff0c;那么直接将键对对象添加到map集合当中 在添加数据的时候&#xff0c…

云原生Kubernetes:pod基础

目录 一、理论 1.pod 2.pod容器分类 3.镜像拉取策略&#xff08;image PullPolicy&#xff09; 二、实验 1.Pod容器的分类 2.镜像拉取策略 三、问题 1.apiVersion 报错 2.pod v1版本资源未注册 3.取行显示指定pod信息 四、总结 一、理论 1.pod (1) 概念 Pod是ku…