swift - 如何在不拉伸(stretch)原始图像的情况下扩展 CIImage 的空间(边界)?

标签 swift resize crop cifilter ciimage

我正在对已裁剪的图像应用多个滤镜,并且我想要将其翻转后的副本放在原始图像旁边。这将使宽度增加两倍。

问题:如何扩展边界以使两者都能适合? .cropped(to:CGRect) 将拉伸(stretch)其中的任何原始内容。存在现有内容的原因是因为我尝试尽可能多地使用applyFilter来节省处理时间。这也是我裁剪原始非镜像图像的原因。

下面是我的 CIImage“alphaMaskBlend2”,带有合成过滤器,以及应用于同一图像的变换,可翻转图像并调整其位置。 sourceCore.extent 是我想要的最终图像的大小。

    alphaMaskBlend2 = alphaMaskBlend2?.applyingFilter("CISourceAtopCompositing",
                                                      parameters: [kCIInputImageKey: (alphaMaskBlend2?.transformed(by: scaledImageTransform))!,
                                                                   kCIInputBackgroundImageKey: alphaMaskBlend2!]).cropped(to: sourceCore.extent)

我已经尝试过 LLDB 中变换的位置。我发现裁剪这个滤镜后,最左边的图像会被拉伸(stretch)。如果我使用钳制到相同的程度,然后再次将图像重新裁剪到相同的程度,图像不再扭曲,但图像的边界只有应有宽度的一半。

实现此目的的唯一方法是根据背景图像(sourceCore)进行合成,该背景图像将是两个图像组合的大小,然后合成另一个图像:

    alphaMaskBlend2 = alphaMaskBlend2?.applyingFilter("CISourceAtopCompositing",
                                                      parameters: [kCIInputImageKey: alphaMaskBlend2!,
                                                                   kCIInputBackgroundImageKey: sourceCore])

    alphaMaskBlend2 = alphaMaskBlend2?.applyingFilter("CISourceAtopCompositing",
                                                      parameters: [kCIInputImageKey: (alphaMaskBlend2?.cropped(to: cropRect).transformed(by: scaledImageTransform))!,
                                                                   kCIInputBackgroundImageKey: alphaMaskBlend2!])

问题是,这比必要的更昂贵。我什至通过基准测试对其进行了测试。如果我能用一种复合 Material 来做到这一点,那就更有意义了。

最佳答案

虽然我可以“翻转”CIImage,但我找不到使用现有CIFilter将其“缝合”的方法侧面是原来的。不过,只要具备一些编写自己的 CIKernel 的基本知识,您就可以做到。实现这一目标的一个简单项目是 here .

