版本记录
| 版本号 | 时间 |
|---|---|
| V1.0 | 2020.10.15 星期四 |
前言
iOS 11+和macOS 10.13+新出了Vision框架,提供了人脸识别、物体检测、物体跟踪等技术,它是基于Core ML的。可以说是人工智能的一部分,接下来几篇我们就详细的解析一下Vision框架。感兴趣的看下面几篇文章。
1. Vision框架详细解析(一) —— 基本概览(一)
2. Vision框架详细解析(二) —— 基于Vision的人脸识别(一)
3. Vision框架详细解析(三) —— 基于Vision的人脸识别(二)
4. Vision框架详细解析(四) —— 在iOS中使用Vision和Metal进行照片堆叠(一)
5. Vision框架详细解析(五) —— 在iOS中使用Vision和Metal进行照片堆叠(二)
6. Vision框架详细解析(六) —— 基于Vision的显著性分析(一)
7. Vision框架详细解析(七) —— 基于Vision的显著性分析(二)
开始
首先看下主要内容:
在此
Vision Framework教程中,您将学习如何使用iPhone的相机扫描QR码并自动在Safari中打开编码的URL。内容来自翻译。
下面看下写作环境:
Swift 5, iOS 14, Xcode 12
下面就是正文啦。
条码无处不在:产品,广告,电影票上。 在本教程中,您将学习如何使用Vision Framework在iPhone上扫描条形码。 您将使用Vision Framework API,例如VNDetectBarcodesRequest,VNBarcodeObservation和VNBarcodeSymbology,并学习如何使用AVCaptureSession进行实时图像采集。
那不是全部! 您还将熟悉:
- 将相机用作输入设备。
- 生成和评估图像置信度分数
(confidence score)。 - 在
SFSafariViewController中打开网页。
打开入门项目。看一下ViewController.swift。 您将在代码中找到一些辅助方法。
注意:要遵循本教程,您需要运行
iOS 11或更高版本的实体iPhone 5s以上的手机。 要在物理设备上运行该应用程序,请确保在Xcode项目设置的Signing and Capabilities部分中设置您的团队。 如果需要设置设备和Xcode项目的帮助,请查看app store tutorial。
在开始扫描条形码之前,最好先获得使用相机的许可。
Getting Permission to Use Camera
为了保护用户隐私,Apple要求开发人员在访问其相机之前必须征得用户的许可。 有两个步骤可准备您的应用以请求正确的许可:
- 1) 通过在
Info.plist中添加键和值来说明应用程序使用相机的原因和方式。 - 2) 使用
AVCaptureDevice.requestAccess(for:completionHandler :)提示用户输入您的解释并获取用户输入以获得权限。
入门项目在Info.plist中包含键值对。 您可以在Privacy – Camera Usage Description键下找到它。
要提示用户使用相机的权限,请打开ViewController.swift。 接下来,在checkPermissions()中找到// TODO: Checking permissions。 在方法内添加以下代码:
switch AVCaptureDevice.authorizationStatus(for: .video) {
// 1
case .notDetermined:
AVCaptureDevice.requestAccess(for: .video) { [self] granted in
if !granted {
showPermissionsAlert()
}
}
// 2
case .denied, .restricted:
showPermissionsAlert()
// 3
default:
return
}
在上面的代码中,您要求iOS提供您应用的当前相机授权状态。
- 1) 如果状态尚未确定,则意味着用户尚未进行权限选择,您可以调用
AVCaptureDevice.requestAccess(for:completionHandler :)。 它向用户显示一个对话框,要求允许使用相机。 如果用户拒绝您的请求,则您会在iPhone设置中再次显示一条弹出消息,询问权限。 - 2) 如果用户先前提供了对摄像头的访问权限,或者拒绝了应用程序对摄像头的访问权限,则会显示一条
alert,要求更新设置以允许访问。 - 3) 否则,该用户已经为您的应用授予使用相机的权限,因此您无需执行任何操作。
构建并运行,您将看到以下内容:

