Home > Mobile >  How to convert this UIGraphics image operation to Core Image?
How to convert this UIGraphics image operation to Core Image?

Time:03-02

I write SplitMirror filter like in Apple Motion app or Snapchat lenses by using UIGraphics to use it with real time camera feed or video processing, with single image request it works well like in attached image in the question but for multiple filtering request not. I think it's code must be changed from UIGraphics to Core Image & CIContext for better performance and less memory usage like CIFilters and actually I don't know how to do it. I tried several ways to convert it but I stuck on merging left and right, this can be done with `CICategoryCompositeOperations filters but which one is fit this case I o have idea, so need some help with this issue.

Filter code using UIGraphics:

//
//  SplitMirror filter.swift
//  Image Editor
// 
//  Created by Coder ACJHP on 25.02.2022.
// 
// SnapChat & Tiktok & Motion App like image filter
// Inspired from 'https://support.apple.com/tr-tr/guide/motion/motn169f94ea/mac'
// Splits an image in half vertically and reverses the left remaining half to create a reflection.

final func splitMirrorFilter(processingImage image: UIImage) -> UIImage {
    // Image size
    let imageSize = image.size

    // Left half
    let leftHalfRect = CGRect(
        origin: .zero,
        size: CGSize(
                width: imageSize.width/2,
                height: imageSize.height
                )
    )
    
    // Right half
    let rightHalfRect = CGRect(
        origin: CGPoint(
            x: imageSize.width - (imageSize.width/2).rounded(),
            y: 0
        ),
        size: CGSize(
            width: imageSize.width - (imageSize.width/2).rounded(),
            height: imageSize.height
        )
    )
    // Split image into two parts
    guard let cgRightHalf = image.cgImage?.cropping(to: rightHalfRect) else { return image }
    // Flip right side to be used as left side
    let flippedLeft = UIImage(cgImage: cgRightHalf, scale: image.scale, orientation: .upMirrored)
    let unFlippedRight = UIImage(cgImage: cgRightHalf, scale: image.scale, orientation: image.imageOrientation)
    
    UIGraphicsBeginImageContextWithOptions(imageSize, false, image.scale)
    
    flippedLeft.draw(at: leftHalfRect.origin)
    unFlippedRight.draw(at: rightHalfRect.origin)
    
    guard let splitMirroredImage = UIGraphicsGetImageFromCurrentImageContext() else { return image }
    UIGraphicsEndImageContext()
    return splitMirroredImage
}

It's result looks like

Here is what it tried with Core Image

    // Splits an image in half vertically and reverses the left remaining half to create a reflection.
final func splitMirrorFilterCIImageVersion(processingImage image: UIImage) -> CIImage? {

    
    guard let ciImageCopy = CIImage(image: image) else { return image.ciImage }
    
    // Image size
    let imageSize = ciImageCopy.extent.size
    let imageRect = CGRect(origin: .zero, size: imageSize)

    // Left half
    let leftHalfRect = CGRect(
        origin: .zero,
        size: CGSize(
                width: imageSize.width/2,
                height: imageSize.height
                )
    )
    
    // Right half
    let rightHalfRect = CGRect(
        origin: CGPoint(
            x: imageSize.width - (imageSize.width/2).rounded(),
            y: 0
        ),
        size: CGSize(
            width: imageSize.width - (imageSize.width/2).rounded(),
            height: imageSize.height
        )
    )
    
    
    
    // Split image into two parts
    let cgRightHalf = ciImageCopy.cropped(to: rightHalfRect)
    context.draw(cgRightHalf.oriented(.upMirrored), in: leftHalfRect, from: imageRect)
    context.draw(cgRightHalf, in: rightHalfRect, from: imageRect)
    
    // I'm stuck here
    
    // Merge two images into one 
    // Here I don't know which filter can be used to merge op
    // CICategoryCompositeOperation filters may fits
}

CodePudding user response:

I think you are on the right track.

You can create the left half from the right half by applying transformations to it using let leftHalf = rightHalf.transformed(by: transformation). The transformation should mirror it and translate it to the correct position, i.e., next to the right half.

You can them combine the two into one image using let result = leftHalf.composited(over: rightHalf) and render that result using a CIContext.

CodePudding user response:

After getting the correct idea from Frank Schlegel 's answer I rewrote filter code and now works well.

 // Splits an image in half vertically and reverses the left remaining half to create a reflection.
final func splitMirrorFilterCIImageVersion(processingImage image: UIImage) -> CIImage? {
    
    guard let ciImageCopy = CIImage(image: image) else { return image.ciImage }
    
    // Image size
    let imageSize = ciImageCopy.extent.size
    
    // Right half
    let rightHalfRect = CGRect(
        origin: CGPoint(
            x: imageSize.width - (imageSize.width/2).rounded(),
            y: 0
        ),
        size: CGSize(
            width: imageSize.width - (imageSize.width/2).rounded(),
            height: imageSize.height
        )
    )
            
    // Split image into two parts
    let ciRightHalf = ciImageCopy.cropped(to: rightHalfRect)
    // Make transform to move right part to left
    let transform = CGAffineTransform(translationX: -rightHalfRect.size.width, y: -rightHalfRect.origin.y)
    // Create left part and apply transform then flip it
    let ciLeftHalf = ciRightHalf.transformed(by: transform).oriented(.upMirrored)
            
    // Merge two images into one
    return ciLeftHalf.composited(over: ciRightHalf)
}
  • Related