Swift使用CIConvolution3X3做垂直边缘检测输出透明图像问题求解
问题原因
- 你使用的垂直Sobel算子权重总和为0,卷积计算后大部分像素值落在负数区间,CoreImage默认会将小于0的颜色值裁剪为0,同时alpha通道会参与卷积计算,最终出现大面积透明的表现。
- 输入图像未做边缘扩展,3x3卷积核无法覆盖图像边缘1像素外的区域,所以只有边缘位置能得到非零计算结果。
- 设置bias崩溃大概率是参数类型传错,CIConvolution3X3的
bias属性要求传入CIVector类型,直接传入数值会触发类型错误崩溃。
修复方案
func verticalEdgeFilter() -> UIImage { guard let inputUIImage = UIImage(named: imageName), let inputCIImage = CIImage(image: inputUIImage) else { return UIImage() } let context = CIContext(options: [.useSoftwareRenderer: false]) // 第一步:先对输入图像做边缘延展,避免卷积后边缘丢失 guard let clampFilter = CIFilter(name: "CIAffineClamp") else { return inputUIImage } clampFilter.setValue(inputCIImage, forKey: kCIInputImageKey) clampFilter.setValue(NSValue(cgAffineTransform: .identity), forKey: "inputTransform") guard let clampedImage = clampFilter.outputImage else { return inputUIImage } // Sobel垂直边缘检测算子 let weights: [CGFloat] = [1.0, 0.0, -1.0, 2.0, 0.0, -2.0, 1.0, 0.0, -1.0] guard let verticalFilter = CIFilter.convolution3X3() else { return inputUIImage } verticalFilter.inputImage = clampedImage verticalFilter.weights = CIVector(values: weights, count: 9) // 加bias把输出值域从[-1,1]偏移到[0,1],避免负数被裁剪 verticalFilter.bias = CIVector(x: 0.5, y: 0.5, z: 0.5, w: 0) guard let output = verticalFilter.outputImage, let cgimg = context.createCGImage(output, from: inputCIImage.extent) else { print("returning original") return inputUIImage } return UIImage(cgImage: cgimg) }
额外优化建议
- 如果只需要检测灰度边缘,可以先把输入图像通过
CIPhotoEffectNoir滤镜转成灰度图,再做卷积计算,能减少无效计算量。 - 也可以直接使用CoreImage内置的
CIEdges滤镜,只需要调整inputIntensity参数就能直接得到边缘检测结果,不需要手动配置卷积核。
内容的提问来源于stack exchange,提问作者D. Schelor
相关产品推荐
相关产品推荐

