带卷积的垂直边缘检测给出透明图像作为 Swift 的结果

Vertical edge detection with convolution giving transparent image as result with Swift

我目前正在尝试编写一个函数来获取图像并应用 3x3 矩阵来过滤垂直边缘。为此,我正在使用 CoreImage 的 CIConvolution3X3 并传递用于在 Sobels 边缘检测中检测垂直边缘的矩阵。

代码如下:

func verticalEdgeFilter() -> UIImage {
    let inputUIImage = UIImage(named: imageName)!
    let inputCIImage = CIImage(image: inputUIImage)
    let context = CIContext()
    let weights: [CGFloat] = [1.0, 0.0, -1.0, 
                              2.0, 0.0, -2.0, 
                              1.0, 0.0, -1.0]
        
    let verticalFilter = CIFilter.convolution3X3()
    verticalFilter.inputImage = inputCIImage  
    verticalFilter.weights = CIVector(values: weights, count: 9)
        
    if let output = verticalFilter.outputImage{
        if let cgimg = context.createCGImage(output, from: output.extent) {
            let processedImage = UIImage(cgImage: cgimg)
            return processedImage
        }
    }
        
    print("returning original")
    return inputUIImage
}

现在我总是得到一个几乎完全透明的图像,带有 2 像素边框,如下所示:

Original

Screenshot of the result (border on the left side)

我是否遗漏了一些明显的东西,因为只有当矩阵的中心值为 0 时图像才透明。但是如果我在某些 webpage 上尝试相同的内核,它至少会产生可用的结果.设置偏见也会让整个事情崩溃,我不明白。

我还检查了有关此的 Apple 文档以及 CIFilter 网页,但我一无所获,所以如果有人可以帮助我解决这个问题或告诉我另一种方法,我将不胜感激在 Swift :)

将此卷积矩阵应用于完全不透明的图像将不可避免地产生完全透明的输出。这是因为内核值的总和为 0,所以在将 9 个相邻像素相乘并将它们相加后,结果的 alpha 分量将为 0。有两种处理方式:

  1. 使用 settingAlphaOne(in:) CIImage 辅助方法使输出不透明。
  2. 使用 CIConvolutionRGB3X3 滤镜,单独保留 alpha 分量并将内核仅应用于 RGB 分量。

至于 2 像素边界,这也是预期的,因为当内核应用于边界处的像素时,它仍然对所有 9 个像素进行采样,其中一些恰好落在图像边界之外(恰好 2 个像素远离每边的边界)。这些不存在的像素作为透明黑色像素 0x000000.

去除边框:

  1. 将图像钳制到一定程度以产生无限图像,其中边界像素重复到边界无限远处。您可以使用 CIClamp 过滤器或 CIImage 辅助函数 clampedToExtent()
  2. 应用卷积滤镜
  3. 将结果图像裁剪到输入图像范围。您可以使用 cropped(to:) CIImage 辅助函数。

经过这些更改后,您的代码可能会变成这样。

func verticalEdgeFilter() -> UIImage {
    let inputUIImage = UIImage(named: imageName)!
    let inputCIImage = CIImage(image: inputUIImage)!
    let context = CIContext()
    let weights: [CGFloat] = [1.0, 0.0, -1.0,
                              2.0, 0.0, -2.0,
                              1.0, 0.0, -1.0]

    let verticalFilter = CIFilter.convolution3X3()
    verticalFilter.inputImage = inputCIImage.clampedToExtent()
    verticalFilter.weights = CIVector(values: weights, count: 9)

    if var output = verticalFilter.outputImage{
        output = output
            .cropped(to: inputCIImage.extent)
            .settingAlphaOne(in: inputCIImage.extent)

        if let cgimg = context.createCGImage(output, from: output.extent) {
            let processedImage = UIImage(cgImage: cgimg)
            return processedImage
        }
    }

    print("returning original")
    return inputUIImage
}

如果您使用 convolutionRGB3X3 而不是 convolution3X3,则无需执行 settingAlphaOne

顺便说一句,如果您想使用卷积过滤器以及 250 个中的任何其他 CIFilter,请查看我刚刚发布的这个应用程序:https://apps.apple.com/us/app/filter-magic/id1594986951