iOS MachineLearning 系列(2)—— 静态图像分析之矩形识别

简介: 本系列文章将完整的介绍iOS中Machine Learning相关技术的应用。本篇文章开始,我们将先介绍一些与Machine Learning相关的API的应用。使用这些API可以快速方便的实现很多如图像识别,分析等复杂功能,且不会增加应用安装包的体积。

iOS MachineLearning 系列(2)—— 静态图像分析之矩形识别

本系列文章将完整的介绍iOS中Machine Learning相关技术的应用。本篇文章开始,我们将先介绍一些与Machine Learning相关的API的应用。使用这些API可以快速方便的实现很多如图像识别,分析等复杂功能,且不会增加应用安装包的体积。

本篇将首先介绍如何分析出静态图片中的矩形区域。矩形区域的是被非常重要,其通常用来对要分析的图片进行预处理,例如通过矩形分析截取其中的二维码,条形码部分后再进行精准的识别。

1 - 矩形分析示例

与视觉相关的大部分AI能力都封装在Vision框架中,本文要介绍的是通过发起矩形分析请求来分析图片,得到分析结果后将分析出来的矩形区域绘制回原图像上。

首先定义一些属性:

// 要分析的图片资源
let image = UIImage(named: "image2")!
lazy var imageView = UIImageView(image: image)

// 绘制的矩形区域
var boxViews: [UIView] = []

// 图像分析请求句柄
lazy var imageRequestHandler = VNImageRequestHandler(cgImage: image.cgImage!,
                                                orientation: .up,
                                                options: [:])

// 图像分析请求实例
private lazy var rectangleDetectionRequest: VNDetectRectanglesRequest = {
    let rectDetectRequest = VNDetectRectanglesRequest { request, error in
        DispatchQueue.main.async {
            self.drawTask(request: request as! VNDetectRectanglesRequest)
        }
    }
    // 自定义一些配置项
    // 设置要分析的最大结果个数(矩形个数)
    rectDetectRequest.maximumObservations = 0
    // 设置最低接受的可信值
    rectDetectRequest.minimumConfidence = 0
    // 设置最小接受的纵横比
    rectDetectRequest.minimumAspectRatio = 0.1
    return rectDetectRequest
}()

其中VNDetectRectanglesRequest即是核心的图片分析请求类,VNImageRequestHandler是请求句柄,用来发起请求。后面我们会详细介绍。在开始请求分析之前,我们还需要定义个方法,用来进行矩形区域绘制:

private func drawTask(request: VNDetectRectanglesRequest) {
    // 将之前绘制的删除
    boxViews.forEach { v in
        v.removeFromSuperview()
    }
    // 遍历分析结果
    for result in request.results ?? [] {
        var box = result.boundingBox
        // 坐标系转换
        box.origin.y = 1 - box.origin.y - box.size.height
        let v = UIView()
        v.backgroundColor = .clear
        v.layer.borderColor = UIColor.black.cgColor
        v.layer.borderWidth = 1
        imageView.addSubview(v)
        let size = imageView.frame.size
        v.frame = CGRect(x: box.origin.x * size.width, y: box.origin.y * size.height, width: box.size.width * size.width, height: box.size.height * size.height)
    }
}

需要注意,Vision框架中的坐标系与CoreGraphics框架中的坐标系是一致的,其以左下角点为(0, 0)点,在UIKit框架中则是以左上角点为(0,0)点,记得进行坐标系的转换。

最后,使用下面的代码来发起请求,静态图像的分析将会是一个耗时的过程,因此建议在非主线程中进行:

DispatchQueue.global(qos: .userInitiated).async {
    do {
        // 发起分析请求
        try self.imageRequestHandler.perform([self.rectangleDetectionRequest])
    } catch let error as NSError {
        print("Failed to perform image request: \(error)")
        return
    }
}

分析的结果会在定义VNDetectRectanglesRequest时传入的回调中返回。

你可以用几张图片来实验下检测效果,如下图:

上面图片中的黑色边框就是我们检测出的结果绘制的。

2 - 关于VNDetectRectanglesRequest类

VNDetectRectanglesRequest类用来对核心的分析请求进行定义,并且设置结果回调。VNDetectRectanglesRequest类是专门创建矩形区域识别的请求类,继承自VNImageBasedRequest,VNImageBasedRequest类是静态图像分析请求的基类,继承自VNRequest类。