拥有摄像头许可后,您可以继续开始采集会话(capturing session)。
Starting an AVCaptureSession
现在,您有权访问设备上的相机。 但是,当您关闭手机上的alert时,什么也不会发生! 您现在可以按照以下步骤开始使用iPhone相机来解决此问题:
- 1) 设置捕获会话
(capturing session)的质量。 - 2) 定义摄像机的输入。
- 3) 为相机定一个输出。
- 4)
Run一下capturing session。
1. Setting Capture Session Quality
在Xcode中,导航到ViewController.swift。 找到setupCameraLiveView()并在// TODO: Setup captureSession之后添加以下代码:
captureSession.sessionPreset = .hd1280x720
captureSession是AVCaptureSession的一个实例。 借助AVCaptureSession,您可以管理采集活动并协调数据从输入设备流向采集输出的方式。
在上面的代码中,将采集会话质量设置为HD。
接下来,您将定义要使用应用程序的众多iPhone相机中的哪一个,并将选择的内容传递给captureSession。
2. Defining a Camera for Input
继续在setupCameraLiveView()中,在// TODO: Add input之后添加此代码:
// 1
let videoDevice = AVCaptureDevice
.default(.builtInWideAngleCamera, for: .video, position: .back)
// 2
guard
let device = videoDevice,
let videoDeviceInput = try? AVCaptureDeviceInput(device: device),
captureSession.canAddInput(videoDeviceInput)
else {
// 3
showAlert(
withTitle: "Cannot Find Camera",
message: "There seems to be a problem with the camera on your device.")
return
}
// 4
captureSession.addInput(videoDeviceInput)
在这里:
- 1) 查找位于iPhone背面的默认广角相机。
- 2) 确保您的应用程序可以将相机用作
capture session的输入设备。 - 3) 如果相机有问题,请向用户显示错误消息。
- 4) 否则,将后置广角相机设置为
capture session的输入设备。
准备好capture session后,您现在就可以设置相机输出了。
3. Making an Output
现在您已经从摄像机接收到视频了,您需要一个放置它的地方。 在// TODO: Add output之后,继续您上次中断的地方,添加:
let captureOutput = AVCaptureVideoDataOutput()
// TODO: Set video sample rate
captureSession.addOutput(captureOutput)
在这里,您将capture session的输出设置为AVCaptureVideoDataOutput的实例。 AVCaptureVideoDataOutput是一个capture输出,用于记录视频并提供对视频帧的访问以进行处理。 您稍后将对此添加更多。
最后,是时候运行capture session了。
4. Running the Capture Session
找到// TODO: Run session注释,并在其后直接添加以下代码:
captureSession.startRunning()
这将启动相机会话,并使您能够继续使用Vision框架。
但首先! 许多人忘记了要再次停止相机会话。
为此,请在viewWillDisappear(_ :)中找到// TODO:Stop Session注释,并在其后添加以下内容:
captureSession.stopRunning()
如果您的视图消失了,这将停止capture session,从而释放一些宝贵的内存。
在设备上构建并运行项目。 这样,后置摄像头就会显示在iPhone屏幕上! 如果您的手机正对着计算机,则外观类似于:

有了这些,现在是时候进入Vision框架了。
Vision Framework
苹果公司创建了Vision Framework,使开发人员可以应用计算机视觉算法对输入的图像和视频执行各种任务。 例如,您可以将Vision用于:
- 人脸和地标检测
- 文字检测
- 图像配准
- 通用特征跟踪
Vision还允许您将自定义Core ML模型用于图像分类或对象检测等任务。
1. Vision and the Camera
Vision Framework在静止图像上运行。 当然,当您在iPhone上使用相机时,图像会平滑移动,就像您期望从视频中看到的那样。 但是,视频是由一系列静止图像接连播放组成的,几乎就像一本翻页书一样flip book。

