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中起作用,但在内核中不起作用。
有什么想法发生了什么?
解决了它。
核心图像“有帮助”将所有内容转换为光线性色彩空间,因为某些过滤器得到了帮助,如果您想要真正的颜色,则必须明确禁用它。 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中完成,而不是在应用程序中的其他任何地方或所有其他类型的图像处理中完成;这似乎是一个非常不一致和隐藏的“功能”。