版本记录
版本号 | 时间 |
---|---|
V1.0 | 2018.10.17 星期三 |
前言
App中很多时候都需要进行视频处理,包括各种滤镜以及编解码等处理,好的视频处理不仅可以提高App的性能,也会给用户带来耳目一新的感觉,这里重新开了一个专题,专门讲述对视频的各种处理。
开始
首先看一下写作环境
Swift 4, iOS 11, Xcode 9
在这个iOS视频深度图教程中,学习如何利用iOS 11强大的视频深度贴图来应用实时视频滤镜并创建特效杰作!
在这个视频深度图教程中,您将学习如何:
- 请求视频Feed的深度信息。
- 操纵深度信息。
- 将视频Feed与深度数据和滤镜相结合,以创建
SFX
杰作。
对于此视频深度贴图教程,您将需要Xcode 9或更高版本。 你还需要一部背面有双摄像头的iPhone,这就是iPhone生成深度信息的方式。 还需要Apple Developer
帐户,因为您需要在设备上运行此应用程序,而不是模拟器。
准备好一切后,下载并浏览本教程的材料(您可以在本教程的顶部或底部找到一个链接)。
打开入门项目,然后在您的设备上Build并运行它。 你会看到这样的事情:
注意:为了捕捉深度信息,iPhone必须设置广角相机变焦以匹配远摄相机变焦。 因此,与相机应用程序相比,应用程序中的视频被放大。
此时,该应用程序没有做太多。 现在需要一起来完善!
Capturing Video Depth Maps Data - 捕获视频深度图数据
捕获视频的深度数据需要将AVCaptureDepthDataOutput
对象添加到AVCaptureSession
。
正如其名称所示,AVCaptureDepthDataOutput
在iOS 11中被添加,专门用于处理深度数据。
打开DepthVideoViewController.swift
并将以下行添加到configureCaptureSession()
的底部:
// 1
let depthOutput = AVCaptureDepthDataOutput()
// 2
depthOutput.setDelegate(self, callbackQueue: dataOutputQueue)
// 3
depthOutput.isFilteringEnabled = true
// 4
session.addOutput(depthOutput)
// 5
let depthConnection = depthOutput.connection(with: .depthData)
// 6
depthConnection?.videoOrientation = .portrait
以下是逐步细分:
- 1) 您创建一个新的
AVCaptureDepthDataOutput
对象 - 2) 然后将当前视图控制器设置为新对象的委托。
callbackQueue
参数是调用委托方法的调度队列。 现在,忽略错误;你以后会解决的。 - 3) 您可以对深度数据启用过滤,以利用Apple的算法填充数据中的任何漏洞。
- 4) 此时,您已准备好将配置的
AVCaptureDepthDataOutput
添加到AVCaptureSession
- 5) 在这里,您可以获得深度输出的
AVCaptureConnection
,以便... - 6) ...确保深度数据的视频方向与视频输入相匹配。
简单吧?
但坚持下去! 在构建和运行项目之前,首先需要告诉应用程序如何处理此深度数据。 这就是委托方法的用武之地。
仍然在DepthVideoViewController.swift
中,在文件末尾添加以下扩展名和委托方法:
// MARK: - Capture Depth Data Delegate Methods
extension DepthVideoViewController: AVCaptureDepthDataOutputDelegate {
func depthDataOutput(_ output: AVCaptureDepthDataOutput,
didOutput depthData: AVDepthData,
timestamp: CMTime,
connection: AVCaptureConnection) {
// 1
if previewMode == .original {
return
}
var convertedDepth: AVDepthData
// 2
if depthData.depthDataType != kCVPixelFormatType_DisparityFloat32 {
convertedDepth = depthData.converting(toDepthDataType: kCVPixelFormatType_DisparityFloat32)
} else {
convertedDepth = depthData
}
// 3
let pixelBuffer = convertedDepth.depthDataMap
// 4
pixelBuffer.clamp()
// 5
let depthMap = CIImage(cvPixelBuffer: pixelBuffer)
// 6
DispatchQueue.main.async { [weak self] in
self?.depthMap = depthMap
}
}
}
下面详细分述:
- 1) 仅当当前预览模式是使用深度贴图的任何内容时,才优化此函数以创建深度贴图。
- 2) 接下来,确保深度数据是您需要的格式:32位浮点
disparity
信息。 - 3) 您将
AVDepthData
对象中的深度数据映射另存为CVPixelBuffer
。 - 4) 使用项目中包含的扩展,然后将像素缓冲区中的像素钳位在0.0到1.0之间。
- 5) 您将像素缓冲区转换为
CIImage
并... - 6) ...然后将其存储在类变量中供以后使用。
你可能现在想要运行它,但在你做之前,你需要做一个小的补充来查看深度图,你需要显示它!
找到AVCaptureVideoDataOutputSampleBufferDelegate
扩展,并在captureOutput(_:didOutput:from :)
中查找switch
语句。 添加以下案例:
case .depth:
previewImage = depthMap ?? image
构建并运行项目,然后点击底部Depth控件的深度段。
这是与视频数据一起捕获的深度数据的视觉表示。
Video Resolutions And Frame Rates - 视频分辨率和帧速率
关于您正在捕获的深度数据,您应该了解一些事项。你的iPhone需要做很多工作来关联两个摄像头之间的像素并计算disparity
。
为了向您提供最佳的实时数据,iPhone限制了它返回的深度数据的分辨率和帧速率。
例如,您可以在iPhone 7 Plus上接收的最大深度数据量为320 x 240
,每秒24帧。 iPhone X能够以30 fps的速度提供数据。
AVCaptureDevice
不允许您设置独立于视频帧速率的深度帧速率。深度数据必须以相同的帧速率或视频帧速率的偶数部分传送。否则,会出现深度数据但没有视频数据的情况,这很奇怪。
因此,你需要做两件事:
- 1) 设置视频帧速率以确保最大可能的深度数据帧速率。
- 2) 确定视频数据和深度数据之间的比例因子。开始创建蒙版和滤镜时,比例因子很重要。
是时候让你的代码更好了!
再次在DepthVideoViewController.swift
中,将以下内容添加到configureCaptureSession()
的底部:
// 1
let outputRect = CGRect(x: 0, y: 0, width: 1, height: 1)
let videoRect = videoOutput.outputRectConverted(fromMetadataOutputRect: outputRect)
let depthRect = depthOutput.outputRectConverted(fromMetadataOutputRect: outputRect)
// 2
scale = max(videoRect.width, videoRect.height) / max(depthRect.width, depthRect.height)
// 3
do {
try camera.lockForConfiguration()
// 4
if let frameDuration = camera.activeDepthDataFormat?
.videoSupportedFrameRateRanges.first?.minFrameDuration {
camera.activeVideoMinFrameDuration = frameDuration
}
// 5
camera.unlockForConfiguration()
} catch {
fatalError(error.localizedDescription)
}
下面进行细分:
- 1) 您计算一个
CGRect
,以像素为单位定义视频和深度输出。 这些方法将完整的元数据输出rect映射到视频和数据输出的完整分辨率。 - 2) 使用
CGRect
进行视频和数据输出,可以计算它们之间的比例因子。 您获取尺寸的最大值,因为深度数据实际上已旋转90度。 - 3) 在这里您可以更改
AVCaptureDevice
配置,因此您需要锁定它,这可能会引发异常 - 4) 然后,将
AVCaptureDevice
的最小帧持续时间(最大帧速率的倒数)设置为等于深度数据支持的帧速率 - 5) 然后解锁在步骤#3中锁定的配置。
好的,构建并运行项目。 无论您是否看到差异,您的代码现在都更加强大且面向未来。
What Can You Do With This Depth Data? - 你可以用这个深度数据做什么?
您可能已经注意到屏幕底部有一个用于Mask 和 Filtered片段的滑块。 此滑块控制mask的深度焦点。
目前,该滑块似乎什么都不做。 那是因为屏幕上没有可视化的mask。 你现在要改变它!
返回到AVCaptureDepthDataOutputDelegate
扩展中的depthDataOutput(_:didOutput:timestamp:connection :)
。 在DispatchQueue.main.async
之前,添加以下内容:
// 1
if previewMode == .mask || previewMode == .filtered {
//2
switch filter {
// 3
default:
mask = depthFilters.createHighPassMask(for: depthMap,
withFocus: sliderValue,
andScale: scale)
}
}
在这段代码中:
- 1) 如果Mask或Filtered段处于活动状态,您只创建一个mask - 对您有好处!
- 2) 然后,您可以打开所选过滤器的类型(位于iPhone屏幕顶部)。
- 3) 最后,创建一个高通mask作为默认情况。 你很快就会填写其他case。
您仍然需要将mask连接到UIImageView
才能看到它。
返回到AVCaptureVideoDataOutputSampleBufferDelegate
扩展,并在captureOutput(_:didOutput:from :)
中查找switch
语句。 添加以下case:
case .mask:
previewImage = mask ?? image
构建并运行项目,然后点击Mask
段。
将滑块向左拖动时,屏幕的更多变为白色。 那是因为你实现了一个高通mask,
做得好! 您为本教程中最激动人心的部分奠定了基础:过滤器!
1. Comic Background Effect - 漫画背景效果
iOS SDK捆绑了一堆Core Image
过滤器。 特别突出的是CIComicEffect
。 此滤镜为图像提供印刷的漫画外观。
您将使用此过滤器将视频流的背景变为漫画。
打开DepthImageFilters.swift
。 这个类是所有mask和过滤器的所在。
将以下方法添加到DepthImageFilters
类:
func comic(image: CIImage, mask: CIImage) -> CIImage {
// 1
let bg = image.applyingFilter("CIComicEffect")
// 2
let filtered = image.applyingFilter("CIBlendWithMask",
parameters: ["inputBackgroundImage": bg,
"inputMaskImage": mask])
// 3
return filtered
}
下面细分说明:
- 1) 您将
CIComicEffect
应用于输入图像。 - 2) 然后使用输入mask将原始图像与漫画图像混合。
- 3) 最后,返回已过滤的图像。
现在,要使用过滤器,请打开DepthVideoViewController.swift
并找到captureOutput(_:didOutput:from :)
。 删除switch语句中的default case
并添加以下case:
case .filtered:
// 1
if let mask = mask {
// 2
switch filter {
// 3
case .comic:
previewImage = depthFilters.comic(image: image, mask: mask)
// 4
default:
previewImage = image
}
} else {
// 5
previewImage = image
}
这段代码很简单。 下面细分说明:
- 1) 你检查是否有mask,因为没有mask你不能过滤!
- 2) 您可以打开UI中选择的
filter
。 - 3) 如果所选过滤器是
comic
,则根据漫画过滤器创建新图像,并将其作为预览图像。 - 4) 否则,您只需保持视频图像不变。
- 5) 最后,你处理mask为
nil
的情况。
在运行代码之前,还应该做一件事,以便更轻松地添加未来的过滤器。
找到depthDataOutput(_:didOutput:timestamp:connection)
,并将以下case添加到switch filter
语句中:
case .comic:
mask = depthFilters.createHighPassMask(for: depthMap,
withFocus: sliderValue,
andScale: scale)
在这里,您创建一个高通mask。
这看起来与default
情况完全相同。 添加其他过滤器后,您将删除default
case,因此最好确保漫画case现在在那里。
前进。 我知道你很高兴能够这样做。 构建并运行项目,然后点击Filtered
segment。
很棒的工作! 你觉得自己是漫画书中的超级英雄吗?
2. No Green Screen? No Problem! - 没有绿屏? 没问题!
这很好,但也许你不想在超级英雄电影上工作? 也许你更喜欢科幻小说?
别担心。 下一个过滤器将让你在月球上欢呼雀跃! 为此,您需要创建一个临时的绿屏效果。
打开DepthImageFilters.swift
并将以下方法添加到类中:
func greenScreen(image: CIImage, background: CIImage, mask: CIImage) -> CIImage {
// 1
let crop = CIVector(x: 0,
y: 0,
z: image.extent.size.width,
w: image.extent.size.height)
// 2
let croppedBG = background.applyingFilter("CICrop",
parameters: ["inputRectangle": crop])
// 3
let filtered = image.applyingFilter("CIBlendWithMask",
parameters: ["inputBackgroundImage": croppedBG,
"inputMaskImage": mask])
// 4
return filtered
}
在此过滤器中:
- 1) 您可以创建一个
4D CIVector
来定义与输入图像相等的裁剪边界。 - 2) 然后将背景图像裁剪为与输入图像相同的大小 - 这对下一步非常重要
- 3) 接下来,通过基于
mask
参数混合输入和背景图像来组合它们。 - 4) 最后,返回已过滤的图像
现在你只需要在DepthVideoViewController.swift
中连接mask和过滤逻辑,你就可以开始了。
在DepthVideoViewController.swift
中找到captureOutput(_:didOutput:from :)
并将以下case添加到switch filter
语句中:
case .greenScreen:
// 1
if let background = background {
// 2
previewImage = depthFilters.greenScreen(image: image,
background: background,
mask: mask)
} else {
// 3
previewImage = image
}
这里:
- 1) 您确保背景图像存在。 它在
viewDidLoad()
中创建。 - 2) 如果存在,请使用刚刚编写的新函数使用背景和mask过滤输入图像。
- 3) 否则,只需使用输入视频图像。
接下来,找到depthDataOutput(_:didOutput:timestamp:connection)
并将以下情况添加到switch
语句中:
case .greenScreen:
mask = depthFilters.createHighPassMask(for: depthMap,
withFocus: sliderValue,
andScale: scale,
isSharp: true)
此代码创建高通mask,但使截取更清晰(更陡峭的斜率)。
构建并运行项目。 移动滑块,看看你可以在月球上放置什么物体。
3. Dream-like Blur Effect - 梦幻般的模糊效果
也许你不喜欢超级英雄或科幻小说类型。 我知道了。 你更像是一个艺术电影类型的人。 如果是这样,那么下一个过滤器来了。
使用此过滤器,除了与相机之间的距离很窄的物体外,您将模糊除了物体之外的任何物体。 这可以给你的电影带来梦幻般的感觉。
返回到DepthImageFilters.swift
并向该类添加一个新函数:
func blur(image: CIImage, mask: CIImage) -> CIImage {
// 1
let blurRadius: CGFloat = 10
// 2
let crop = CIVector(x: 0,
y: 0,
z: image.extent.size.width,
w: image.extent.size.height)
// 3
let invertedMask = mask.applyingFilter("CIColorInvert")
// 4
let blurred = image.applyingFilter("CIMaskedVariableBlur",
parameters: ["inputMask": invertedMask,
"inputRadius": blurRadius])
// 5
let filtered = blurred.applyingFilter("CICrop",
parameters: ["inputRectangle": crop])
// 6
return filtered
}
这个有点复杂,但这就是你做的:
- 1) 您可以定义要使用的模糊半径 - 半径越大,模糊越多,模糊越慢!
- 2) 再次,您创建一个
4D CIVector
来定义裁剪区域。 这是因为模糊将有效地增加边缘处的图像,您只需要原始尺寸。 - 3) 然后你反转mask,因为你使用的模糊滤镜在mask是白色的地方模糊。
- 4) 接下来,使用反转mask和模糊半径作为参数,将
CIMaskedVariableBlur
滤镜应用于图像。 - 5) 您裁剪模糊图像以保持所需的大小。
- 6) 最后,返回已过滤的图像。
打开DepthVideoViewController.swift
并在captureOutput(_:didOutput:from :)
中的switch
语句中添加一个新case:
case .blur:
previewImage = depthFilters.blur(image: image, mask: mask)
这将在UI中选择时创建模糊过滤器。 当你在那里时,你可以删除default
情况,因为switch filter
语句现在是详尽的。
现在为mask
。
将以下情况的default
case替换为depthDataOutput(_:didOutput:timestamp:connection)
内的switch语句:
case .blur:
mask = depthFilters.createBandPassMask(for: depthMap,
withFocus: sliderValue,
andScale: scale)
在这里,您可以为要使用的模糊滤镜创建带通mask。
构建并运行此项目。 尝试调整Mask & Filtered
部分中的滑块以及更改过滤器以查看可以创建的效果。
源码
1. Swift
首先看一下文档结构
看一下sb中的内容
1. CVPixelBufferExtension.swift
import AVFoundation
import UIKit
extension CVPixelBuffer {
func clamp() {
let width = CVPixelBufferGetWidth(self)
let height = CVPixelBufferGetHeight(self)
CVPixelBufferLockBaseAddress(self, CVPixelBufferLockFlags(rawValue: 0))
let floatBuffer = unsafeBitCast(CVPixelBufferGetBaseAddress(self), to: UnsafeMutablePointer<Float>.self)
for y in 0 ..< height {
for x in 0 ..< width {
let pixel = floatBuffer[y * width + x]
floatBuffer[y * width + x] = min(1.0, max(pixel, 0.0))
}
}
CVPixelBufferUnlockBaseAddress(self, CVPixelBufferLockFlags(rawValue: 0))
}
}
2. ControlEnums.swift
enum PreviewMode: Int {
case original
case depth
case mask
case filtered
}
enum FilterType: Int {
case comic
case greenScreen
case blur
}
3. DepthImageFilters.swift
import UIKit
enum MaskParams {
static let slope: CGFloat = 4.0
static let sharpSlope: CGFloat = 10.0
static let width: CGFloat = 0.1
}
class DepthImageFilters {
func createHighPassMask(for depthImage: CIImage,
withFocus focus: CGFloat,
andScale scale: CGFloat,
isSharp: Bool = false) -> CIImage {
let s = isSharp ? MaskParams.sharpSlope : MaskParams.slope
let filterWidth = 2 / s + MaskParams.width
let b = -s * (focus - filterWidth / 2)
let mask = depthImage
.applyingFilter("CIColorMatrix", parameters: [
"inputRVector": CIVector(x: s, y: 0, z: 0, w: 0),
"inputGVector": CIVector(x: 0, y: s, z: 0, w: 0),
"inputBVector": CIVector(x: 0, y: 0, z: s, w: 0),
"inputBiasVector": CIVector(x: b, y: b, z: b, w: 0)])
.applyingFilter("CIColorClamp")
.applyingFilter("CIBicubicScaleTransform",
parameters: ["inputScale": scale])
return mask
}
func createBandPassMask(for depthImage: CIImage,
withFocus focus: CGFloat,
andScale scale: CGFloat) -> CIImage {
let s1 = MaskParams.slope
let s2 = -MaskParams.slope
let filterWidth = 2 / MaskParams.slope + MaskParams.width
let b1 = -s1 * (focus - filterWidth / 2)
let b2 = -s2 * (focus + filterWidth / 2)
let mask0 = depthImage
.applyingFilter("CIColorMatrix", parameters: [
"inputRVector": CIVector(x: s1, y: 0, z: 0, w: 0),
"inputGVector": CIVector(x: 0, y: s1, z: 0, w: 0),
"inputBVector": CIVector(x: 0, y: 0, z: s1, w: 0),
"inputBiasVector": CIVector(x: b1, y: b1, z: b1, w: 0)])
.applyingFilter("CIColorClamp")
let mask1 = depthImage
.applyingFilter("CIColorMatrix", parameters: [
"inputRVector": CIVector(x: s2, y: 0, z: 0, w: 0),
"inputGVector": CIVector(x: 0, y: s2, z: 0, w: 0),
"inputBVector": CIVector(x: 0, y: 0, z: s2, w: 0),
"inputBiasVector": CIVector(x: b2, y: b2, z: b2, w: 0)])
.applyingFilter("CIColorClamp")
let combinedMask = mask0.applyingFilter("CIDarkenBlendMode",
parameters: ["inputBackgroundImage": mask1])
let mask = combinedMask.applyingFilter("CIBicubicScaleTransform",
parameters: ["inputScale": scale])
return mask
}
func comic(image: CIImage, mask: CIImage) -> CIImage {
let bg = image.applyingFilter("CIComicEffect")
let filtered = image.applyingFilter("CIBlendWithMask",
parameters: ["inputBackgroundImage": bg,
"inputMaskImage": mask])
return filtered
}
func greenScreen(image: CIImage, background: CIImage, mask: CIImage) -> CIImage {
let crop = CIVector(x: 0,
y: 0,
z: image.extent.size.width,
w: image.extent.size.height)
let croppedBG = background.applyingFilter("CICrop",
parameters: ["inputRectangle": crop])
let filtered = image.applyingFilter("CIBlendWithMask",
parameters: ["inputBackgroundImage": croppedBG,
"inputMaskImage": mask])
return filtered
}
func blur(image: CIImage, mask: CIImage) -> CIImage {
let blurRadius: CGFloat = 10
let crop = CIVector(x: 0,
y: 0,
z: image.extent.size.width,
w: image.extent.size.height)
let invertedMask = mask.applyingFilter("CIColorInvert")
let blurred = image.applyingFilter("CIMaskedVariableBlur",
parameters: ["inputMask": invertedMask,
"inputRadius": blurRadius])
let filtered = blurred.applyingFilter("CICrop",
parameters: ["inputRectangle": crop])
return filtered
}
}
4. DepthVideoViewController.swift
import UIKit
import AVFoundation
class DepthVideoViewController: UIViewController {
@IBOutlet weak var previewView: UIImageView!
@IBOutlet weak var previewModeControl: UISegmentedControl!
@IBOutlet weak var filterControl: UISegmentedControl!
@IBOutlet weak var filterControlView: UIView!
@IBOutlet weak var depthSlider: UISlider!
var sliderValue: CGFloat = 0.0
var previewMode = PreviewMode.original
var filter = FilterType.comic
let session = AVCaptureSession()
let dataOutputQueue = DispatchQueue(label: "video data queue",
qos: .userInitiated,
attributes: [],
autoreleaseFrequency: .workItem)
var background: CIImage?
var depthMap: CIImage?
var mask: CIImage?
var scale: CGFloat = 0.0
var depthFilters = DepthImageFilters()
override func viewDidLoad() {
super.viewDidLoad()
if let bgImage = UIImage(named: "earth_rise.jpg") {
background = CIImage(image: bgImage)
}
filterControlView.isHidden = true
depthSlider.isHidden = true
previewMode = PreviewMode(rawValue: previewModeControl.selectedSegmentIndex) ?? .original
filter = FilterType(rawValue: filterControl.selectedSegmentIndex) ?? .comic
sliderValue = CGFloat(depthSlider.value)
configureCaptureSession()
session.startRunning()
}
override var shouldAutorotate: Bool {
return false
}
}
// MARK: - Helper Methods
extension DepthVideoViewController {
func configureCaptureSession() {
guard let camera = AVCaptureDevice.default(.builtInDualCamera,
for: .video,
position: .unspecified) else {
fatalError("No depth video camera available")
}
session.sessionPreset = .photo
do {
let cameraInput = try AVCaptureDeviceInput(device: camera)
session.addInput(cameraInput)
} catch {
fatalError(error.localizedDescription)
}
let videoOutput = AVCaptureVideoDataOutput()
videoOutput.setSampleBufferDelegate(self, queue: dataOutputQueue)
videoOutput.videoSettings = [kCVPixelBufferPixelFormatTypeKey as String: kCVPixelFormatType_32BGRA]
session.addOutput(videoOutput)
let videoConnection = videoOutput.connection(with: .video)
videoConnection?.videoOrientation = .portrait
let depthOutput = AVCaptureDepthDataOutput()
depthOutput.setDelegate(self, callbackQueue: dataOutputQueue)
depthOutput.isFilteringEnabled = true
session.addOutput(depthOutput)
let depthConnection = depthOutput.connection(with: .depthData)
depthConnection?.videoOrientation = .portrait
let outputRect = CGRect(x: 0, y: 0, width: 1, height: 1)
let videoRect = videoOutput.outputRectConverted(fromMetadataOutputRect: outputRect)
let depthRect = depthOutput.outputRectConverted(fromMetadataOutputRect: outputRect)
scale = max(videoRect.width, videoRect.height) / max(depthRect.width, depthRect.height)
do {
try camera.lockForConfiguration()
if let frameDuration = camera.activeDepthDataFormat?
.videoSupportedFrameRateRanges.first?.minFrameDuration {
camera.activeVideoMinFrameDuration = frameDuration
}
camera.unlockForConfiguration()
} catch {
fatalError(error.localizedDescription)
}
}
}
// MARK: - Capture Video Data Delegate Methods
extension DepthVideoViewController: AVCaptureVideoDataOutputSampleBufferDelegate {
func captureOutput(_ output: AVCaptureOutput,
didOutput sampleBuffer: CMSampleBuffer,
from connection: AVCaptureConnection) {
let pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer)
let image = CIImage(cvPixelBuffer: pixelBuffer!)
let previewImage: CIImage
switch previewMode {
case .original:
previewImage = image
case .depth:
previewImage = depthMap ?? image
case .mask:
previewImage = mask ?? image
case .filtered:
if let mask = mask {
switch filter {
case .comic:
previewImage = depthFilters.comic(image: image, mask: mask)
case .greenScreen:
if let background = background {
previewImage = depthFilters.greenScreen(image: image,
background: background,
mask: mask)
} else {
previewImage = image
}
case .blur:
previewImage = depthFilters.blur(image: image, mask: mask)
}
} else {
previewImage = image
}
}
let displayImage = UIImage(ciImage: previewImage)
DispatchQueue.main.async { [weak self] in
self?.previewView.image = displayImage
}
}
}
// MARK: - Capture Depth Data Delegate Methods
extension DepthVideoViewController: AVCaptureDepthDataOutputDelegate {
func depthDataOutput(_ output: AVCaptureDepthDataOutput,
didOutput depthData: AVDepthData,
timestamp: CMTime,
connection: AVCaptureConnection) {
if previewMode == .original {
return
}
var convertedDepth: AVDepthData
if depthData.depthDataType != kCVPixelFormatType_DisparityFloat32 {
convertedDepth = depthData.converting(toDepthDataType: kCVPixelFormatType_DisparityFloat32)
} else {
convertedDepth = depthData
}
let pixelBuffer = convertedDepth.depthDataMap
pixelBuffer.clamp()
let depthMap = CIImage(cvPixelBuffer: pixelBuffer)
if previewMode == .mask || previewMode == .filtered {
switch filter {
case .comic:
mask = depthFilters.createHighPassMask(for: depthMap,
withFocus: sliderValue,
andScale: scale)
case .greenScreen:
mask = depthFilters.createHighPassMask(for: depthMap,
withFocus: sliderValue,
andScale: scale,
isSharp: true)
case .blur:
mask = depthFilters.createBandPassMask(for: depthMap,
withFocus: sliderValue,
andScale: scale)
}
}
DispatchQueue.main.async { [weak self] in
self?.depthMap = depthMap
}
}
}
// MARK: - Slider Methods
extension DepthVideoViewController {
@IBAction func sliderValueChanged(_ sender: UISlider) {
sliderValue = CGFloat(depthSlider.value)
}
}
// MARK: - Segmented Control Methods
extension DepthVideoViewController {
@IBAction func previewModeChanged(_ sender: UISegmentedControl) {
previewMode = PreviewMode(rawValue: previewModeControl.selectedSegmentIndex) ?? .original
if previewMode == .mask || previewMode == .filtered {
filterControlView.isHidden = false
depthSlider.isHidden = false
} else {
filterControlView.isHidden = true
depthSlider.isHidden = true
}
}
@IBAction func filterTypeChanged(_ sender: UISegmentedControl) {
filter = FilterType(rawValue: filterControl.selectedSegmentIndex) ?? .comic
}
}
后记
本篇主要讲述了视频深度相关处理简单示例,感兴趣的给个赞或者关注~~~