使用CIFilter randomGenerator()生成胶片颗粒出现网格图案的问题排查
问题:CoreImage实现复古胶片颗粒时出现规律性横竖网格异常
仿照Apple官方文档实现复古胶片颗粒效果,但叠加颗粒并裁剪至原图尺寸时,出现规律性横竖网格状异常图案。该问题在不同尺寸图像中均存在,图像越大图案越明显,小尺寸(如100x100)图像无此问题。已将颗粒效果应用于纯色图展示异常:纯色背景上可见明显的横竖网格状颗粒拼接痕迹。
以下是颗粒生成函数代码:
private func applyRandomGrain() { /// generate grain & convert to grayscale let grainGenerator = CIFilter.randomGenerator() let grayscale = CIFilter.minimumComponent() grayscale.inputImage = grainGenerator.outputImage /// adjust brightness of grain image let exposureFilter = CIFilter.exposureAdjust() exposureFilter.inputImage = grayscale.outputImage exposureFilter.ev = Float(settings.grainExposure) /// adjust intensity of grain image with opacity let alphaVector = CIVector(x: 0, y: 0, z: 0, w: settings.grainOpacity) let alphaFilter = CIFilter.colorMatrix() alphaFilter.inputImage = exposureFilter.outputImage alphaFilter.aVector = alphaVector guard let inputImage = outputImage else { return } /// apply grain image to original image let composite = CIFilter.sourceOverCompositing() composite.backgroundImage = inputImage composite.inputImage = alphaFilter.outputImage guard let imageWithGrain = composite.outputImage else { return } outputImage = imageWithGrain.cropped(to: inputImage) }
原因分析
CIFilter.randomGenerator()生成的是周期性重复的随机纹理,它的默认输出是固定尺寸的tile(通常为512x512)。当叠加到比tile大的图像上时,CoreImage会自动重复这个tile来覆盖整个区域,从而产生明显的网格状拼接痕迹——这就是你看到的横竖异常图案。小尺寸图像因为小于tile尺寸,不会触发重复,因此无此问题。
解决方法
让随机纹理生成器直接输出与原图尺寸匹配的纹理,从源头避免tile重复。修改后的代码如下:
private func applyRandomGrain() { guard let inputImage = outputImage else { return } /// 生成匹配原图尺寸的随机颗粒并转为灰度 let grainGenerator = CIFilter.randomGenerator() let extent = inputImage.extent // 缩放随机纹理到原图尺寸 grainGenerator.transform = CGAffineTransform(scaleX: extent.width, y: extent.height) let grayscale = CIFilter.minimumComponent() grayscale.inputImage = grainGenerator.outputImage /// 调整颗粒亮度 let exposureFilter = CIFilter.exposureAdjust() exposureFilter.inputImage = grayscale.outputImage exposureFilter.ev = Float(settings.grainExposure) /// 调整颗粒透明度 let alphaVector = CIVector(x: 0, y: 0, z: 0, w: settings.grainOpacity) let alphaFilter = CIFilter.colorMatrix() alphaFilter.inputImage = exposureFilter.outputImage alphaFilter.aVector = alphaVector /// 叠加颗粒到原图 let composite = CIFilter.sourceOverCompositing() composite.backgroundImage = inputImage composite.inputImage = alphaFilter.outputImage guard let imageWithGrain = composite.outputImage else { return } outputImage = imageWithGrain.cropped(to: inputImage) }
关键修改说明
- 提前获取原图的
extent(范围),计算缩放变换并赋值给randomGenerator的transform属性,让随机纹理直接覆盖整个原图区域,避免后续重复拼接。 - 将获取
inputImage的逻辑前置,确保生成颗粒时就能匹配原图尺寸,流程更合理。
如果不想调整生成器变换,也可以用CIAffineClamp滤镜包裹颗粒纹理,防止CoreImage重复tile,但直接调整生成器变换是更高效的方案。
内容的提问来源于stack exchange,提问作者Trevor
相关产品推荐
相关产品推荐