当将相机与Vision Framework配合使用时,Vision会将运动视频分割成其组成图像,并以称为采样率(sample rate)的某个频率处理这些图像之一。
在本教程中,您将使用Vision Framework查找图像中的条形码。Vision框架可以读取17种不同的条形码格式,包括UPC和QR码。
在接下来的部分中,您将指示您的应用查找QR码并阅读其内容。是时候开始了!
Using the Vision Framework
要在您的应用中实施Vision Framework,您需要执行以下三个基本步骤:
- 1) Request - 请求:当您想使用该框架检测某些内容时,可以使用
VNRequest的子类来定义请求。 - 2) Handler - 处理:使用
VNImageRequestHandler的子类处理该请求并为任何检测执行图像分析。 - 3) Observation - 观察:您可以使用
VNObservation的子类分析已处理请求的结果。
是时候创建您的第一个Vision请求了。
1. Creating a Vision Request
Vision提供了VNDetectBarcodesRequest来检测图像中的条形码。您现在就实现它。
在ViewController.swift中,在文件顶部找到// TODO: Make VNDetectBarcodesRequest variable,并在其后添加以下代码:
lazy var detectBarcodeRequest = VNDetectBarcodesRequest { request, error in
guard error == nil else {
self.showAlert(
withTitle: "Barcode error",
message: error?.localizedDescription ?? "error")
return
}
self.processClassification(request)
}
在此代码中,您设置了一个VNDetectBarcodesRequest,它将在调用时检测条形码。 当方法认为找到条形码时,会将条形码传递给processClassification(_ :)。 您稍后将定义processClassification(_ :)。
但是首先,您需要重新查看视频和采样率。
2. Vision Handler
请记住,视频是图像的集合,并且Vision Framework会以某种频率处理这些图像之一。 要相应地设置视频,请找到setupCameraLiveView()并找到您之前离开的TODO::// TODO: Set video sample rate。 然后,在注释之后和调用addOutput(_ :)之前添加以下代码:
captureOutput.videoSettings =
[kCVPixelBufferPixelFormatTypeKey as String: Int(kCVPixelFormatType_32BGRA)]
captureOutput.setSampleBufferDelegate(
self,
queue: DispatchQueue.global(qos: DispatchQoS.QoSClass.default))
在此代码中,您将视频流的像素格式设置为32-bit BGRA。 然后,将self设置为sample buffer的代理。 当缓冲区中有新图像可用时,Vision会从AVCaptureVideoDataOutputSampleBufferDelegate调用适当的代理方法。
由于您已通过self作为代理,因此必须使ViewController遵循AVCaptureVideoDataOutputSampleBufferDelegate代理。 您的类已经遵循了,并定义了一个回调方法:captureOutput(_:didOutput:from :)。 找到此方法并在// TODO: Live Vision之后插入以下内容:
// 1
guard let pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer) else {
return
}
// 2
let imageRequestHandler = VNImageRequestHandler(
cvPixelBuffer: pixelBuffer,
orientation: .right)
// 3
do {
try imageRequestHandler.perform([detectBarcodeRequest])
} catch {
print(error)
}
在这里:
- 1) 从
sample buffer中获取图像,例如从flip book中获取页面。 - 2) 使用该图像创建一个新的
VNImageRequestHandler。 - 3) 使用上面的
handler执行detectBarcodeRequest。

3. Vision Observation
回想一下Vision Request部分。 在那里,您构建了detectBarcodeRequest,如果认为发现条形码,则会调用processClassification(_ :)。 最后一步,您将填写processClassification(_ :)来分析已处理请求的结果。
在processClassification(_ :)中,找到// TODO: Main logic,并在其下面添加以下代码:
// 1
guard let barcodes = request.results else { return }
DispatchQueue.main.async { [self] in
if captureSession.isRunning {
view.layer.sublayers?.removeSubrange(1...)
// 2
for barcode in barcodes {
guard
// TODO: Check for QR Code symbology and confidence score
let potentialQRCode = barcode as? VNBarcodeObservation
else { return }
// 3
showAlert(
withTitle: potentialQRCode.symbology.rawValue,
// TODO: Check the confidence score
message: potentialQRCode.payloadStringValue ?? "" )
}
}
}
在此代码中,您:
- 1) 从请求中获取潜在条形码的列表。
- 2) 遍历可能的条形码以单独分析每个条形码。
- 3) 如果结果之一恰好是条形码,则显示带有条形码类型和条形码中编码的字符串的
alert。
构建并再次运行。 这次,将相机对准条形码。
Booooom,您扫描了!

到现在为止还挺好。 但是,如果您可以通过某种方式确切地知道所指向的对象实际上是条形码,该怎么办? 接下来的更多内容...
Adding a Confidence Score
到目前为止,您已经与AVCaptureSession和Vision Framework进行了广泛的合作。 但是,您还可以采取更多措施来加强实现。 具体来说,您可以将Vision Observation限制为仅识别QR型条形码,并且可以确保视觉框架确定在图像中找到了QR码。

每当条形码观察者(barcode observer)分析已处理请求的结果时,它都会设置一个称为confidence的属性。 此属性告诉您结果的置信度,通常将其置为[0,1],其中1是最置信度。
在processClassification(_ :)内部,找到// TODO: Check for QR Code symbology and confidence score并更换guard:
guard
let potentialQRCode = barcode as? VNBarcodeObservation
else { return }
为
guard
// TODO: Check for QR Code symbology and confidence score
let potentialQRCode = barcode as? VNBarcodeObservation,
potentialQRCode.confidence > 0.9
else { return }
在这里,您可以确保Vision至少有90%的置信度找到了条形码。
现在,使用相同的方法,找到// TODO: Check the confidence score。 消息密钥下面的值当前为potentialQRCode.payloadStringValue ?? ""。 更改为:
String(potentialQRCode.confidence)
现在,您将显示置信度分数,而不是在弹窗中显示条形码的有效载荷。 因为分数是数字,所以您将值合并为字符串,以便它可以显示在弹窗中。
构建并运行。 扫描示例QR码时,您会在弹出的弹窗中看到置信度得分。

