【问题标题】:What would be a good method to variably adjust line thickness after finding edges in an image?在找到图像边缘后可变调整线条粗细的好方法是什么?
【发布时间】:2019-12-21 20:34:29
【问题描述】:

CIEdges(以及其他 CIFilters)在设备和 iOS 版本之间的工作方式不再一致。所以我决定用卷积滤波器找到边缘。成功:我可以得到一个很好的黑白蒙版图像,并让渐变或纯色显示出来:

问题是,我想不出一个可变调整线条粗细的好方法。我尝试的一种方法是减少源图像的版本,并在黑白相机图像的顶部应用卷积和放大。那种作品,但线条看起来锯齿状和粗糙。有任何想法吗?这是我的代码:

func sketch(with ciImage: CIImage) -> CIImage {

    var sourceCore = ciImage

    var convolutionValue_A:CGFloat = -0.0925937220454216
    var convolutionValue_B:CGFloat = -0.4166666567325592
    var convolutionValue_C:CGFloat = -1.8518532514572144
    var convolutionValue_D:CGFloat = 0.23148006200790405
    var convolutionValue_E:CGFloat = 4.5833334922790527
    var convolutionValue_F:CGFloat = 14.166666984558105

    var brightnessVal:CGFloat = 1.1041666269302368
    var contrastVal:CGFloat = 3.0555555820465088

    // radially symmetrical convolution weights:
    var weightsArr: [CGFloat] = [
        convolutionValue_A, convolutionValue_A, convolutionValue_B, convolutionValue_B, convolutionValue_B, convolutionValue_A, convolutionValue_A,
        convolutionValue_A, convolutionValue_B, convolutionValue_C, convolutionValue_C, convolutionValue_C, convolutionValue_B, convolutionValue_A,
        convolutionValue_B, convolutionValue_C, convolutionValue_D, convolutionValue_E, convolutionValue_D, convolutionValue_C, convolutionValue_B,
        convolutionValue_B, convolutionValue_C, convolutionValue_E, convolutionValue_F, convolutionValue_E, convolutionValue_C, convolutionValue_B,
        convolutionValue_B, convolutionValue_C, convolutionValue_D, convolutionValue_E, convolutionValue_D, convolutionValue_C, convolutionValue_B,
        convolutionValue_A, convolutionValue_B, convolutionValue_C, convolutionValue_C, convolutionValue_C, convolutionValue_B, convolutionValue_A,
        convolutionValue_A, convolutionValue_A, convolutionValue_B, convolutionValue_B, convolutionValue_B, convolutionValue_A, convolutionValue_A
    ]

    let inputWeights:CIVector = CIVector(values: weightsArr, count: weightsArr.count)

    sourceCore = sourceCore
        .applyingFilter("CIColorControls", parameters: [kCIInputImageKey: sourceCore,
                                                        kCIInputSaturationKey: 0.0,
                                                        kCIInputBrightnessKey: brightnessVal,
                                                        kCIInputContrastKey: contrastVal])

    // transforms image to only show edges in black and white
    sourceCore = sourceCore
        .applyingFilter("CIConvolution7X7", parameters: [kCIInputImageKey: sourceCore,

    let whiteCIColor = CIColor.white
    let whiteColor = CIImage(color: whiteCIColor).cropped(to: ciImage.extent)

    // for some reason, I need to blend the black and white mask with the color white.
    // either CIColorDodgeBlendMode or CILinearDodgeBlendMode seems to work fine here:
    sourceCore = sourceCore
            .applyingFilter("CIColorDodgeBlendMode", parameters: [kCIInputImageKey: sourceCore,
                                                                    kCIInputBackgroundImageKey: whiteColor])
                                                         kCIInputWeightsKey: inputWeights]).cropped(to: sourceCore.extent)

    // give camera image a black and white Noir effect
    var ciImage = ciImage
        .applyingFilter("CIPhotoEffectNoir", parameters: [kCIInputImageKey: ciImage])


    // make solid color
    let color = CIColor(red: 0.819, green: 0.309, blue: 0.309)
    let colFilter = CIFilter(name: "CIConstantColorGenerator")!
    colFilter.setValue(color, forKey: kCIInputColorKey)
    var solidColor = colFilter.value(forKey: "outputImage") as! CIImage
    solidColor = solidColor.cropped(to: ciImage.extent)

    // color is shown through outlines correctly, 
    // and image is black and white
    sourceCore = sourceCore
        .applyingFilter("CIBlendWithMask", parameters: [
            kCIInputImageKey: ciImage, // black and white image
            kCIInputBackgroundImageKey: solidColor, // solid color
            kCIInputMaskImageKey:sourceCore]) // edge work image

    ciImage = sourceCore

    return ciImage

}

【问题讨论】:

  • FWIW,我在创建具有圆角半径的蒙版并为其添加边缘时遇到了同样的问题,所有这些都使用 CIImages。我认为(完全不确定)部分原因是抗锯齿和像素。也许您可以尝试稍微模糊边缘线?
  • 模糊是我考虑过并尝试过的一种方法,如果我可以使用某些阈值将颜色值四舍五入为黑色并向下舍入为白色,这可能是一个解决方案。 CIColorClamp 几乎是我正在寻找的,除了增加你匹配的值小于一定数量,如果超过一定数量则减少值。除非我理解错了,否则我不能使用它。
  • 当我第一次阅读你的问题时,我想到了这个问题,它就是答案——我认为它没有帮助,但它可能会给你一个想法......stackoverflow.com/questions/45927329/cifilter-combination
  • 哦,是的,我以前见过这个问题。谢谢,这是我所追求的,但程度更高。
  • 祝你好运。如果我正确阅读了您想要的内容(没有锯齿的较厚边缘),我认为掩蔽和模糊的组合可能会更好。如果您找到满意的东西,请发布它 - 我很乐意支持它(并可能合并它)。

标签: swift cifilter


【解决方案1】:

边缘的粗细取决于卷积核的宽度图像分辨率。

要(动态)增加卷积的宽度,您可能需要实现自己的自定义CIKernel,因为 Core Image 最多仅支持 9x9 卷积核。更宽的内核应该会提供更平滑的结果,但也更昂贵。

您可以通过在应用内核之前缩小图像(就像您已经做过的那样)并在之后再次放大它来实现类似的效果。您可以尝试使用像CILanczosScaleTransformCIBicubicScaleTransform 这样的“智能”放大过滤器并使用它们的参数。使用双三次过滤器的inputC,您应该能够控制结果的柔和度。

【讨论】:

  • 有趣,谢谢。我从没想过使用其他转换 CIFilters。我应该看看它们的效果如何。
  • 注意:我在他们的 CIFilters 列表中找不到 CIBicubicScaleTransform,但他们看到过滤器本身有一个文档。
  • 是的,Apple 的文档不再维护。我建议去 cifilter.io 参考。
  • 哇。我从来没有听说过。谢谢。
猜你喜欢
  • 1970-01-01
  • 2013-11-15
  • 1970-01-01
  • 2014-09-03
  • 1970-01-01
  • 2020-12-18
  • 1970-01-01
  • 2021-10-16
  • 2022-07-05
相关资源
最近更新 更多