如何在不拉伸的情况下裁剪 swift 中的宽图像

How to crop wide images in swift without stretching

我在裁剪宽幅面的照片时遇到了问题。例如,我用 iPad 前置摄像头拍照,分辨率为 960w,1280h,我需要裁剪为 875w,570h。我尝试了中的一些方法,但它们都拉伸了图像或者没有得到我想要的尺寸。

这是我尝试的第一种方法:

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
}

这总是会拉伸图像。

我想尝试剪出我想要的确切尺寸,所以我尝试了这个:

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
}

但这导致图像与我想要的相反,570w,875h。所以我考虑过反转这些值,但如果我这样做,我会得到 605w、570h。也许问题在于我如何获得图像的 X 和 Y 位置?

编辑

在 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))
    }

 }
}

我是这样称呼它的:

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

图像数据来自前置摄像头的捕获。

这是我的捕获代码:

@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 }                        
}

您只需将原始尺寸宽度减去目标尺寸宽度,除以二并设置裁剪原点 x 值。接下来对高度执行相同的操作并设置 y 位置。然后用裁剪后的 cgImage 初始化一个新的 UIImage:

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))