Swift 在找到图像中的边缘后,有什么好方法可以可变地调整线条厚度?

Swift 在找到图像中的边缘后,有什么好方法可以可变地调整线条厚度?,swift,cifilter,Swift,Cifilter,CIEdges(以及其他cifilter)在设备和iOS版本之间的工作不再一致。所以我决定用卷积滤波器寻找边缘。成功:我可以得到一个好的黑白图像遮罩,并让渐变色或纯色显示出来: 问题是,我想不出一个好方法来可变地调整线的厚度。我尝试的一种方法是减少源图像的版本,并在黑白相机图像的顶部应用卷积和高比例。这种方法很管用,但线条看起来参差不齐,粗糙不堪。有什么想法吗?这是我的密码: func sketch(with ciImage: CIImage) -> CIImage { var

CIEdges(以及其他cifilter)在设备和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

}

边缘的厚度取决于卷积核的宽度或图像分辨率

为了(动态地)增加卷积的宽度,您可能需要实现自己的自定义
CIKernel
,因为核心映像最多只支持9x9个卷积内核。更宽的内核应该会产生更平滑的结果,但也要昂贵得多


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

FWIW,在创建具有角半径的遮罩并向其添加边时,我遇到了相同的问题,所有这些都使用了CIImages。我认为(一点也不确定)部分原因在于抗锯齿和像素。也许你可以试着稍微模糊边缘线?模糊是我考虑过的一种方法,我也尝试过一点,如果我能将颜色值四舍五入为黑色,并使用特定的阈值四舍五入为白色,这可能是一种解决方案。CicolorCamp几乎就是我要寻找的,除了增加匹配的值小于一定量,如果超过一定量,则减少值。除非我理解错了,否则我不能用这个。当我第一次读到你的问题时,我想到了这个问题,它的答案-我不认为它有帮助,但它可能会给你一个想法。。。哦,是的,我以前见过这个问题。谢谢,这是我想要的,但程度更高。祝你好运。如果我正确地阅读了您想要的内容(较厚的边缘没有锯齿),我认为掩蔽和模糊的组合可能会更好。如果你觉得满意的话,请把它贴出来——我很乐意把它投上去(可能还包括进去)。很有趣,谢谢。我从未想过使用其他变换过滤器。我应该看看它们工作得有多好。注意:我在他们的Cifilter列表中找不到CIBicubicScaleTransform,但他们看到过滤器本身有一个文档。是的,苹果的文档不再保存。我建议您访问cifilter.io作为参考。我从没听说过。非常感谢。