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.rhkb.cn/news/133342.html

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

相关文章

从一到无穷大 #15 Gorilla,论黄金26H与时序数据库缓存系统的可行性

本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。 本作品 (李兆龙 博文, 由 李兆龙 创作),由 李兆龙 确认,转载请注明版权。 引言 缓存系统的高效存在前提,在满足前提的情况下可以接受缺陷便没有理由不引入缓…

pdf添加水印

给pdf文件添加水印 引入依赖 <dependency><groupId>com.itextpdf</groupId><artifactId>itextpdf</artifactId><version>5.5.13.3</version></dependency>添加水印 package com.it2.pdfdemo02.util;import com.itextpdf.tex…

Qt应用程序连接达梦数据库-飞腾PC麒麟V10

目录 前言1 安装ODBC1.1 下载unixODBC源码1.2 编译安装1.4 测试 2 编译QODBC2.1 修改 qsqldriverbase.pri 文件2.2 修改 odbc.pro 文件2.3 编译并安装QODBC 3 Qt应用程序连接达梦数据库测试4 优化ODBC配置&#xff0c;方便程序部署4.1 修改pro文件&#xff0c;增加DESTDIR 变量…

高可用Kuberbetes部署Prometheus + Grafana

概述 阅读官方文档部署部署Prometheus Grafana GitHub - prometheus-operator/kube-prometheus at release-0.10 环境 步骤 下周官方github仓库 git clone https://github.com/prometheus-operator/kube-prometheus.git git checkout release-0.10 进入工作目录 cd kube…

聚观早报|华为Mate 60 Pro支持面容支付;特斯拉重回底特律车展

【聚观365】9月8日消息 华为Mate 60 Pro已支持面容支付 特斯拉将重回底特律车展 iPhone在美国有1.67亿用户 韩国半导体8月份出口85.6亿美元 比亚迪元PLUS冠军版将于9月15日上市 华为Mate 60 Pro已支持面容支付 毫无预热的华为Mate 60 Pro突然在华为商城首批开售&#xf…

老站长带你全面认识基站和天线

认识基站 作为数量最多的移动通信设备 基站几乎是随处可见 其实 基站也分为很多种 基站的天线&#xff0c;也分为很多种&#xff0c;真正都能区分清楚的人其实不多。 什么是基站 Base Station 一般特指“公用移动通信基站” 大家都知道&#xff0c;基站就是给手机提供信…

【Vue】vue2使用pdfjs预览pdf文件,在线预览方式一,pdfjs文件包打开新窗口预览pdf文件

系列文章目录 【Vue】vue2预览显示quill富文本内容&#xff0c;vue-quill-editor回显页面&#xff0c;v-html回显富文本内容 【Vue】vue2项目使用swiper轮播图2023年8月21日实战保姆级教程 【Vue】vue2使用pdfjs预览pdf文件&#xff0c;在线预览方式一&#xff0c;pdfjs文件包…

Convai:让虚拟游戏角色更智能的对话AI人工智能平台

【产品介绍】​ 名称 Convai​ 具体描述​ Convai是一款专为虚拟世界而设计的对话人工智能平台&#xff0c;它可以让你为你的游戏或应用中的角色 赋予人类般的对话能力。Convai利用了最先进的生成式对话人工智能技术&#xff0c;让你的角色可以…

数学建模__动态规划

动态规划就是&#xff0c;将任务每一步均记录下来&#xff0c;以便将来重复使用时能够直接调用 问题描述&#xff1a;给定n个物品&#xff0c;每个物品的重量是Wi,价值是Vi&#xff0c;但是背包最多能装下capacity重量的物品&#xff0c;问我们如何选择才能利益最大化。 这里涉…

R语言分析糖尿病数据:多元线性模型、MANOVA、决策树、典型判别分析、HE图、Box's M检验可视化...

全文链接&#xff1a;https://tecdat.cn/?p33609 Reaven和Miller&#xff08;1979&#xff09;研究了145名非肥胖成年人的葡萄糖耐量和胰岛素血液化学指标之间的关系。他们使用斯坦福线性加速器中心的PRIM9系统将数据可视化为3D&#xff0c;并发现了一个奇特的图案&#xff0c…