此项目包含一个示例图像,并使用 CoreImage 和 GLKView 它:

  • 通过调换 CIPerspectiveCorrection 的 Y“底部/顶部”坐标来翻转图像
  • 使用 CIConstantColor 创建新的“调色板”图像,然后使用 CICrop 将其裁剪为原始宽度的两倍
  • 使用一个非常简单的 CIKernel(注册为“Stitch”来实际将其缝合在一起

这是要翻转的代码:

    // use CIPerspectiveCorrection to "flip" on the Y axis

    let minX:CGFloat = 0
    let maxY:CGFloat = 0
    let maxX = originalImage?.extent.width
    let minY = originalImage?.extent.height

    let flipFilter = CIFilter(name: "CIPerspectiveCorrection")
    flipFilter?.setValue(CIVector(x: minX, y: maxY), forKey: "inputTopLeft")
    flipFilter?.setValue(CIVector(x: maxX!, y: maxY), forKey: "inputTopRight")
    flipFilter?.setValue(CIVector(x: minX, y: minY!), forKey: "inputBottomLeft")
    flipFilter?.setValue(CIVector(x: maxX!, y: minY!), forKey: "inputBottomRight")
    flipFilter?.setValue(originalImage, forKey: "inputImage")
    flippedImage = flipFilter?.outputImage

这是创建调色板的代码:

    let paletteFilter = CIFilter(name: "CIConstantColorGenerator")
    paletteFilter?.setValue(CIColor(red: 0.7, green: 0.4, blue: 0.4), forKey: "inputColor")
    paletteImage = paletteFilter?.outputImage
    let cropFilter = CIFilter(name: "CICrop")
    cropFilter?.setValue(paletteImage, forKey: "inputImage")
    cropFilter?.setValue(CIVector(x: 0, y: 0, z: (originalImage?.extent.width)! * 2, w: (originalImage?.extent.height)!), forKey: "inputRectangle")
    paletteImage = cropFilter?.outputImage

以下是注册和使用自定义CIFilter的代码:

    // register and use stitch filer

    StitchedFilters.registerFilters()
    let stitchFilter = CIFilter(name: "Stitch")
    stitchFilter?.setValue(originalImage?.extent.width, forKey: "inputThreshold")
    stitchFilter?.setValue(paletteImage, forKey: "inputPalette")
    stitchFilter?.setValue(originalImage, forKey: "inputOriginal")
    stitchFilter?.setValue(flippedImage, forKey: "inputFlipped")
    finalImage = stitchFilter?.outputImage

演示项目中的所有代码(带有布局约束)都在 viewDidLoad 中,因此请将其放在其所属的位置!

以下代码用于 (a) 创建一个名为 Stitch 的 CIFilter 子类,并 (b) 注册它,以便您可以像任何其他过滤器一样使用它:

func openKernelFile(_ name:String) -> String {
    let filePath = Bundle.main.path(forResource: name, ofType: ".cikernel")
    do {
        return try String(contentsOfFile: filePath!)
    }
    catch let error as NSError {
        return error.description
    }
}

let CategoryStitched = "Stitch"

class StitchedFilters: NSObject, CIFilterConstructor {
    static func registerFilters() {
        CIFilter.registerName(
            "Stitch",
            constructor: StitchedFilters(),
            classAttributes: [
                kCIAttributeFilterCategories: [CategoryStitched]
            ])
    }
    func filter(withName name: String) -> CIFilter? {
        switch name {
        case "Stitch":
            return Stitch()
        default:
            return nil
        }
    }
}

class Stitch:CIFilter {

    let kernel = CIKernel(source: openKernelFile("Stitch"))
    var inputThreshold:Float  = 0
    var inputPalette: CIImage!
    var inputOriginal: CIImage!
    var inputFlipped: CIImage!

    override var attributes: [String : Any] {
        return [
            kCIAttributeFilterDisplayName: "Stitch",

            "inputThreshold": [kCIAttributeIdentity: 0,
                               kCIAttributeClass: "NSNumber",
                               kCIAttributeDisplayName: "Threshold",
                               kCIAttributeDefault: 0.5,
                               kCIAttributeMin: 0,
                               kCIAttributeSliderMin: 0,
                               kCIAttributeSliderMax: 1,
                               kCIAttributeType: kCIAttributeTypeScalar],

            "inputPalette": [kCIAttributeIdentity: 0,
                             kCIAttributeClass: "CIImage",
                             kCIAttributeDisplayName: "Palette",
                             kCIAttributeType: kCIAttributeTypeImage],

            "inputOriginal": [kCIAttributeIdentity: 0,
                              kCIAttributeClass: "CIImage",
                              kCIAttributeDisplayName: "Original",
                              kCIAttributeType: kCIAttributeTypeImage],

            "inputFlipped": [kCIAttributeIdentity: 0,
                             kCIAttributeClass: "CIImage",
                             kCIAttributeDisplayName: "Flipped",
                             kCIAttributeType: kCIAttributeTypeImage]
        ]
    }
    override init() {
        super.init()
    }
    override func setValue(_ value: Any?, forKey key: String) {
        switch key {
        case "inputThreshold":
            inputThreshold = value as! Float
        case "inputPalette":
            inputPalette = value as! CIImage
        case "inputOriginal":
            inputOriginal = value as! CIImage
        case "inputFlipped":
            inputFlipped = value as! CIImage
        default:
            break
        }
    }
    @available(*, unavailable) required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    override var  outputImage: CIImage {
        return kernel!.apply(
            extent: inputPalette.extent,
            roiCallback: {(index, rect) in return rect},
            arguments: [
                inputThreshold as Any,
                inputPalette as Any,
                inputOriginal as Any,
                inputFlipped as Any
            ])!
    }
}

最后是CIKernel代码:

kernel vec4 stitch(float threshold, sampler palette, sampler original, sampler flipped) {
    vec2 coord = destCoord();
    if (coord.x < threshold) {
        return sample(original, samplerCoord(original));
    } else {
        vec2 flippedCoord = coord - vec2(threshold, 0.0);
        vec2 flippedCoordinate = samplerTransform(flipped, flippedCoord);
        return sample(flipped, flippedCoordinate);
    }
}

现在,其他人可能有更优雅的东西 - 甚至可能使用现有的 CIFilter - 但这效果很好。它仅使用 GPU,因此从性能角度来看,可以“实时”使用。我添加了不需要的代码(注册过滤器,使用字典定义属性),使其更像是对那些刚开始创建 CIKernels 的人的教学练习,任何了解使用 CIFilters 的人都可以使用> 可以消费。如果您关注内核代码,您就会发现它与 C 看起来多么相似。

最后,有一个警告。我只是将(Y 轴)翻转图像缝合到原始图像的右侧。如果您想要其他东西,则需要进行调整。

关于swift - 如何在不拉伸(stretch)原始图像的情况下扩展 CIImage 的空间(边界)?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52045504/

相关文章:

javascript - 如何使文本字段中的文本光标开始几像素?

jquery - 自动调整内容大小 - 一页网站

swift - 无法将按钮操作拖动到 Xcode 8 中的现有功能

html - 使用 Alamofire 的谷歌搜索失败(iOS、Swift、JSON、HTML)

arrays - 通过 If 语句追加数组

c# - C# 和 .NET 3.5 中的图像大小调整效率

可变大小裁剪字段的纯 CSS 格式

java - 如何在android中实现手绘图片裁剪?

jquery - 如何使用jquery插件裁剪图像

ios - 如何让 Swift 与 Webview 交互?