(十三)深入了解AVFoundation-采集:視頻幀采集與實時濾鏡處理

引言

在移動應用中,實時視頻處理已成為視頻拍攝、短視頻、直播、美顏相機等功能的核心技術之一。從簡單的濾鏡疊加,到復雜的美顏、AR 特效,背后都離不開對每一幀圖像的高效采集與處理。在前幾篇文章中,我們已經實現了基本的視頻采集、人臉識別等功能,而本篇將邁出更進一步的一步 ——?實時處理每一幀圖像,打造動態視覺效果

本篇內容將圍繞?AVCaptureVideoDataOutput?展開,講解如何獲取原始視頻幀,并借助 CoreImage 或 Metal 實現濾鏡、美顏等實時圖像處理效果。最后,我們還將以一個“實時美顏相機”為示例,串聯起采集、處理與渲染的完整流程,幫助你搭建具備實用價值的實時視頻處理系統。

采集視頻幀:AVCaptureVideoDataOutput

在使用 AVFoundation 進行圖像采集時,無論是拍照、錄像、還是視頻幀處理,整體的配置流程幾乎一致。我們依然需要:

  1. 創建?AVCaptureSession。
  2. 添加輸入設備(通常是攝像頭)。
  3. 添加輸出對象。
  4. 啟動會話。

唯一的區別在于?輸出類型的不同。在拍照場景中,我們使用的是?AVCapturePhotoOutput;錄制視頻則使用?AVCaptureMovieFileOutput。而本篇重點關注的實時視頻幀處理,需要使用的是:AVCaptureVideoDataOutput。

AVCaptureVideoDataOutput?負責將攝像頭捕捉到的原始幀(CVPixelBuffer)逐幀輸出給我們,這種輸出是“實時的”,每一幀都會通過代理方法交付給我們進行處理,非常適合用于:

  • 添加濾鏡
  • 美顏處理
  • 實時圖像識別

類的基本結構

我們先來看一下?PHCaptureVideoController 的基本結構:

