【问题标题】:Binarize Picture with Core Image on iOS在 iOS 上使用 Core Image 对图片进行二值化
【发布时间】:2017-07-04 09:11:42
【问题描述】:

我想知道是否可以使用 Core Image 对图像进行二值化(仅转换为黑白)?

我使用 OpenCV 和 GPUImage 制作了它,但如果可能的话,我更希望它使用 Apple Core Image

【问题讨论】:

  • (原谅我。我发布了这个评论作为答案。哎呀。)是的。检查 CIPhotoEffectMono (developer.apple.com/library/content/documentation/…)。我还编写了一个自定义的 CIColorKernel(GLSL 代码),可以“淡化”为黑白。我会在几分钟后将两者作为答案发布。

标签: swift core-image


【解决方案1】:

是的。您至少有两个选择,CIPhotoEffectMono 或小型自定义 CIColorKernel。

CIPhotoEffectMono:

func createMonoImage(image:UIImage) -> UIImage {
    let filter = CIFilter(name: "CIPhotoEffectMono")
    filter!.setValue(CIImage(image: image), forKey: "inputImage")
    let outputImage = filter!.outputImage
    let cgimg = ciCtx.createCGImage(outputImage!, from: (outputImage?.extent)!)
    return UIImage(cgImage: cgimg!)
}

注意,我写的很快,你可能需要收紧零回报。

CIColorKernel:

FadeToBW GLSL(0.0 因子全彩,1.0 因子无颜色):

kernel vec4 fadeToBW(__sample s, float factor) {
    vec3 lum = vec3(0.299,0.587,0.114);
    vec3 bw = vec3(dot(s.rgb,lum));
    vec3 pixel = s.rgb + (bw - s.rgb) * factor;
    return vec4(pixel,s.a);
}

下面的代码将其作为一个名为 FadeToBW.cikernel 的文件打开。您也可以将其作为字符串直接发布到openKernelFile 调用中。

Swift 代码:

func createMonoImage(image:UIImage, inputColorFade:NSNumber) -> UIImage {
    let ciKernel = CIColorKernel(string: openKernelFile("FadeToBW"))
    let extent = image.extent
    let arguments = [image, inputColorFade]        
    let outputImage = ciKernel.applyWithExtent(extent, arguments: arguments)
    let cgimg = ciCtx.createCGImage(outputImage!, from: (outputImage?.extent)!)
    return UIImage(cgImage: cgimg!)
}

再次,添加一些守卫等。

【讨论】:

  • 您好 dfd,非常感谢您的快速回复!在尝试自定义内核解决方案时,我在应用内核后收到“致命错误:在展开可选值时意外发现 nil” - 你知道吗,为什么?
  • 我使用了以下代码:let ciKernel = CIColorKernel(string: "...")! let image = CIImage.init(image: inputImage)! let extent = image.extent let arguments = [inputImage, inputColorFade] let outputImage = ciKernel.apply(withExtent: extent, arguments: arguments)! let context = CIContext(options:nil) let cgimg = context.createCGImage(outputImage, from: (outputImage.extent)) return UIImage(cgImage: cgimg!)
  • 啊。我的错。从工作应用程序复制的未经测试的代码。这不是 inputImage 而是 image。我正在编辑我的答案。出于测试目的,在 apply(withExtent:arguments) 上放置一个断点并检查其他内容是否为 nil。
【解决方案2】:

您可以为此使用 MetalPerformanceShaders。和 CIImageProcessingKernel。 https://developer.apple.com/documentation/coreimage/ciimageprocessorkernel

这是所需类的代码。

 class ThresholdImageProcessorKernel: CIImageProcessorKernel {
    static let device = MTLCreateSystemDefaultDevice()
    override class func process(with inputs: [CIImageProcessorInput]?, arguments: [String : Any]?, output: CIImageProcessorOutput) throws {
        guard
            let device = device,
            let commandBuffer = output.metalCommandBuffer,
            let input = inputs?.first,
            let sourceTexture = input.metalTexture,
            let destinationTexture = output.metalTexture,
            let thresholdValue = arguments?["thresholdValue"] as? Float else  {
                return
        }

        let threshold = MPSImageThresholdBinary(
            device: device,
            thresholdValue: thresholdValue,
            maximumValue: 1.0,
            linearGrayColorTransform: nil)

        threshold.encode(
            commandBuffer: commandBuffer,
            sourceTexture: sourceTexture,
            destinationTexture: destinationTexture)
    }
}

你可以这样使用它:

    let context = CIContext(options: nil)

            if let binaryCIImage = try? ThresholdImageProcessorKernel.apply(
                withExtent: croppedCIImage.extent,
                inputs: [croppedCIImage],
                arguments: ["thresholdValue": Float(0.2)]) {
                if let cgImage = context.createCGImage(binaryCIImage, from: binary.extent) {
                    DispatchQueue.main.async {
                        let resultingImage = UIImage(cgImage: cgImage)
                        if resultingImage.size.width > 100 {
                            print("Received an image \(resultingImage.size)")
                        }
                    }
                }
            }

【讨论】:

  • 虽然我需要将 binary.extent 更改为 binaryCIImage.extent,但对我来说效果很好——希望这是正确的吗?此外,看起来很疯狂,Apple 的开发人员没有想到包含这样的阈值过滤器,尽管实际上有数百个过滤器可以满足您可能需要的一切:/
  • 非常适合我。谢谢
【解决方案3】:

我已经成功地使用 CIPhotoEffectMono 或等效方法将其转换为灰度,然后使用具有高得离谱的 inputContrast 数(我使用 10000)的 CIColorControls。这有效地使其成为黑白并因此二值化。对于那些不想弄乱自定义内核的人很有用。

此外,您可以使用 Apple 的“色度键”过滤器之类的示例,该过滤器使用 Hue 进行过滤,但您无需查看 Hue,只需给出二值化数据的规则(即:何时将 RGB 全部设置为 1.0 以及何时设置为 0.0)。

https://developer.apple.com/documentation/coreimage/applying_a_chroma_key_effect

【讨论】:

    【解决方案4】:

    从谷歌搜索中找到了这个帖子,我想我会提到从 iOS 14 和 OSX 11.0 开始,CoreImage 包括 CIColorThresholdCIColorThresholdOtsu 过滤器(后者使用 Otsu 的方法从图像中计算阈值直方图)

    见:

    https://cifilter.io/CIColorThreshold/

    https://cifilter.io/CIColorThresholdOtsu/

    【讨论】:

      【解决方案5】:
      let outputImage = inputImage.applyingFilter("CIColorMonochrome",
                                  parameters: [kCIInputColorKey: CIColor.white])
      

      如果您想使用 250 个 CIFilter 中的每一个,请查看此应用:https://apps.apple.com/us/app/filter-magic/id1594986951

      【讨论】:

        猜你喜欢
        • 2012-06-11
        • 2013-02-14
        • 2014-10-29
        • 1970-01-01
        • 2021-02-28
        • 1970-01-01
        • 2021-10-27
        • 2014-01-12
        • 2017-01-25
        相关资源
        最近更新 更多