我希望AVCaptureVideoDataOutput中的整个图像的平均像素值,我正在捕捉图像并循环显示像素。
我想知道使用GPU / openGL是否有更有效的方法来实现这一点,因为这是一个可并行化的图像处理任务。 (也许是一个沉重的高斯模糊,并读取中心像素值?)
一个特定的要求是使用高水平的平均值来获得高精度结果。请注意下面的CGFloat结果。
编辑:添加了CIAreaAverage的实现,如Simon所示。它被useGPU
布尔分开。
func captureOutput(captureOutput: AVCaptureOutput!, didOutputSampleBuffer sampleBuffer: CMSampleBuffer!, fromConnection connection: AVCaptureConnection!) {
var redmean:CGFloat = 0.0;
var greenmean:CGFloat = 0.0;
var bluemean:CGFloat = 0.0;
if (useGPU) {
let pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer)
let cameraImage = CIImage(CVPixelBuffer: pixelBuffer!)
let filter = CIFilter(name: "CIAreaAverage")
filter!.setValue(cameraImage, forKey: kCIInputImageKey)
let outputImage = filter!.valueForKey(kCIOutputImageKey) as! CIImage!
let ctx = CIContext(options:nil)
let cgImage = ctx.createCGImage(outputImage, fromRect:outputImage.extent)
let rawData:NSData = CGDataProviderCopyData(CGImageGetDataProvider(cgImage))!
let pixels = UnsafePointer<UInt8>(rawData.bytes)
let bytes = UnsafeBufferPointer<UInt8>(start:pixels, count:rawData.length)
var BGRA_index = 0
for pixel in UnsafeBufferPointer(start: bytes.baseAddress, count: bytes.count) {
switch BGRA_index {
case 0:
bluemean = CGFloat (pixel)
case 1:
greenmean = CGFloat (pixel)
case 2:
redmean = CGFloat (pixel)
case 3:
break
default:
break
}
BGRA_index++
}
} else {
let imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer)
CVPixelBufferLockBaseAddress(imageBuffer!, 0)
let baseAddress = CVPixelBufferGetBaseAddressOfPlane(imageBuffer!, 0)
let bytesPerRow = CVPixelBufferGetBytesPerRow(imageBuffer!)
let width = CVPixelBufferGetWidth(imageBuffer!)
let height = CVPixelBufferGetHeight(imageBuffer!)
let colorSpace = CGColorSpaceCreateDeviceRGB()
let bitmapInfo = CGBitmapInfo(rawValue: CGImageAlphaInfo.PremultipliedFirst.rawValue).rawValue | CGBitmapInfo.ByteOrder32Little.rawValue
let context = CGBitmapContextCreate(baseAddress, width, height, 8, bytesPerRow, colorSpace, bitmapInfo)
let imageRef = CGBitmapContextCreateImage(context)
CVPixelBufferUnlockBaseAddress(imageBuffer!, 0)
let data:NSData = CGDataProviderCopyData(CGImageGetDataProvider(imageRef))!
let pixels = UnsafePointer<UInt8>(data.bytes)
let bytes = UnsafeBufferPointer<UInt8>(start:pixels, count:data.length)
var redsum:CGFloat = 0
var greensum:CGFloat = 0
var bluesum:CGFloat = 0
var BGRA_index = 0
for pixel in UnsafeBufferPointer(start: bytes.baseAddress, count: bytes.count) {
switch BGRA_index {
case 0:
bluesum += CGFloat (pixel)
case 1:
greensum += CGFloat (pixel)
case 2:
redsum += CGFloat (pixel)
case 3:
//alphasum += UInt64(pixel)
break
default:
break
}
BGRA_index += 1
if BGRA_index == 4 { BGRA_index = 0 }
}
redmean = redsum / CGFloat(bytes.count)
greenmean = greensum / CGFloat(bytes.count)
bluemean = bluesum / CGFloat(bytes.count)
}
print("R:\(redmean) G:\(greenmean) B:\(bluemean)")
答案 0 :(得分:4)
CIAreaAverage过滤器性能不佳的问题和原因是缺少输入范围的定义。 因此,滤镜的输出与输入图像的大小相同,因此您可以在完整的图像上循环而不是1×1像素的图像。因此,执行所需的时间与初始版本相同。
如CIAreaAverage文档中所述,您可以指定 inputExtent 参数。 如何在swift中完成此操作可以在类似问题的this answer中找到:
let cameraImage = CIImage(CVPixelBuffer: pixelBuffer!)
let extent = cameraImage.extent
let inputExtent = CIVector(x: extent.origin.x, y: extent.origin.y, z: extent.size.width, w: extent.size.height)
let filter = CIFilter(name: "CIAreaAverage", withInputParameters: [kCIInputImageKey: cameraImage, kCIInputExtentKey: inputExtent])!
let outputImage = filter.outputImage!
如果您希望获得更高的性能,可以确保重复使用CIContext,而不是为每个捕获的帧重新创建它。
答案 1 :(得分:2)
核心图像过滤器可以完成这项工作CIAreaAverage,它会返回包含感兴趣区域的平均颜色的单像素图像(您感兴趣的区域将是整个图像)。
仅供参考,我有一篇讨论applying Core Image filters to a live camera feed here的博客文章。简而言之,过滤器需要一个CIImage,您可以根据captureImage
在sampleBuffer
内创建:
let pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer)
let cameraImage = CIImage(CVPixelBuffer: pixelBuffer!)
...而且cameraImage
您需要传递给CIAreaAverage
。
干杯,
西蒙
答案 2 :(得分:1)
如果您将数据作为浮点值,则可以使用
func vDSP_meanv
如果这不是一个选项,请尝试以某种方式处理数据,以便优化程序可以使用SIMD指令。我没有任何好的配方,对我来说这是一次试错练习,但代码的某些重新排列可能比其他人提供更好的机会。例如,我会尝试从循环中删除开关。 SIMD将对您的计算进行矢量化,此外,您可以通过GCD使用多线程处理,在单独的核心上处理每一行图像数据......