How to crop wide images in swift without stretching

人盡茶涼 提交于 2020-03-21 04:27:26

问题


I am having trouble cropping pictures taken to be of an exact size that is in the wide format. For instance I take a picture with an iPad front camera, which has the resolution of 960w,1280h and I need to crop to be exactly 875w,570h. I tried some of the methods in here, but they all stretch the image or don't get the size I want.

Here is the first method that I tried:

func cropToBounds(image: UIImage, width: Double, height: Double) -> UIImage {

    let cgimage = image.cgImage!
    let contextImage: UIImage = UIImage(cgImage: cgimage)
    guard let newCgImage = contextImage.cgImage else { return contextImage }
    let contextSize: CGSize = contextImage.size
    var posX: CGFloat = 0.0
    var posY: CGFloat = 0.0
    let cropAspect: CGFloat = CGFloat(width / height)

    var cropWidth: CGFloat = CGFloat(width)
    var cropHeight: CGFloat = CGFloat(height)

    if width > height { //Landscape
        cropWidth = contextSize.width
        cropHeight = contextSize.width / cropAspect
        posY = (contextSize.height - cropHeight) / 2
    } else if width < height { //Portrait
        cropHeight = contextSize.height
        cropWidth = contextSize.height * cropAspect
        posX = (contextSize.width - cropWidth) / 2
    } else { //Square
        if contextSize.width >= contextSize.height { //Square on landscape (or square)
            cropHeight = contextSize.height
            cropWidth = contextSize.height * cropAspect
            posX = (contextSize.width - cropWidth) / 2
        }else{ //Square on portrait
            cropWidth = contextSize.width
            cropHeight = contextSize.width / cropAspect
            posY = (contextSize.height - cropHeight) / 2
        }
    }

    let rect: CGRect = CGRect(x: posX, y: posY, width: cropWidth, height: cropHeight)

    // Create bitmap image from context using the rect
    guard let imageRef: CGImage = newCgImage.cropping(to: rect) else { return contextImage}

    // Create a new image based on the imageRef and rotate back to the original orientation
    let cropped: UIImage = UIImage(cgImage: imageRef, scale: image.scale, orientation: image.imageOrientation)

    print(image.scale)
    UIGraphicsBeginImageContextWithOptions(CGSize(width: width, height: height), false, 0.0)
    cropped.draw(in: CGRect(x: 0, y: 0, width: width, height: height))
    let resized = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext()


    return resized ?? image
}

This always stretches the image.

I thought about trying to cut the exact size I wanted, so I tried this:

func cropImage(image: UIImage, width: Double, height: Double)->UIImage{

    let cgimage = image.cgImage!
    let contextImage: UIImage = UIImage(cgImage: cgimage)
    let contextSize: CGSize = contextImage.size
    var posX: CGFloat = 0.0
    var posY: CGFloat = 0.0
    var recWidth : CGFloat = CGFloat(width)
    var recHeight : CGFloat = CGFloat(height)

    if width > height { //Landscape
         posY = (contextSize.height - recHeight) / 2
    }
    else { //Square

        posX = (contextSize.width - recWidth) / 2

    }

    let rect: CGRect = CGRect(x: posX, y: posY, width: recWidth, height: recHeight)
    let imageRef:CGImage = cgimage.cropping(to: rect)!
    print(imageRef.width)
    print(imageRef.height)
    let croppedimage:UIImage = UIImage(cgImage: imageRef, scale: image.scale, orientation: image.imageOrientation)
    print(croppedimage.size)



    return croppedimage
}

But this resulted in an image with the opposite of what I want, 570w,875h. So I thought about inverting the values, but if I do that I get 605w, 570h. Maybe the problem is in how I get the X and Y positions of the image?

EDIT

Here is what I am doing now after the help of Leo Dabus:

extension UIImage {
  func cropped(to size: CGSize) -> UIImage? {
    guard let cgImage = cgImage?
        .cropping(to: .init(origin: .init(x: (self.size.width-size.width)/2,
                                          y: (self.size.height-size.height)/2),
                            size: size)) else { return nil }
    let format = imageRendererFormat
    return UIGraphicsImageRenderer(size: size, format: format).image {
        _ in
        UIImage(cgImage: cgImage, scale: 1, orientation: imageOrientation)
            .draw(in: .init(origin: .zero, size: size))
    }

 }
}

This is how I call it:

let foto = UIImage(data: imageData)!
let size = CGSize(width: 875.0, height: 570.0)
let cropedPhoto = foto.cropped(to: size)

The imageData comes from a capture of the front camera.

And this is my capture code:

@objc func takePhoto(_ sender: Any?) {
    let videoOrientation = AVCaptureVideoOrientation.portrait
    stillImageOutput!.connection(with: .video)?.videoOrientation = videoOrientation

    let settings = AVCapturePhotoSettings(format: [AVVideoCodecKey: AVVideoCodecType.jpeg])
    let gesture = previewView.gestureRecognizers
    previewView.removeGestureRecognizer(gesture![0])
}

func photoOutput(_ output: AVCapturePhotoOutput, didFinishProcessingPhoto photo: AVCapturePhoto, error: Error?) {
    guard let imageData = photo.fileDataRepresentation()
        else { return }                        
}

回答1:


You just need to get the original size width subtract the destination size width, divide by two and set the cropping origin x value. Next do the same with the height and set the y position. Then just initialize a new UIImage with the cropped cgImage:

extension UIImage {
    func cropped(to size: CGSize) -> UIImage? {
        guard let cgImage = cgImage?
            .cropping(to: .init(origin: .init(x: (self.size.width - size.width) / 2,
                                              y: (self.size.height - size.height) / 2),
                                size: size)) else { return nil }
        return UIImage(cgImage: cgImage, scale: 1, orientation: imageOrientation)
    }
}

let imageURL = URL(string: "https://www.comendochucruteesalsicha.com.br/wp-content/uploads/2016/09/IMG_5356-960x1280.jpg")!
let image = UIImage(data: try! Data(contentsOf: imageURL))!

let squared = image.cropped(to: .init(width: 875, height: 570))



来源:https://stackoverflow.com/questions/60065539/how-to-crop-wide-images-in-swift-without-stretching

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!