import UIKit
import AVFoundationclass PHCaptureVideoController: NSObject, AVCaptureVideoDataOutputSampleBufferDelegate {/// 會話let session = AVCaptureSession()/// 輸出private let videoDataOutput = AVCaptureVideoDataOutput()/// 輸入private var captureDeviceInput: AVCaptureDeviceInput?/// 隊列private let sessionQueue = DispatchQueue(label: "com.example.captureSession")/// 代理weak var delegate: PHCaptureProtocol?/// 配置會話func setupConfigureSession() {session.beginConfiguration()// 1.設置會話預設setupSessionPreset()// 2.設置會話輸入if !setupSessionInput(device: self.getDefaultCameraDevice()) {delegate?.captureError(NSError(domain: "PHCaptureController", code: 1001, userInfo: [NSLocalizedDescriptionKey: "Failed to add input"]))return}// 3.設置會話輸出if !setupSessionOutput() {delegate?.captureError(NSError(domain: "PHCaptureController", code: 1002, userInfo: [NSLocalizedDescriptionKey: "Failed to add output"]))return}session.commitConfiguration()}/// 設置會話話預設private func setupSessionPreset() {session.sessionPreset = .high}/// 設置會話輸入private func setupSessionInput(device: AVCaptureDevice? = nil) -> Bool {return true}/// 設置會話輸出private func setupSessionOutput() -> Bool {return true}/// 啟動會話func startSession() {}/// 停止會話func stopSession() {}//MARK: private/// 獲取默認攝像頭private func getDefaultCameraDevice() -> AVCaptureDevice? {return getCameraDevice(position: .back)}/// 獲取指定攝像頭private func getCameraDevice(position: AVCaptureDevice.Position) -> AVCaptureDevice? {let devices = AVCaptureDevice.DiscoverySession(deviceTypes: [.builtInWideAngleCamera], mediaType: .video, position: position).devicesreturn devices.first}//MARK: - AVCaptureVideoDataOutputSampleBufferDelegate/// 捕獲輸出func captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) {// 處理視頻數據delegate?.captureVideo(sampleBuffer)}/// 捕獲輸出丟失func captureOutput(_ output: AVCaptureOutput, didDrop sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) {}}

整個類中劃分為了幾個方法:

  1. 配置會話。
  2. 設置會話輸入。
  3. 設置會話輸出。
  4. 啟動會話、停止會話。
  5. 捕捉到視頻幀的回調、丟失視頻幀的回調。

配置會話

我們在?setupConfigureSession 方法中需要完成三個操作:

  1. 設置會話預設。
  2. 添加攝像頭輸入。
  3. 添加照片輸出。

并且保證這些操作需要在會話?beginConfiguration 與?commitConfiguration 方法之間執行。

    /// 配置會話func setupConfigureSession() {session.beginConfiguration()// 1.設置會話預設setupSessionPreset()// 2.設置會話輸入if !setupSessionInput(device: self.getDefaultCameraDevice()) {delegate?.captureError(NSError(domain: "PHCaptureController", code: 1001, userInfo: [NSLocalizedDescriptionKey: "Failed to add input"]))return}// 3.設置會話輸出if !setupSessionOutput() {delegate?.captureError(NSError(domain: "PHCaptureController", code: 1002, userInfo: [NSLocalizedDescriptionKey: "Failed to add output"]))return}session.commitConfiguration()}
    /// 設置會話話預設private func setupSessionPreset() {session.sessionPreset = .high}

會話輸入

添加攝像頭設備,并包裝一層 AVCaptureDeviceInput 添加到會話中。

    /// 設置會話輸入private func setupSessionInput(device: AVCaptureDevice? = nil) -> Bool {// 1.獲取攝像頭guard let device = device else { return false }do {captureDeviceInput = try AVCaptureDeviceInput(device: device)if session.canAddInput(captureDeviceInput!) {session.addInput(captureDeviceInput!)} else {return false}} catch {delegate?.captureError(error)return false}return true}

會話輸出

會話輸出使用AVCaptureVideoDataOutput輸出,設置像素合適以及檢查是否可以添加。

/// 設置會話輸出
private func setupSessionOutput() -> Bool {videoDataOutput.videoSettings = [kCVPixelBufferPixelFormatTypeKey as String: kCVPixelFormatType_32BGRA]videoDataOutput.setSampleBufferDelegate(self, queue: sessionQueue)if session.canAddOutput(videoDataOutput) {session.addOutput(videoDataOutput)} else {return false}return true
}
  1. 設置像素格式:我們選擇?kCVPixelFormatType_32BGRA,這是 CoreImage 和 Metal 最常用、兼容性最強的格式;
  2. 設置代理與處理隊列:setSampleBufferDelegate(_:queue:)?會將每一幀回調給你指定的隊列處理,避免阻塞主線程;
  3. 檢查輸出是否可添加:通過?canAddOutput?判斷 session 是否支持添加該輸出類型,確保穩定性。

啟動、停止會話

在自定義的串行隊列中執行啟動和停止會話。

    /// 啟動會話func startSession() {sessionQueue.async {if !self.session.isRunning {self.session.startRunning()}}}/// 停止會話func stopSession() {sessionQueue.async {if self.session.isRunning {self.session.stopRunning()}}}

視頻幀數據處理

接下來我需要實現AVCaptureVideoDataOutputSampleBufferDelegate的代理方法,并處理回調中的視頻數據。

回調方法

AVCaptureVideoDataOutputSampleBufferDelegate提供了兩個代理方法,一個用于捕獲實時輸出的視頻幀數據,一個用來捕獲丟失的幀數據。

我們在捕獲視頻幀數據的方法中將 CMSampleBuffer 數據回調到視圖控制器。

    //MARK: - AVCaptureVideoDataOutputSampleBufferDelegate/// 捕獲輸出func captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) {// 處理視頻數據delegate?.captureVideo(sampleBuffer)}

添加濾鏡

在這里我們就采用一個最簡單的方式為實時預覽添加一個濾鏡,通過CIFilter來創建。它支持很多類型的濾鏡,比如顏色翻轉、漫畫風格、色彩分層、像素化等等。

    // 視頻幀func captureVideo(_ sampleBuffer: CMSampleBuffer) {// 處理視頻幀guard let pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer) else { return }// 處理像素緩沖區let ciImage = CIImage(cvPixelBuffer: pixelBuffer)// 添加濾鏡let filter = CIFilter(name: "CIComicEffect")filter?.setValue(ciImage, forKey: kCIInputImageKey)guard let outputImage = filter?.outputImage,let cgImage = ciContext.createCGImage(outputImage, from: ciImage.extent) else {return}let uiImage = UIImage(cgImage: cgImage, scale: 1.0, orientation: .right)// 顯示到預覽圖層DispatchQueue.main.async {self.previewImageView.image = uiImage}}

最終效果如下:

結語

通過本文,我們實現了使用?AVCaptureVideoDataOutput?獲取原始視頻幀,并結合 Core Image 對其進行實時處理的完整流程。無論是美顏、濾鏡,還是圖像分析,這種方式都為實時圖像處理提供了極大的靈活性和可擴展性。

不過,需要注意的是,CoreImage?雖然上手簡單、易于調試,但在處理高分辨率視頻幀或多個濾鏡疊加時,性能可能會成為瓶頸。如果你希望在性能上進一步優化,或者實現更加復雜、專業的圖像處理效果,推薦使用更底層的圖形處理框架,例如?Metal?或?OpenGLES,它們可以更精細地控制渲染流程、內存使用和 GPU 資源調度,是構建高性能視頻應用的不二之選。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/84076.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/84076.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/84076.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

數字政務安全實戰:等保2.0框架下OA系統防護全解析

近期在Python基礎教學領域深入鉆研函數機制、數據結構優化等內容時,深刻意識到信息安全作為技術基石的戰略價值。在政務數字化轉型浪潮中,Python憑借其高擴展性與豐富的安全生態庫,成為構建政務OA系統安全防護體系的核心工具。本文將以等保2.…

Pytorch項目實戰-2:花卉分類

一、前言 在深度學習項目中,數據集的處理和模型的訓練、測試、預測是關鍵環節。本文將為小白詳細介紹從數據集搜集、清洗、劃分到模型訓練、測試、預測以及模型結構查看的全流程,附帶代碼和操作說明,讓你輕松上手! 二、數據集 …

React Flow 邊事件處理實戰:鼠標事件、鍵盤操作及連接規則設置(附完整代碼)

本文為《React Agent:從零開始構建 AI 智能體》專欄系列文章。 專欄地址:https://blog.csdn.net/suiyingy/category_12933485.html。項目地址:https://gitee.com/fgai/react-agent(含完整代碼示?例與實戰源)。完整介紹…

java小結(一)

java(上) 模塊一 1.JDK,JRE,JVM 知識點 核心內容 易混淆點 JDK定義 Java Development Kit(Java開發工具包),包含開發所需全部工具 JDK包含JRE的關系容易混淆 JRE定義 Java Runtime Environment(Jav…

ddns-go安裝介紹-強大的ipv6動態域名解析神器-家庭云計算專家

ddns-go 是一款輕量級開源動態域名解析工具,專注于解決動態IP環境下的域名綁定問題,尤其適配IPv6網絡環境。其核心功能包括: 1.IPv6動態解析:自動檢測本地IPv6地址變化(支持網卡、接口或命令獲取)&#xf…

Docker-mongodb

拉取 MongoDB 鏡像: docker pull mongo 創建容器并設置用戶: 要掛載本地數據目錄,請替換此路徑: /Users/Allen/Env/AllenDocker/mongodb/data/db docker run -d --name local-mongodb \-e MONGO_INITDB_ROOT_USERNAMEadmin \-e MONGO_INITDB_ROOT_PA…

WooCommerce緩存教程 – 如何防止緩存破壞你的WooCommerce網站?

我們在以前的文章中探討過如何加快你的WordPress網站的速度,并研究過各種形式的緩存。 然而,像那些使用WooCommerce的動態電子商務網站,在讓緩存正常工作方面往往會面臨重大挑戰。 在本指南中,我們將告訴你如何為WooCommerce設置…

貪心算法 Part04

總結下重疊區間問題 LC 452. 用最少數量的箭引爆氣球 和 LC 435. 無重疊區間 本質上是一樣的。 LC 452. 用最少數量的箭引爆氣球 是求n個區間當中 , 區間的種類數量 k。此處可以理解為,重疊在一起的區間屬于同一品種,沒有重疊的區間當然…

云原生CD工具-Argocd+ArgoRollout入門到精通

第一章 Argo CD簡介 課時1.1 Argo產品介紹 ARGO官網地址:https://argoproj.github.io/ 旗下產品有: Argo Workflows、ArgoCD 、Argo Rollouts 、Argo Events 課時1.2 什么是Argo CD Argo CD 是一個開源的持續交付工具, 是 Kubernetes 的聲明式 GitOps 持續交付工具。專…

數據分析與應用---數據可視化基礎

目錄 Matplotlib基礎繪圖 (一)、pyplot繪圖基礎語法與常用參數 1、pyplot基礎語法 (1) 創建畫布與創建子圖 (2) 添加畫布內容 (3) 保存與顯示圖形 案例代碼 2. 設置pyplot的動態rc參數 (二)、使用Matplotlib繪制進階圖形 1. 繪制散點圖----scatter 2. 繪制折線…

PP-YOLOE-SOD學習筆記1

項目:基于PP-YOLOE-SOD的無人機航拍圖像檢測案例全流程實操 - 飛槳AI Studio星河社區 一、安裝環境 先準備新環境py>3.9 1.先cd到源代碼的根目錄下 2.pip install -r requirements.txt 3.python setup.py install 這一步需要看自己的GPU情況,去飛漿…

力扣HOT100之二叉樹:114. 二叉樹展開為鏈表

這道題自己嘗試著做了一下,感覺還是得用遞歸來做比較簡單,但是一直想的是用前序遍歷來構造鏈表,導致怎么做都不對,去看了下靈神的題解,然后問了下GPT,現在終于弄明白了。雖然構造出來的鏈表的排列順序是按照…

Spring Boot 注解 @ConditionalOnMissingBean是什么

一句話總結: ConditionalOnMissingBean 是 Spring Boot 提供的一個 條件注解(Conditional Annotation),意思是: 只有當 Spring 容器中 不存在 某個 Bean 時,當前的 Bean 或配置才會被加載。 這是一種典型的…

PyInstaller 如何在mac電腦上生成在window上可執行的exe文件

PyInstaller跨平臺打包限制 PyInstaller 無法直接從macOS生成Windows可執行文件,因為它需要訪問目標平臺的系統庫和Python環境來構建可執行文件。要在macOS上為Windows打包Python應用,需要通過以下方法之一: 方法一:使用虛擬機或…

零基礎設計模式——創建型模式 - 抽象工廠模式

第二部分:創建型模式 - 抽象工廠模式 (Abstract Factory Pattern) 我們已經學習了單例模式(保證唯一實例)和工廠方法模式(延遲創建到子類)。現在,我們來探討創建型模式中更為復雜和強大的一個——抽象工廠…

【通用智能體】Serper API 詳解:搜索引擎數據獲取的核心工具

Serper API 詳解:搜索引擎數據獲取的核心工具 一、Serper API 的定義與核心功能二、技術架構與核心優勢2.1 技術實現原理2.2 對比傳統方案的突破性優勢 三、典型應用場景與代碼示例3.1 SEO 監控系統3.2 競品廣告分析 四、使用成本與配額策略五、開發者注意事項六、替…

Flask-SQLAlchemy核心概念:模型類與數據庫表、類屬性與表字段、外鍵與關系映射

前置閱讀,關于Flask-SQLAlchemy支持哪些數據庫及基本配置,鏈接:Flask-SQLAlchemy_數據庫配置 摘要 本文以一段典型的 SQLAlchemy 代碼示例為引入,闡述以下核心概念: 模型類(Model Class) ? 數…

野火魯班貓(arrch64架構debian)從零實現用MobileFaceNet算法進行實時人臉識別(四)安裝RKNN Toolkit2

RKNN Toolkit2是用來將onnx模型轉成rknn專用模型,并可通過RKNN Toolkit Lite2或者RKNPU調用NPU進行加速計算的工具。 一開始我安裝很多次都無法成功安裝。后來跟售后技術對接,必須是PC平臺的Linux環境才可以。我的電腦是windows,所以我需要用…

基于深度學習的工件檢測系統設計與實現

在工業自動化領域,工件檢測一直是提高生產效率和產品質量的關鍵環節。傳統的人工檢測方法不僅效率低下,而且容易受到主觀因素的影響,導致誤判率較高。隨著深度學習技術的飛速發展,基于圖像識別的自動檢測系統逐漸成為研究熱點。今…

CyberSecAsia專訪CertiK首席安全官:區塊鏈行業亟需“安全優先”開發范式

近日,權威網絡安全媒體CyberSecAsia發布了對CertiK首席安全官Wang Tielei博士的專訪,雙方圍繞企業在進軍區塊鏈領域時所面臨的關鍵安全風險與防御策略展開深入探討。 Wang博士在采訪中指出,跨鏈橋攻擊、智能合約漏洞以及私鑰管理不當&#x…