【问题标题】:How to crop wide images in swift without stretching如何在不拉伸的情况下快速裁剪宽图像
【发布时间】:2020-05-20 18:17:37
【问题描述】:

我无法将拍摄的照片裁剪为宽幅格式的精确尺寸。例如,我用 iPad 前置摄像头拍照,分辨率为 960w,1280h,我需要裁剪为 875w,570h。我尝试了here中的一些方法,但是它们都拉伸了图像或者没有得到我想要的大小。

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

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)

imageData 来自前置摄像头的捕获。

这是我的捕获代码:

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

【问题讨论】:

  • 与您的问题无关,但UIImagecgImage 属性可能会返回nil。顺便说一句,获得CGImage 的目的是什么,然后从那里初始化另一个UIImage
  • 说实话,我这样做只是因为在另一个堆栈溢出答案中我看到有人这样做,所以我想尝试看看我是否会得到不同的东西,但它并没有区别。

标签: ios swift image crop swift4.2


【解决方案1】:

您只需将原始尺寸宽度减去目标尺寸宽度,除以二并设置裁剪原点 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))

【讨论】:

  • 您可能需要使用新的上下文来渲染图像。查看此帖子stackoverflow.com/a/42098812/2303865
  • 您需要在裁剪之前重绘/拼合图像
  • 发生的情况是 UIImage 有一些与之关联的元数据(包括它的方向)。 CGImage 没有。当您在新的上下文中渲染图像时,您正在创建具有正确方向的新图像,因此它不依赖于正确显示的元数据。 PNG也是如此。如果您保存 PNG 而不重新绘制它,它可能无法正确显示,具体取决于拍摄图片的方向。 JPEG 图像没有这个问题,因为它也保存了元数据。
  • 所以只需使用我上面发布的链接的 flatten 属性创建一个新的 UIImage 。然后,您可以从中获取 CGImage 并对其进行操作(在您的情况下对其进行裁剪),而无需担心拍摄的方向
  • 非常感谢,就是这样!
猜你喜欢
  • 1970-01-01
  • 2015-05-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多