我们先来看VNRequest类:

@available(iOS 11.0, *)
open class VNRequest : NSObject, NSCopying {
    // 构造方法,无处理回调
    public convenience init()

    // 构造方法其中回调参数定义如下
    // (VNRequest, Error?) -> Void
    // VNRequest为当前实例本身 error是异常(如果有)
    public init(completionHandler: VNRequestCompletionHandler? = nil)

    // 是否开启后台线程模式,此模式会占用更少的内存,CPU,GPU资源,给用户更好的渲染体验,但是会以耗时为代价
    open var preferBackgroundProcessing: Bool

    // 是否允许使用GPU进行加速
    open var usesCPUOnly: Bool

    // 分析结果列表,VNObservation是结果基类,不同的子类实现不同的功能
    open var results: [VNObservation]? { get }
    
    // 处理回调,此回调中会传入当前Request对象,通过内部的results拿到结果
    open var completionHandler: VNRequestCompletionHandler? { get }
    
    // 进行分析的特定算法版本
    open var revision: Int
    // 所支持的算法版本集合
    open class var supportedRevisions: IndexSet { get }
    // 默认的版本
    open class var defaultRevision: Int { get }
    // 当前使用的算法版本
    open class var currentRevision: Int { get }
    // 取消分析请求
    open func cancel()
}

在VNRequest类中封装了一组VNObservation对象,当成功的完成了图像分析任务后,结果会被封装成VNObservation对象,不同的分析任务对应的结果对象也不同,VNObservation是这些结果的基类,其中封装了基础的信息,如下:

@available(iOS 11.0, *)
open class VNObservation : NSObject, NSCopying, NSSecureCoding, VNRequestRevisionProviding {
    // 唯一标识id
    open var uuid: UUID { get }

    // 此结果的可信度,取值0到1之间
    open var confidence: VNConfidence { get }

    // 此结果的有效时间
    @available(iOS 14.0, *)
    open var timeRange: CMTimeRange { get }
}

VNImageBasedReques类是VNRequest的一个子类,其是静态图片分析请求类的基类,其中只封装了一个属性:

@available(iOS 11.0, *)
open class VNImageBasedRequest : VNRequest {
    // 矩形被标准化处理后的尺寸,默认为{{ 0, 0 }, { 1, 1 }}
    open var regionOfInterest: CGRect
}

regionOfInterest属性非常有用,其默认会把我们要处理的图像标准化为单位矩形,返回的结果中的坐标是以此单位矩形为标准的。

最后,我们再来看下VNDetectRectanglesRequest类,这个类即使我们进行矩形区域识别的请求配置类,如下:

@available(iOS 11.0, *)
open class VNDetectRectanglesRequest : VNImageBasedRequest {
    // 设置检测接受的矩形最小的纵横比 VNAspectRatio是Float类型的别名,取值0-1之间
    open var minimumAspectRatio: VNAspectRatio
    
    // 设置检测所接受的最大的纵横比,取值0-1之间
    open var maximumAspectRatio: VNAspectRatio

    // 设置矩形角度可以偏离90度的最大角度,取值0-45之间
    open var quadratureTolerance: VNDegrees
    
    // 设置允许检测到的最小的矩形尺寸,设置为相对原图像比例值0-1之间
    open var minimumSize: Float
    
    // 设置能够接受的最小可信度,0到1之间,小于此可信度的检测结果不会被返回
    open var minimumConfidence: VNConfidence

    // 设置允许检测出的最多结果数,默认为1,设置为0表示不限制,但是Vision框架目前最多支持16
    open var maximumObservations: Int
    
    // 结果数组
    open var results: [VNRectangleObservation]? { get }
}

需要注意,设置最大最小纵横比时,会总是以长的一边作为纵,短的一边作为横。

3 - 关于VNRectangleObservation类

VNRectangleObservatio是矩形区域分析请求的结果类,继承自VNDetectedObjectObservation类,VNDetectedObjectObservation类是VNObservation的子类,其通常与对象的识别有关,其封装了与识别相关的属性,如下:

@available(iOS 11.0, *)
open class VNDetectedObjectObservation : VNObservation {
    // 检测出的区域,注意原点在左下角
    open var boundingBox: CGRect { get }
    // 缓冲区的图像数据
    open var globalSegmentationMask: VNPixelBufferObservation? { get }
}

VNRectangleObservation类则封装了与矩形相关的属性数据:

