本文作者:苯酚
0x01 前语
iOS 16 体系为我们带来了比较冷艳的桌面锁屏作用:Depth Effect。它可以运用一张一般图片当布景,一同可以在恰当的当地遮拦住部分桌面组件,形成一种景深的作用(如下图)。
那么我们可以在自己的 App 完结相似的作用吗?一开始我以为 iOS 16 新增了新的的 UIKit 控件,可以像 UIVisualEffectView
相同几行简略的 API 就可以完结,但终究发现没有。假设给的图片是现已分好层的多张图,那么完结就是简略的将时钟控件像夹心饼干相同夹在中心即可。但是实践中发现,网上随便下载的单张图片设置为锁屏布景它也可以到达这种作用。联想到 iOS 16 的体系相册在重按后可以将照片中的主体直接切开拖拽出来,所以以为它一定是利用了某些图像切开算法将前景和布景别离开来,这样就得到了多层的图像
0x02 图像切开(Image Segmentation)
比较经典的图像切开算法是 分水岭算法(Watershed),它切开出来的图像很精准,且边缘处理非常好,但它要求人工在前景和布景的大约方位上别离画上一笔(仅一笔就好,后边算法将自动别离出前景和布景),并不适用本文全自动的要求。最近几年机器学习呈现出了不少的作用,其中之一就是全自动化的图像切开。果然在通过简略的查找后,发现苹果现已供给预训练好的模型。
访问苹果机器学习官网 developer.apple.com/machine-lea… 下载训练好的模型 DeeplabV3。将模型文件拖到 Xcode 工程中,选中后可以检查它的一些信息:
这儿其实我们首要关注模型的输入、输出就好,点击 Predictions 标签页,可以看到,模型要求输入 513×513 的图片,输出是成员类型为 Int32,巨细 513×513 的二维数组,每个数值表明对应图像像素点的分类。这儿的成员之所以是 Int32 而不是简略的 Bool,是因为该模型可以将图像切开为多个不同的部分,不只是前景和布景。实践中我们发现,数值为 0 可以以为是布景,非 0 值为前景。
下面是一张样例图片运转切开之后得到的作用:
它被分为了 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])
}
}
}
留意:
- request 的回谐和 handler 主张央求的代码在同一个线程中,同步等候作用,所以这儿最好 dispatch 到子线程操作
- request 需求设置 imageCropAndScaleOption 为
.scallFill
,否则它默许将自动裁切中心部分,将得到不符合预期的作用
输入以下样例图片,
将回来的作用 arrayValue
处理成为黑白图片后的作用:
发现它切开的仍是挺精准的。当然,假设要在代码中当掩码图(mask)来运用,应当将它处理为布景全透明,而前景不透的图片:
终究,我们将原图放最基层,其它控件放中心,原图 + mask 的视图放最上层,就形成了终究的作用:
实践反面的原理就是夹心饼干:
再多来几张作用图:
0x04 后记
当然该模型并不是万能的,在详细的使用中还存在局限性,关于有人物的照片切开得较好,但是关于相似大场景的景色照这种或许呈现彻底无法切开的状况。本文的 Demo 可以在 Github 上找到。
参考资料
- developer.apple.com/documentati…
- www.appcoda.com.tw/vision-pers…
- enlight.nyc/projects/im…
本文发布自网易云音乐技术团队,文章未经授权禁止任何形式的转载。我们常年接纳各类技术岗位,假设你预备换作业,又刚好喜爱云音乐,那就加入我们 grp.music-fe(at)corp.netease.com!