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 中有效,但在内核中无效。
有什么想法吗?