做得很好! 如您所见,采样的QR码的置信度得分非常高,这意味着Vision可以肯定这实际上是QR码。
1. Using Barcode Symbology
当前,您的扫描会响应所有类型的条形码。 但是,如果您希望扫描仪仅响应QR码怎么办? 幸运的是,Vision为您提供了解决方案!
在上面的Vision Observation部分中,您使用VNBarcodeObservation的confidence来确定Vision是否能找到条形码。 同样,您可以使用VNBarcodeObservation的symbology属性来检查在Vision Request中找到的符号类型。
再次在processClassification(_ :)中找到// TODO: Check for QR Code symbology and confidence score。 然后在guard内部,在第一个let子句之后添加以下条件:
potentialQRCode.symbology == .QR,
您的guard现在应该如下所示:
guard
let potentialQRCode = barcode as? VNBarcodeObservation,
potentialQRCode.symbology == .QR,
potentialQRCode.confidence > 0.9
else { return }
有了这个新条件,您已经添加了一个检查以确保条形码的类型为.QR。 如果条形码不是QR码,则忽略该请求。
构建并运行。 扫描采样QR码,看看您的应用程序现在忽略了采样条形码。

最后,您将通过在Safari中打开一个编码的URL来完成这一漂亮的新功能。
Opening Encoded URLs in Safari
条形码扫描仪现已完成。 当然,简单地扫描和读取条形码只是成功的一半。
您的用户将希望对扫描结果进行处理。 毕竟,他们确实允许您使用相机!
QR码通常包含指向有趣网站的URL。 当用户扫描QR码时,他们想转到编码地址处的页面。
在以下各节中,您将致力于做到这一点。
1. Setting Up Safari
首先,您需要添加对在SFSafariViewController中打开链接的支持。
找到observationHandler(payload :),然后在// TODO: Open it in Safari之后添加以下内容:
// 1
guard
let payloadString = payload,
let url = URL(string: payloadString),
["http", "https"].contains(url.scheme?.lowercased())
else { return }
// 2
let config = SFSafariViewController.Configuration()
config.entersReaderIfAvailable = true
// 3
let safariVC = SFSafariViewController(url: url, configuration: config)
safariVC.delegate = self
present(safariVC, animated: true)
使用此代码,您:
- 1) 确保
QR码中的编码字符串是有效的URL。 - 2) 设置
SFSafariViewController的新实例。 - 3) 将
Safari打开到QR码中编码的URL。
接下来,您将在扫描有效的QR码后着手触发此功能。
2. Opening Safari
要使用此功能打开URL,如果发现一个QR码,则必须告诉processClassification(_ :)使用observationHandler(payload :)。
在processClassification(_ :)中,使用
showAlert(
withTitle: potentialQRCode.symbology.rawValue,
// TODO: Check the confidence score
message: String(potentialQRCode.confidence)
使用
observationHandler(payload: potentialQRCode.payloadStringValue)
在这里,您无需打开条形码阅读器遇到QR码时的弹窗,而只需打开Safari即可找到QR码中编码的URL。
构建并运行设备,然后将其指向示例QR码。

恭喜你! 您开发了一个功能齐全的应用程序,该应用程序使用iPhone的相机扫描和读取QR码,并在Safari中打开编码的URL!
在本教程中,您学习了如何:
- 使用
Vision Framework。 - 制作并执行
VNRequest。 - 如何使用
SFSafariViewController。 - 如何限制要扫描的条形码的种类。
该项目只是您使用Vision Framework所做的一切的开始。 如果您想了解更多信息,请查看 Face Detection Tutorial Using the Vision Framework for iOS。 对于更高级的挑战,请查看AR Face Tracking Tutorial for iOS: Getting Started。
您也可以访问Vision Framework,AVCaptureSession,VNDetectBarcodesRequest,VNBarcodeObservation和VNBarcodeSymbology 的开发人员文档 developer documentation。
后记
本篇主要讲述了基于Vision的QR扫描,感兴趣的给个赞或者关注~~~

