版本記錄
版本號 | 時間 |
---|---|
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
}
}
后記
本篇主要講述了視頻深度相關處理簡單示例,感興趣的給個贊或者關注~~~