在 CMSampleBuffer 中获取非矩形轮廓内的 RGB 平均值的最快方法
Fastest way to get the RGB average inside of a non-rectangular contour in the CMSampleBuffer
我正在尝试从 AVCaptureVideoDataOutput 获取在帧中面部标志区域(将其视为面部轮廓)上生成的非矩形多边(闭合)轮廓内的 RGB 平均值。我目前有以下代码,
let landmarkPath = CGMutablePath()
let landmarkPathPoints = landmark.normalizedPoints
.map({ landmarkPoint in
CGPoint(
x: landmarkPoint.y * faceBoundingBox.height + faceBoundingBox.origin.x,
y: landmarkPoint.x * faceBoundingBox.width + faceBoundingBox.origin.y)
})
landmarkPath.addLines(between: landmarkPathPoints)
landmarkPath.closeSubpath()
let averageFilter = CIFilter(name: "CIAreaAverage", parameters: [kCIInputImageKey: frame, kCIInputExtentKey: landmarkPath])!
let outputImage = averageFilter.outputImage!
但是,它目前抛出 *** Terminating app due to uncaught exception 'NSInvalidArgumentException',原因:'-[__NSCFType CGRectValue]: unrecognized selector sent to instance 0x283a57a80' terminating with uncaught exception of输入 NSException。我怀疑这是因为 kCIInputExtentKey 不是一个合适的 CIVector 矩形对象。有没有什么办法解决这一问题?如何为 CIAreaAverage 过滤器定义非矩形区域?如果不可能,获得整个感兴趣区域的平均 RGB 的最有效方法是什么?
提前致谢!
如果您可以使轮廓外的所有像素透明,那么您可以使用 CIKmeans
过滤器,其中 inputCount
等于 1
并且 inputExtent
设置为获取轮廓内区域平均颜色的帧(过滤器的输出将包含 1 像素图像,像素的颜色就是您要查找的颜色)。
现在,要使轮廓外的所有像素透明,您可以这样做:
- 创建蒙版图像,但将轮廓内部的所有像素设置为白色,外部设置为黑色(将背景设置为黑色并用白色填充路径)。
- 使用
CIBlendWithMask
过滤器,其中:
inputBackgroundImage
是完全透明(清晰)的图像
inputImage
为原帧
inputMaskImage
是你上面创建的遮罩
该过滤器的输出将为您提供轮廓外所有像素完全透明的图像。现在您可以按照开头所述使用 CIKMeans
过滤器。
顺便说一句,如果您想使用 230 个过滤器中的每一个,请查看此应用程序:https://apps.apple.com/us/app/filter-magic/id1594986951
更新:
CIFilters 只能与 CIImages 一起使用。所以蒙版图像也必须是 CIImage。
一种方法是从包含遮罩的 CAShapeLayer 创建一个 CGImage,然后从中创建 CIImage。代码如下所示:
// Create the closed contour path from points
let path = CGMutablePath()
path.addLines(between: points)
path.closeSubpath()
// Create CAShapeLayer matching the dimensions of the input frame
let layer = CAShapeLayer()
layer.frame = frame.extent // Assuming frame is the input CIImage with the face
// Set background and fill color and set the path
layer.fillColor = UIColor.white.cgColor
layer.backgroundColor = UIColor.black.cgColor
layer.path = path
// Render the contents of the CAShapeLayer to CGImage
let width = Int(layer.bounds.width)
let height = Int(layer.bounds.height)
let context = CGContext(data: nil,
width: width,
height: height,
bitsPerComponent: 8,
bytesPerRow: 4 * width,
space: CGColorSpaceCreateDeviceRGB(),
bitmapInfo: CGImageAlphaInfo.premultipliedLast.rawValue)!
layer.render(in: context)
let cgImage = context.makeImage()!
// Create CIImage out of it
let ciImage = CIImage(cgImage: cgImage)
// To create clear background CIImage just do this:
let bgImage = CIImage.clear.cropped(to: frame.extent)
我正在尝试从 AVCaptureVideoDataOutput 获取在帧中面部标志区域(将其视为面部轮廓)上生成的非矩形多边(闭合)轮廓内的 RGB 平均值。我目前有以下代码,
let landmarkPath = CGMutablePath()
let landmarkPathPoints = landmark.normalizedPoints
.map({ landmarkPoint in
CGPoint(
x: landmarkPoint.y * faceBoundingBox.height + faceBoundingBox.origin.x,
y: landmarkPoint.x * faceBoundingBox.width + faceBoundingBox.origin.y)
})
landmarkPath.addLines(between: landmarkPathPoints)
landmarkPath.closeSubpath()
let averageFilter = CIFilter(name: "CIAreaAverage", parameters: [kCIInputImageKey: frame, kCIInputExtentKey: landmarkPath])!
let outputImage = averageFilter.outputImage!
但是,它目前抛出 *** Terminating app due to uncaught exception 'NSInvalidArgumentException',原因:'-[__NSCFType CGRectValue]: unrecognized selector sent to instance 0x283a57a80' terminating with uncaught exception of输入 NSException。我怀疑这是因为 kCIInputExtentKey 不是一个合适的 CIVector 矩形对象。有没有什么办法解决这一问题?如何为 CIAreaAverage 过滤器定义非矩形区域?如果不可能,获得整个感兴趣区域的平均 RGB 的最有效方法是什么?
提前致谢!
如果您可以使轮廓外的所有像素透明,那么您可以使用 CIKmeans
过滤器,其中 inputCount
等于 1
并且 inputExtent
设置为获取轮廓内区域平均颜色的帧(过滤器的输出将包含 1 像素图像,像素的颜色就是您要查找的颜色)。
现在,要使轮廓外的所有像素透明,您可以这样做:
- 创建蒙版图像,但将轮廓内部的所有像素设置为白色,外部设置为黑色(将背景设置为黑色并用白色填充路径)。
- 使用
CIBlendWithMask
过滤器,其中:inputBackgroundImage
是完全透明(清晰)的图像inputImage
为原帧inputMaskImage
是你上面创建的遮罩
该过滤器的输出将为您提供轮廓外所有像素完全透明的图像。现在您可以按照开头所述使用 CIKMeans
过滤器。
顺便说一句,如果您想使用 230 个过滤器中的每一个,请查看此应用程序:https://apps.apple.com/us/app/filter-magic/id1594986951
更新:
CIFilters 只能与 CIImages 一起使用。所以蒙版图像也必须是 CIImage。 一种方法是从包含遮罩的 CAShapeLayer 创建一个 CGImage,然后从中创建 CIImage。代码如下所示:
// Create the closed contour path from points
let path = CGMutablePath()
path.addLines(between: points)
path.closeSubpath()
// Create CAShapeLayer matching the dimensions of the input frame
let layer = CAShapeLayer()
layer.frame = frame.extent // Assuming frame is the input CIImage with the face
// Set background and fill color and set the path
layer.fillColor = UIColor.white.cgColor
layer.backgroundColor = UIColor.black.cgColor
layer.path = path
// Render the contents of the CAShapeLayer to CGImage
let width = Int(layer.bounds.width)
let height = Int(layer.bounds.height)
let context = CGContext(data: nil,
width: width,
height: height,
bitsPerComponent: 8,
bytesPerRow: 4 * width,
space: CGColorSpaceCreateDeviceRGB(),
bitmapInfo: CGImageAlphaInfo.premultipliedLast.rawValue)!
layer.render(in: context)
let cgImage = context.makeImage()!
// Create CIImage out of it
let ciImage = CIImage(cgImage: cgImage)
// To create clear background CIImage just do this:
let bgImage = CIImage.clear.cropped(to: frame.extent)