Как расширить пространство (границы) CIImage, не растягивая оригинал?

Я применяю несколько фильтров к уже обрезанному изображению и хочу, чтобы его дубликат был перевернут рядом с оригиналом. Это сделало бы его вдвое шире.

Проблема: как расширить границы, чтобы уместились оба? .cropped (to: CGRect) растянет весь исходный контент. Причина, по которой существует существующий контент, заключается в том, что я пытаюсь максимально использовать applyFilter, чтобы сэкономить на обработке. По этой же причине я обрезаю исходное незеркальное изображение.

Ниже мой CIImage "alphaMaskBlend2" с композитным фильтром и преобразованием, примененным к тому же изображению, которое переворачивает его и регулирует его положение. sourceCore.extent - это размер, который мне нужен для окончательного изображения.

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

Я поигрался с позицией преобразования в LLDB. Я обнаружил, что при обрезке этого фильтра крайнее левое изображение растягивается. Если я использую фиксированный до той же степени, а затем повторно кадрирую изображение до такой же степени, изображение больше не искажается, но границы изображения составляют только половину ширины, которая должна быть.

Единственный способ добиться этого - это наложить на фоновое изображение (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!])

Проблема в том, что это дороже, чем необходимо. Я даже протестировал это с помощью бенчмаркинга. Было бы гораздо больше смысла, если бы я мог сделать это с помощью одного композита.


person Chewie The Chorkie    schedule 27.08.2018    source источник
comment
Если вас мало беспокоят корректировки в реальном времени, не могли бы вы просто записать вещи через контекст и сшить два CIImage вместе?   -  person dfd    schedule 29.08.2018
comment
Извините, я должен был упомянуть, что это делается в реальном времени через камеру.   -  person Chewie The Chorkie    schedule 30.08.2018
comment
Я отредактировал сообщение, чтобы показать, что я делаю, чтобы решить эту проблему, но это не очень хорошо сказывается на производительности и не имеет особого смысла.   -  person Chewie The Chorkie    schedule 30.08.2018
comment
Ой. Не уверен, насколько хороша будет производительность через камеру, но я только сейчас осознал то, над чем сейчас работаю - он использует UIViews для создания маски - может помочь. У меня может быть что-то, что полностью использует графический процессор, что является ключом к производительности. Вы можете легко перевернуть CIImage, используя CIPerspectiveCorrection, перевернув ось X или Y. Теперь вопрос, как лучше прошить .... Если что-то выложу сегодня-завтра. Может быть, если вы хотите что-то посмотреть, посмотрите этот CIFilter.   -  person dfd    schedule 30.08.2018
comment
Это могло бы помочь с производительностью вместо того, чтобы я использовал масштабный перевод -1 x, но если вы выясните, как сшить их вместе, чтобы сделать одно большое изображение на графическом процессоре (особенно на CIImage), это было бы полезным ответом. Спасибо.   -  person Chewie The Chorkie    schedule 30.08.2018


Ответы (1)


Хотя я могу "перевернуть" CIImage, я не смог найти способ использовать существующий CIFilter, чтобы "сшить" его рядом с оригиналом. Однако, обладая некоторыми базовыми знаниями о написании собственного CIKernel, вы сможете. Простой проект по достижению этого находится здесь.

Этот проект содержит образец изображения и использует CoreImage и GLKView его:

  • переворачивает изображение, транспонируя координаты Y «низ / верх» для CIPerspectiveCorrection
  • создает новое изображение "палитры", используя 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, поэтому, пожалуйста, поместите его туда, где он принадлежит!

Вот код для (а) создания CIFilter подкласса под названием Stitch и (б) его регистрации, чтобы вы могли использовать его как любой другой фильтр:

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 - но это хорошо работает. Он использует только графический процессор, поэтому с точки зрения производительности его можно использовать в «реальном времени». Я добавил ненужный код (регистрация фильтра, использование словаря для определения атрибутов), чтобы сделать его более учебным упражнением для новичков в создании CIKernels, который может использовать любой, кто знает об использовании CIFilters. Если вы сосредоточитесь на коде ядра, вы поймете, насколько он похож на C.

И наконец, нюанс. Я просто сшиваю перевернутое изображение (ось Y) справа от оригинала. Вам нужно будет что-то отрегулировать, если вы хотите чего-то еще.

person dfd    schedule 30.08.2018
comment
Спасибо за все эти усилия. Это может быть потому, что мне нужно выяснить, как добиться этого с помощью цепочки, но у меня примерно на 10-15% больше процессора, чем у моего другого фильтра. Просто любопытно: для чего конкретно нужен поддонный фильтр? Значения RGB выглядят произвольно, но, возможно, для этого есть причина. Это необходимо? - person Chewie The Chorkie; 04.09.2018
comment
Я предполагаю, что изображение палитры просто для того, чтобы увидеть, все ли выровнено. Я удалил это из всего кода. Любопытно, что я все еще получаю дополнительные 10-15% ударов процессора, возможно, из-за отсутствия цепочки. - person Chewie The Chorkie; 04.09.2018
comment
У одного шва тоже есть выпуклость - немного меньше, но все же есть. Если бы я мог объединить два композитных фильтра, это высвободило бы много ресурсов процессора. Основное беспокойство по поводу производительности заключается в том, что я запускаю это через камеру в реальном времени. В противном случае это не было бы проблемой. - person Chewie The Chorkie; 04.09.2018
comment
Короче говоря, мое следующее приложение (почти готовое) имеет функцию компоновки - начиная с палитры (CIImage) размером 640x640, в нее можно добавлять изображения вместе с фильтрами, которые меняют внешний вид. Я адаптировал эту концепцию здесь - вам нужно сделать несколько вещей ... перевернуть изображение, сшить его и (с точки зрения того, для чего предназначена палитра) создать окончательный результат, который представляет собой высоту и УДВОИТЬ ширину с точки зрения степени. Так что это единственная причина для этого. Да, это не элегантно, как я сказал в ответе. - person dfd; 04.09.2018
comment
Сказав, о чем палитра, вот и все остальное. Я не смог найти ПРОСТОЙ (читай: способ встроенного фильтра CI), чтобы перевернуть изображение (оно у вас уже было) и сшить. Итак, я создал один. Тот, который использовал простое использование кода ядра. (Я все еще надеюсь, что у других есть способ лучше, чем у меня. Некоторые фильтры CI дают результат с бесконечной степенью - это на ваше усмотрение вы. Это, в сочетании с пониманием того, как устроены ядра CI обработанный (цвет по сравнению с деформацией / общим) достиг моего кода. Моя попытка больше касается статического изображения. У меня есть последний комментарий .... - person dfd; 04.09.2018
comment
Я почти всегда рекомендую это - и, возможно, уже рекомендовал вам это! Саймон Глэдман - мое главное вдохновение. Эта статья в блоге (flexmonkey. blogspot.com/2015/07/) старый - я думаю, Swift 2 - но все же своевременный. Он перешел к другим вещам. Но, насколько мне небезразлично, он по-прежнему является источником для CoreImage. Его книга CoreImage для Swift (также Swift 2) до сих пор мне нравится. В вашем случае да, я проверил, могу ли я использовать ROI вместо довольно простого фильтра. Может быть, что-то там тоже могло бы вам помочь. - person dfd; 04.09.2018
comment
Позвольте нам продолжить это обсуждение в чате. - person Chewie The Chorkie; 04.09.2018