我一直在使用Core Image的卷积滤波器进行一些工作,我注意到足够长的卷积链会导致意外的输出,我怀疑这是底层整数,浮点数或半浮点数类型的数值溢出的结果用于保存像素数据。这是特别出乎意料的,因为文档说每个卷积的输出值都是"clamped to the range between 0.0 and 1.0",因此,更大的值不应在过滤器的连续遍历中累积,但这似乎正在发生。
我这里有一些示例代码来演示这种意外行为。您应该能够将其原样粘贴到任何Xcode项目中,在其末尾设置一个断点,在适当的平台上运行它(我使用的是iPhone Xs,而不是模拟器),然后在中断时发生时,使用“快速外观”检查过滤器链。
import CoreImage
import CoreImage.CIFilterBuiltins
// --------------------
// CREATE A WHITE IMAGE
// --------------------
// the desired size of the image
let size = CGSize(width: 300, height: 300)
// create a pixel buffer to use as input; every pixel is bgra(0,0,0,0) by default
var pixelBufferOut: CVPixelBuffer?
CVPixelBufferCreate(kCFAllocatorDefault, Int(size.width), Int(size.height), kCVPixelFormatType_32BGRA, nil, &pixelBufferOut)
let input = pixelBufferOut!
// create an image from the input
let image = CIImage(cvImageBuffer: input)
// create a color matrix filter that will turn every pixel white
// bgra(0,0,0,0) becomes bgra(1,1,1,1)
let matrixFilter = CIFilter.colorMatrix()
matrixFilter.biasVector = CIVector(string: "1 1 1 1")
// turn the image white
matrixFilter.inputImage = image
let whiteImage = matrixFilter.outputImage!
// the matrix filter sets the image's extent to infinity
// crop it back to original size so Quick Looks can display the image
let cropped = whiteImage.cropped(to: CGRect(origin: .zero, size: size))
// ------------------------------
// CONVOLVE THE IMAGE SEVEN TIMES
// ------------------------------
// create a 3x3 convolution filter with every weight set to 1
let convolutionFilter = CIFilter.convolution3X3()
convolutionFilter.weights = CIVector(string: "1 1 1 1 1 1 1 1 1")
// 1
convolutionFilter.inputImage = cropped
let convolved = convolutionFilter.outputImage!
// 2
convolutionFilter.inputImage = convolved
let convolvedx2 = convolutionFilter.outputImage!
// 3
convolutionFilter.inputImage = convolvedx2
let convolvedx3 = convolutionFilter.outputImage!
// 4
convolutionFilter.inputImage = convolvedx3
let convolvedx4 = convolutionFilter.outputImage!
// 5
convolutionFilter.inputImage = convolvedx4
let convolvedx5 = convolutionFilter.outputImage!
// 6
convolutionFilter.inputImage = convolvedx5
let convolvedx6 = convolutionFilter.outputImage!
// 7
convolutionFilter.inputImage = convolvedx6
let convolvedx7 = convolutionFilter.outputImage!
// <-- put a breakpoint here
// when you run the code you can hover over the variables
// to see what the image looks like at various stages through
// the filter chain; you will find that the image is still white
// up until the seventh convolution, at which point it turns black
这是一个溢出问题的进一步证据是,如果我使用CIContext
将图像渲染到输出像素缓冲区,则我有机会通过{{ 1}}选项。在我的平台上,默认值为CIContextOption.workingFormat
,这意味着每个颜色通道都使用16位浮点数。相反,如果我使用CIFormat.RGBAh
来使用完整的32位浮点数,那么这个问题就消失了,因为溢出32位比花16位要花更多的时间。
我对这里正在发生的事情的见解是对的还是我完全不满意?是有关夹紧的文档是错误的还是过滤器的错误?
答案 0 :(得分:1)
文档似乎已过时。也许这是因为Core Image在iOS上默认使用8位无符号字节纹理格式,因为它们介于0.0
和1.0
之间。
使用浮点型格式时,不再钳制这些值,而是将其存储为内核返回的值。而且,由于您从白色(1.0
开始,并应用了7个连续的具有未归一化权重的卷积(1
而不是1/9
),因此每个通道的最终值是9 ^ 7 = 4,782,969,超出16位浮点范围。
为避免此类情况,应标准化卷积权重,以使它们的总和为1.0
。
顺便说一句:要创建一定大小的白色图像,只需执行以下操作:
let image = CIImage(color: .white).cropped(to: CGSize(width: 300, height: 300))
?