@available(iOS 11.0, *)
open class VNRectangleObservation : VNDetectedObjectObservation {
    // 左上角位置
    open var topLeft: CGPoint { get }
    // 右上角位置
    open var topRight: CGPoint { get }
    // 左下角位置
    open var bottomLeft: CGPoint { get }
    // 右下角位置
    open var bottomRight: CGPoint { get }
}

理解了请求配置类与分析结果类的用法,剩下的就是请求句柄了。

4 - 关于VNImageRequestHandler类

VNImageRequestHandler类是请求句柄类,更通俗的说,其为分析请求提供了图像数据源,并触发请求。其支持的构造方法如下:

@available(iOS 11.0, *)
open class VNImageRequestHandler : NSObject {
    // 构造方法
    public init(cvPixelBuffer pixelBuffer: CVPixelBuffer, options: [VNImageOption : Any] = [:])
    public init(cvPixelBuffer pixelBuffer: CVPixelBuffer, orientation: CGImagePropertyOrientation, options: [VNImageOption : Any] = [:])
    public init(cgImage image: CGImage, options: [VNImageOption : Any] = [:])
    public init(cgImage image: CGImage, orientation: CGImagePropertyOrientation, options: [VNImageOption : Any] = [:])
    public init(ciImage image: CIImage, options: [VNImageOption : Any] = [:])
    public init(ciImage image: CIImage, orientation: CGImagePropertyOrientation, options: [VNImageOption : Any] = [:])
    public init(url imageURL: URL, options: [VNImageOption : Any] = [:])
    public init(url imageURL: URL, orientation: CGImagePropertyOrientation, options: [VNImageOption : Any] = [:])
    public init(data imageData: Data, options: [VNImageOption : Any] = [:])
    public init(data imageData: Data, orientation: CGImagePropertyOrientation, options: [VNImageOption : Any] = [:])
    public init(cmSampleBuffer sampleBuffer: CMSampleBuffer, options: [VNImageOption : Any] = [:])
    public init(cmSampleBuffer sampleBuffer: CMSampleBuffer, orientation: CGImagePropertyOrientation, options: [VNImageOption : Any] = [:])
}

VNImageRequestHandler类的构造方法很多,但归根结底是要提供三部分内容:

  1. 图片数据源。
  2. 图片的方向。
  3. 额外参数。

其中,图片的数据源可以从二进制数据加载,可以从网络加载,可以从CoreImage或CoreGraphics框架的图片对象加载等等,这里不多赘述。

图片的方向需要在构造句柄实例对象时进行提供,枚举如下:

@frozen public enum CGImagePropertyOrientation : UInt32, @unchecked Sendable {

    
    case up = 1 // 正向 

    case upMirrored = 2 // 水平镜像

    case down = 3 // 180度旋转

    case downMirrored = 4 // 竖直镜像

    case leftMirrored = 5 // 顺时针旋转90度后镜像

    case right = 6 // 顺时针旋转90度

    case rightMirrored = 7 // 逆时针旋转90度后镜像

    case left = 8 // 逆时针旋转90度
}

额外参数可以配置为一个字典对象,提供更多图片数据,支持配置的字段如下:

properties:此键可配置为一个属性字典,参考CGImageSourceCopyPropertiesAtIndex。

cameraIntrinsics:相机内部数据配置。

ciContex:CIContext配置。

最后,调用VNImageRequestHandler类的如下方法即可开始静态图像处理:

open func perform(_ requests: [VNRequest]) throws

同一个图像句柄可以同时发起多种图像处理请求。

注:本文所介绍的示例代码可在如下仓库获取:

https://github.com/ZYHshao/MachineLearnDemo

