2

tl; dr:当我在 Swift 中对具有特定阈值的图像进行阈值处理时,我得到了干净的分割(并在 Matlab 中仔细检查了它是否完全匹配),但是当我在 Core Image 内核中进行处理时,它并没有干净地分割。我的内核中有错误吗?

我正在尝试使用 Core Image 内核设置阈值。我的代码看起来很简单:

class ThresholdFilter: CIFilter
{
    var inputImage : CIImage?
    var threshold: Float = 0.554688 // This is set to a good value via Otsu's method

    var thresholdKernel =  CIColorKernel(source:
        "kernel vec4 thresholdKernel(sampler image, float threshold) {" +
        "  vec4 pixel = sample(image, samplerCoord(image));" +
        "  const vec3 rgbToIntensity = vec3(0.114, 0.587, 0.299);" +
        "  float intensity = dot(pixel.rgb, rgbToIntensity);" +
        "  return intensity < threshold ? vec4(0, 0, 0, 1) : vec4(1, 1, 1, 1);" +
        "}")

    override var outputImage: CIImage! {
        guard let inputImage = inputImage,
            let thresholdKernel = thresholdKernel else {
                return nil
        }

        let extent = inputImage.extent
        let arguments : [Any] = [inputImage, threshold]
        return thresholdKernel.apply(extent: extent, arguments: arguments)
    }
}

像这个简单的叶子这样的图像: 在此处输入图像描述 得到适当的阈值: 在此处输入图像描述

但是有些图像,像这样(背景更混乱): 在此处输入图像描述 变成垃圾: 在此处输入图像描述

我认为这不仅仅是选择一个糟糕的阈值的问题,因为我可以在 Matlab 中使用这个完全相同的阈值并获得清晰的分割: 在此处输入图像描述

为了仔细检查,我在纯 Swift 中“重做”内核outputImage,只是打印到控制台:

let img: CGImage = inputImage.cgImage!
let imgProvider: CGDataProvider = img.dataProvider!
let imgBitmapData: CFData = imgProvider.data!
var imgBuffer = vImage_Buffer(data: UnsafeMutableRawPointer(mutating: CFDataGetBytePtr(imgBitmapData)), height: vImagePixelCount(img.height), width: vImagePixelCount(img.width), rowBytes: img.bytesPerRow)

for i in 0...img.height {
    for j in 0...img.width {
        let test = imgBuffer.data.load(fromByteOffset: (i * img.width + j) * 4, as: UInt32.self)

        let r = Float((test >> 16) & 255) / 256
        let g = Float((test >> 8) & 255) / 256
        let b = Float(test & 255) / 256
        let intensity = 0.114 * r + 0.587 * g + 0.299 * b

        print(intensity > threshold ? "1" : "0", terminator: "")
    }
    print("")
}

这会在 0 和 1 中打印出清晰分割的图像。我无法将其缩小到足以一次将其全部显示在屏幕上,但您可以看到叶子上的孔被清楚地分割: 在此处输入图像描述

我担心 Matlab 和内核之间的像素强度可能不同(因为 RGB 到强度可以以不同的方式完成),所以我使用这种控制台打印方法来检查不同像素的确切强度,它们都匹配强度我在 Matlab 中看到相同的图像。由于我在 Swift 和内核之间使用相同的点积,我不知道为什么这个阈值在 Swift 和 Matlab 中有效,但在内核中无效。

有什么想法吗?

4

1 回答 1

2

解决了。

Core Image “有用地”将所有内容转换为光线性色彩空间,因为某些过滤器由此得到帮助,如果你想要真实的颜色,你必须明确禁用它。https://developer.apple.com/library/content/documentation/GraphicsImaging/Conceptual/CoreImaging/ci_performance/ci_performance.html#//apple_ref/doc/uid/TP30001185-CH10-SW7

您可以在初始化传递给过滤器的 CIImage 时这样做:

filter.inputImage = CIImage(image: image!, options: [kCIImageColorSpace: NSNull()])

我不知道为什么这只在 CIFilters 中完成,而不是在应用程序的其他任何地方或所有其他类型的图像处理中完成;这似乎是一个非常不一致和隐藏的“功能”。

于 2018-01-01T21:58:56.003 回答