我的目标是:
1)将视频文件从磁盘读入CMSampleBuffer
的数组。
2)对于每个样本缓冲区,将其转换为CIImage
。
3)在每个图像上,运行面部检测并获取面部边界。
4)将每个图像裁剪到该边界。
5)生成CIImage
s数组后,将其写回临时文件。
我有一个基本的实现,读取视频:
private func retrieveSampleBuffersForAsset(asset: AVAsset) -> [CMSampleBuffer] {
var samples: [CMSampleBuffer] = []
do {
let reader = try AVAssetReader(asset: asset)
if let videoTrack = asset.tracksWithMediaType(AVMediaTypeVideo).last {
let videoSettings: [String: AnyObject] = [kCVPixelBufferPixelFormatTypeKey as String : NSNumber(unsignedInt: kCVPixelFormatType_420YpCbCr8BiPlanarFullRange)]
let readerOutput = AVAssetReaderTrackOutput(track: videoTrack, outputSettings: videoSettings)
reader.addOutput(readerOutput)
reader.startReading()
while let sample = readerOutput.copyNextSampleBuffer() {
samples.append(sample)
}
}
} catch {
return []
}
return samples
}
然后我将缓冲区转换为CIImage
并将其裁剪为:
let detector = CIDetector(ofType: CIDetectorTypeFace, context: self.context, options: [CIDetectorImageOrientation: NSNumber(integer: 6)])
let features = detector.featuresInImage(self.inputImage)
guard let firstFeature = features.first else {
return
}
let cropFilter = CIFilter(name: "CICrop")
let cropRect: CIVector = CIVector(CGRect: firstFeature.bounds)
print(firstFeature.bounds)
cropFilter?.setValue(self.inputImage, forKey: "inputImage")
cropFilter?.setValue(cropRect, forKey: "inputRectangle")
self.outputImage = cropFilter?.outputImage
对于每个缓冲区,我创建一个图像,通过上面的自定义过滤器运行它并裁剪它。然后我将输出的裁剪图像并将其附加到列表中。
我被困的地方如下:
1)这是一种疯狂的记忆密集型。
如果没有应用程序崩溃,我无法处理单个视频,这是有道理的,因为如果为每个缓冲区创建一个图像,内存将会变得越来越大。
我在考虑解决这个问题的方法是裁剪图像,然后立即使用资产编写器将其写入磁盘,这样我就不必保留图像了。
2)这很慢。
人脸检测相对优化,但是当我尝试对我的相机胶卷中的所有视频资源执行此操作时,延迟非常明显。
理想情况下,我想使用GPUImage来读取文件并从那里获取样本缓冲区,但我不确定如何。