SQlite操作后如何正确退出

在 C 语言中&#xff0c;使用 SQLite 库进行数据库操作后&#xff0c;可以通过以下步骤来正常退出和关闭 SQLite 连接&#xff1a; 关闭数据库连接&#xff1a;在完成数据库操作后&#xff0c;使用 sqlite3_close() 函数来关闭 SQLite 连接。该函数接受一个指向 sqlite3 数据库…

一文详解TCP/IP协议栈的心跳、丢包重传、连接超时机制实例

1、问题概述 虽然软件底层模块在网络恢复后能自动重连上服务器&#xff0c;但会议因为网络问题已经退出&#xff0c;需要重新加入会议。因为客户特殊的网络运行环境&#xff0c;会频繁出现网络抖动不稳定的情况&#xff0c;客户要求必须要实现60秒内网络恢复后能依然保持在会议…

Unity中Shader的屏幕抓取 GrabPass

文章目录 前言一、抓取1、抓取指令2、在使用抓取的屏幕前&#xff0c;需要像使用属性一样定义一下,_GrabTexture这个名字是Unity定义好的 前言 Unity中Shader的屏幕抓取 GrabPass 一、抓取 1、抓取指令 屏幕的抓取需要使用一个Pass GrabPass{} GrabPass{“NAME”} 2、在使用…

752. 打开转盘锁

链接&#xff1a; 752. 打开转盘锁 题解&#xff1a; class Solution { public:int openLock(vector<string>& deadends, string target) {std::unordered_set<std::string> table(deadends.begin(), deadends.end());if (table.find("0000") ! t…

基础算法---区间合并

直接上题目,不废话! 题目 给定 n 个区间 [l,r]&#xff0c;要求合并所有有交集的区间。 注意如果在端点处相交&#xff0c;也算有交集。 输出合并完成后的区间个数。 例如&#xff1a;[1,3] 和 [2,6] 可以合并为一个区间 [1,6]。 输入格式 第一行包含整数 n。 接下来 n 行&am…

python-xpath语法-爬取彼岸图4k高清动漫壁纸

安装 pip install lxml导入 from lxml import etreexpath使用路径表达式提取html文档中的元素或元素集&#xff0c;然后元素通过沿路径path或步steps来选取数据 XPath常用语法格式 表达式描述div选取div元素的所有子元素/div选取根元素divul//li选取ul元素下的所有li子元素…

二蛋赠书二期:《Python机器学习项目实战》

文章目录 前言活动规则参与方式本期赠书《Python机器学习项目实战》作者介绍内容简介读者对象获奖名单 结语 前言 大家好&#xff01;我是二蛋&#xff0c;一个热爱技术、乐于分享的工程师。在过去的几年里&#xff0c;我一直通过各种渠道与大家分享技术知识和经验。我深知&am…

2327. 知道秘密的人数;1722. 执行交换操作后的最小汉明距离;2537. 统计好子数组的数目

2327. 知道秘密的人数 核心思想&#xff1a;动态规划&#xff0c;每天的人可以分为三种&#xff0c;可分享秘密的人&#xff0c;不可分享秘密的人&#xff0c;忘记秘密的人。定义f[i]为第i天可分享秘密的人&#xff0c;那么第(idelay ,iforget)天&#xff0c;会增加f[i]个可分…

C++算法进阶系列之倍增算法 ST 表

1. 引言 前文使用倍增算法实现了快速求幂的运算&#xff0c;本文继续讲解ST表&#xff0c;ST表即倍增表&#xff0c;本质就是动态规划表&#xff0c;记忆化了不同子问题域中的结果&#xff0c;用于实时查询。只是动态规划过程和传统的稍有点不一样&#xff0c;采用了倍增思想。…

h5开发网站-页面内容不够高时,如何定位footer始终位于页面的最底部

一、问题描述&#xff1a; 在使用h5开发页面时&#xff0c;会遇到这个情况&#xff1a;当整个页面高度不足以占满显示屏一屏&#xff0c;页脚不是在页面最底部&#xff0c;影响用户视觉。想让页脚始终在页面最底部&#xff0c;我们可能会想到用&#xff1a; 1.min-height来控…