本文作者:苯酚

0x01 前语

iOS 16 体系为我们带来了比较冷艳的桌面锁屏作用:Depth Effect。它可以运用一张一般图片当布景,一同可以在恰当的当地遮拦住部分桌面组件,形成一种景深的作用(如下图)。

怎么完结 iOS 16 带来的 Depth Effect 图片作用

那么我们可以在自己的 App 完结相似的作用吗?一开始我以为 iOS 16 新增了新的的 UIKit 控件,可以像 UIVisualEffectView 相同几行简略的 API 就可以完结,但终究发现没有。假设给的图片是现已分好层的多张图,那么完结就是简略的将时钟控件像夹心饼干相同夹在中心即可。但是实践中发现,网上随便下载的单张图片设置为锁屏布景它也可以到达这种作用。联想到 iOS 16 的体系相册在重按后可以将照片中的主体直接切开拖拽出来,所以以为它一定是利用了某些图像切开算法将前景和布景别离开来,这样就得到了多层的图像

怎么完结 iOS 16 带来的 Depth Effect 图片作用

0x02 图像切开(Image Segmentation)

比较经典的图像切开算法是 分水岭算法(Watershed),它切开出来的图像很精准,且边缘处理非常好,但它要求人工在前景和布景的大约方位上别离画上一笔(仅一笔就好,后边算法将自动别离出前景和布景),并不适用本文全自动的要求。最近几年机器学习呈现出了不少的作用,其中之一就是全自动化的图像切开。果然在通过简略的查找后,发现苹果现已供给预训练好的模型。

访问苹果机器学习官网 developer.apple.com/machine-lea… 下载训练好的模型 DeeplabV3。将模型文件拖到 Xcode 工程中,选中后可以检查它的一些信息:

怎么完结 iOS 16 带来的 Depth Effect 图片作用

这儿其实我们首要关注模型的输入、输出就好,点击 Predictions 标签页,可以看到,模型要求输入 513×513 的图片,输出是成员类型为 Int32,巨细 513×513 的二维数组,每个数值表明对应图像像素点的分类。这儿的成员之所以是 Int32 而不是简略的 Bool,是因为该模型可以将图像切开为多个不同的部分,不只是前景和布景。实践中我们发现,数值为 0 可以以为是布景,非 0 值为前景。

怎么完结 iOS 16 带来的 Depth Effect 图片作用

下面是一张样例图片运转切开之后得到的作用:

怎么完结 iOS 16 带来的 Depth Effect 图片作用

它被分为了 0 和 15 两个值,别离就是布景和前景了。

0x03 实践

模型现已有了,完结方案也差不多了,接下来就是详细的实践了。

模型拖到 Xcode 工程中后,Xcode 将自动为我们生成一个类:DeepLabV3。我们可以直接创立它的实例而无需任何的 import

    lazy var model = try! DeepLabV3(configuration: {
        let config = MLModelConfiguration()
        config.allowLowPrecisionAccumulationOnGPU = true
        config.computeUnits = .cpuAndNeuralEngine
        return config
    }())

然后,用这个实例创立一个 VNCoreMLRequest,央求通过机器学习引擎来剖析图片,并在回调中得到作用:

    lazy var request = VNCoreMLRequest(model: try! VNCoreMLModel(for: model.model)) { [unowned self] request, error in
        if let results = request.results as? [VNCoreMLFeatureValueObservation] {
            // 终究的切开作用在 arrayValue 中
            if let feature = results.first?.featureValue, let arrayValue = feature.multiArrayValue {
                let width = arrayValue.shape[0].intValue
                let height = arrayValue.shape[1].intValue
                let stride = arrayValue.strides[0].intValue
                // ...
            }
        }
    }

终究在合适的当地创立 VNImageRequestHandler 主张央求:

    private func segment() {
        if let image = self.imageView.image {
            imageSize = image.size
            DispatchQueue.global().async { [unowned self] in
                self.request.imageCropAndScaleOption = .scaleFill
                let handler = VNImageRequestHandler(cgImage: image.resize(to: .init(width: 513, height: 513)).cgImage!)
                try? handler.perform([self.request])
            }
        }
    }

留意:

  1. request 的回谐和 handler 主张央求的代码在同一个线程中,同步等候作用,所以这儿最好 dispatch 到子线程操作
  2. request 需求设置 imageCropAndScaleOption 为 .scallFill,否则它默许将自动裁切中心部分,将得到不符合预期的作用

输入以下样例图片,

怎么完结 iOS 16 带来的 Depth Effect 图片作用

将回来的作用 arrayValue 处理成为黑白图片后的作用:

怎么完结 iOS 16 带来的 Depth Effect 图片作用

发现它切开的仍是挺精准的。当然,假设要在代码中当掩码图(mask)来运用,应当将它处理为布景全透明,而前景不透的图片:

怎么完结 iOS 16 带来的 Depth Effect 图片作用

终究,我们将原图放最基层,其它控件放中心,原图 + mask 的视图放最上层,就形成了终究的作用:

怎么完结 iOS 16 带来的 Depth Effect 图片作用

实践反面的原理就是夹心饼干:

怎么完结 iOS 16 带来的 Depth Effect 图片作用

怎么完结 iOS 16 带来的 Depth Effect 图片作用

再多来几张作用图:

怎么完结 iOS 16 带来的 Depth Effect 图片作用
怎么完结 iOS 16 带来的 Depth Effect 图片作用

0x04 后记

当然该模型并不是万能的,在详细的使用中还存在局限性,关于有人物的照片切开得较好,但是关于相似大场景的景色照这种或许呈现彻底无法切开的状况。本文的 Demo 可以在 Github 上找到。

参考资料

  1. developer.apple.com/documentati…
  2. www.appcoda.com.tw/vision-pers…
  3. enlight.nyc/projects/im…

本文发布自网易云音乐技术团队,文章未经授权禁止任何形式的转载。我们常年接纳各类技术岗位,假设你预备换作业,又刚好喜爱云音乐,那就加入我们 grp.music-fe(at)corp.netease.com!