专注技术,懂的热爱,愿意分享,做个朋友
目录
相关文章
|
16天前
|
安全 Android开发 数据安全/隐私保护
深入探讨iOS与Android系统安全性对比分析
在移动操作系统领域,iOS和Android无疑是两大巨头。本文从技术角度出发,对这两个系统的架构、安全机制以及用户隐私保护等方面进行了详细的比较分析。通过深入探讨,我们旨在揭示两个系统在安全性方面的差异,并为用户提供一些实用的安全建议。
|
2月前
|
开发工具 Android开发 Swift
安卓与iOS开发环境对比分析
在移动应用开发的广阔舞台上,安卓和iOS这两大操作系统无疑是主角。它们各自拥有独特的特点和优势,为开发者提供了不同的开发环境和工具。本文将深入浅出地探讨安卓和iOS开发环境的主要差异,包括开发工具、编程语言、用户界面设计、性能优化以及市场覆盖等方面,旨在帮助初学者更好地理解两大平台的开发特点,并为他们选择合适的开发路径提供参考。通过比较分析,我们将揭示不同环境下的开发实践,以及如何根据项目需求和目标受众来选择最合适的开发平台。
51 2
|
2月前
|
安全 Android开发 数据安全/隐私保护
探索安卓与iOS的安全性差异:技术深度分析与实践建议
本文旨在深入探讨并比较Android和iOS两大移动操作系统在安全性方面的不同之处。通过详细的技术分析,揭示两者在架构设计、权限管理、应用生态及更新机制等方面的安全特性。同时,针对这些差异提出针对性的实践建议,旨在为开发者和用户提供增强移动设备安全性的参考。
138 3
|
1月前
|
开发工具 Android开发 Swift
安卓与iOS开发环境的差异性分析
【10月更文挑战第8天】 本文旨在探讨Android和iOS两大移动操作系统在开发环境上的不同,包括开发语言、工具、平台特性等方面。通过对这些差异性的分析,帮助开发者更好地理解两大平台,以便在项目开发中做出更合适的技术选择。
|
2月前
|
安全 Linux Android开发
探索安卓与iOS的安全性差异:技术深度分析
本文深入探讨了安卓(Android)和iOS两个主流操作系统平台在安全性方面的不同之处。通过比较它们在架构设计、系统更新机制、应用程序生态和隐私保护策略等方面的差异,揭示了每个平台独特的安全优势及潜在风险。此外,文章还讨论了用户在使用这些设备时可以采取的一些最佳实践,以增强个人数据的安全。
|
1月前
|
Java Android开发 Swift
安卓与iOS开发对比:平台选择对项目成功的影响
【10月更文挑战第4天】在移动应用开发的世界中,选择合适的平台是至关重要的。本文将深入探讨安卓和iOS两大主流平台的开发环境、用户基础、市场份额和开发成本等方面的差异,并分析这些差异如何影响项目的最终成果。通过比较这两个平台的优势与挑战,开发者可以更好地决定哪个平台更适合他们的项目需求。
112 1
|
1月前
|
设计模式 安全 Swift
探索iOS开发:打造你的第一个天气应用
【9月更文挑战第36天】在这篇文章中,我们将一起踏上iOS开发的旅程,从零开始构建一个简单的天气应用。文章将通过通俗易懂的语言,引导你理解iOS开发的基本概念,掌握Swift语言的核心语法,并逐步实现一个具有实际功能的天气应用。我们将遵循“学中做,做中学”的原则,让理论知识和实践操作紧密结合,确保学习过程既高效又有趣。无论你是编程新手还是希望拓展技能的开发者,这篇文章都将为你打开一扇通往iOS开发世界的大门。
|
10天前
|
安全 数据处理 Swift
深入探索iOS开发中的Swift语言特性
本文旨在为开发者提供对Swift语言在iOS平台开发的深度理解,涵盖从基础语法到高级特性的全面分析。通过具体案例和代码示例,揭示Swift如何简化编程过程、提高代码效率,并促进iOS应用的创新。文章不仅适合初学者作为入门指南,也适合有经验的开发者深化对Swift语言的认识。
31 9
|
9天前
|
Android开发 Swift iOS开发
探索安卓与iOS开发的差异和挑战
【10月更文挑战第37天】在移动应用开发的广阔舞台上,安卓和iOS这两大操作系统扮演着主角。它们各自拥有独特的特性、优势以及面临的开发挑战。本文将深入探讨这两个平台在开发过程中的主要差异,从编程语言到用户界面设计,再到市场分布的不同影响,旨在为开发者提供一个全面的视角,帮助他们更好地理解并应对在不同平台上进行应用开发时可能遇到的难题和机遇。
|
7天前
|
iOS开发 开发者
探索iOS开发中的SwiftUI框架
【10月更文挑战第39天】在苹果的生态系统中,SwiftUI框架以其声明式语法和易用性成为开发者的新宠。本文将深入SwiftUI的核心概念,通过实际案例展示如何利用这一框架快速构建用户界面,并探讨其对iOS应用开发流程的影响。