自适应阈值CIKernel/CIFilter iOS

8

我已经进行了广泛的研究,以寻找在iOS上执行自适应阈值处理的内核。不幸的是,我不理解内核语言或其背后的逻辑。下面,我找到了一个执行阈值处理的例程(https://gist.github.com/xhruso00/a3f8a9c8ae7e33b8b23d)。

static NSString * const kKernelSource = @"kernel vec4 thresholdKernel(sampler image)\n"
"{\n"
"  float inputThreshold = 0.05;\n"
"  float pass = 1.0;\n"
"  float fail = 0.0;\n"
"  const vec4   vec_Y = vec4( 0.299, 0.587, 0.114, 0.0 );\n"
"  vec4  src = unpremultiply( sample(image, samplerCoord(image)) );\n"
"  float Y = dot( src, vec_Y );\n"
"  src.rgb = vec3( compare( Y - inputThreshold, fail, pass));\n"
"  return premultiply(src);\n"
"}";

能否将其改写为自适应阈值卷积核? 我提供给它的图像已经变成了黑白并已经被模糊处理。 你可以指导我去哪些资源吗? 我想坚持使用CoreImage,因为我的整个堆栈都是基于它构建的。

编辑:我正在尝试实现的最佳示例/参考已在GPUImage的GPUImageAdaptiveThresholdFilter中实现 - https://github.com/BradLarson/GPUImage/blob/c5f0914152419437869c35e29858773b1a06083c/framework/Source/GPUImageAdaptiveThresholdFilter.m


我使用描述、金属和现代方法更新了我的代码 https://gist.github.com/xhruso00/a3f8a9c8ae7e33b8b23d 我受到下面代码的启发,但下面的代码不尊重 alpha 预乘(透明区域将变成黑色或白色)。 - Marek H
3个回答

6

这看起来怎么样:我使用了CoreImage CIBoxBlur(尽管专用卷积滤镜可能更快),并将其输出传递到我的现有阈值滤镜中。

class AdaptiveThresholdFilter: CIFilter
{
    var inputImage : CIImage?


    var thresholdKernel =  CIColorKernel(string:
    "kernel vec4 thresholdFilter(__sample image, __sample threshold)" +
    "{" +
    "   float imageLuma = dot(image.rgb, vec3(0.2126, 0.7152, 0.0722));" +
    "   float thresholdLuma = dot(threshold.rgb, vec3(0.2126, 0.7152, 0.0722));" +

    "   return vec4(vec3(step(imageLuma, thresholdLuma)), 1.0);" +
    "}"
    )


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

        let blurred = inputImage.imageByApplyingFilter("CIBoxBlur",
               withInputParameters: [kCIInputRadiusKey: 9])

        let extent = inputImage.extent
        let arguments = [inputImage, blurred]

        return thresholdKernel.applyWithExtent(extent, arguments: arguments)
    }
}

我发现了这张图片和以下代码:

let page = CIImage(image: UIImage(named: "son1.gif")!)

let filter = AdaptiveThresholdFilter()

filter.inputImage = page

let final = filter.outputImage

我得到了这个结果:

输入图像描述


祝好!
西蒙

谢谢你帮我入门。我正在寻找更类似于http://homepages.inf.ed.ac.uk/rbf/HIPR2/images/son1adp2.gif的东西。 - mmackh

5

Simon的滤镜是实现所需效果的正确方法,但是您需要修改一些内容。

首先,交换imageLumathresholdLuma的顺序,因为我们希望黑色字母保持黑色而不是相反。此外,您应该添加一个常量(我选择了0.01)以去除噪声。

    var thresholdKernel =  CIColorKernel(string:
    "kernel vec4 thresholdFilter(__sample image, __sample threshold)" +
        "{" +
        "   float imageLuma = dot(image.rgb, vec3(0.2126, 0.7152, 0.0722));" +
        "   float thresholdLuma = dot(threshold.rgb, vec3(0.2126, 0.7152, 0.0722));" +
        "   return vec4(vec3(step(thresholdLuma, imageLuma+0.001)), 1);"     
    "}"

override var outputImage: CIImage! {
    guard let inputImage = inputImage,
        let thresholdKernel = thresholdKernel else {
        return nil
    }
    let blurred = inputImage.applyingFilter("CIBoxBlur", withInputParameters: [kCIInputRadiusKey: 5]) // block size
    let extent = inputImage.extent
    let arguments = [inputImage, blurred]
    return thresholdKernel.apply(withExtent: extent, arguments: arguments)
}

只需使用苹果的Core Image,无需安装任何外部库,就可以得到以下结果 :)

enter image description here

当然,您可以稍微调整常量和块大小的值。


Pascal,感谢您的回答。有没有想法在CoreImage中实现这个功能?https://github.com/ctodobom/OpenNoteScanner/blob/master/app/src/main/java/com/todobom/opennotescanner/ImageProcessor.java#L409 - mmackh
你提到了一个常量0.01,但是实际上是0.001,这是一个错误吗?此外,你将这个常量应用于图像亮度还是阈值亮度(假设我想要白色字体和黑色背景)。 - Pochi
0.01和0.001似乎没有太大的区别。您可以尝试不同的值来适应您的需求。 - triiiiista

4

您可以使用CIColorThresholdOtsu核心图像过滤器。


1
这个较新的答案也非常有用!如果开发人员编写了一个阈值算法,只想应用阈值,则CIColorThreshold也值得一看:https://cifilter.io/CIColorThreshold/ - Rethunk

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接