如何调整UIImage的大小以减少上传图像大小

Jay*_*Jay 66 uiimage ios swift

我一直在搜索谷歌,并且只会遇到减少高度/宽度的库或者通过CoreImage编辑UIImage外观的库.但我没有看到或找到一个库,帖子解释了如何减少图像大小,所以当它上传时,它不是完整的图像大小.

到目前为止我有这个:

        if image != nil {
        //let data = NSData(data: UIImagePNGRepresentation(image))
        let data = UIImagePNGRepresentation(image)
        body.appendString("--\(boundary)\r\n")
        body.appendString("Content-Disposition: form-data; name=\"image\"; filename=\"randomName\"\r\n")
        body.appendString("Content-Type: image/png\r\n\r\n")
        body.appendData(data)
        body.appendString("\r\n")
    }
Run Code Online (Sandbox Code Playgroud)

它正在发送12MB的照片.我怎样才能减少到1mb?谢谢!

Leo*_*bus 158

Xcode 8.2.1•Swift 3.0.2

extension UIImage {
    func resized(withPercentage percentage: CGFloat) -> UIImage? {
        let canvas = CGSize(width: size.width * percentage, height: size.height * percentage)
        return UIGraphicsImageRenderer(size: canvas, format: imageRendererFormat).image {
            _ in draw(in: CGRect(origin: .zero, size: canvas))  
        }
    }
    func resized(toWidth width: CGFloat) -> UIImage? {
        let canvas = CGSize(width: width, height: CGFloat(ceil(width/size.width * size.height)))
        return UIGraphicsImageRenderer(size: canvas, format: imageRendererFormat).image {
            _ in draw(in: CGRect(origin: .zero, size: canvas))
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

用法:

let image = UIImage(data: try! Data(contentsOf: URL(string:"http://i.stack.imgur.com/Xs4RX.jpg")!))!

let thumb1 = image.resized(withPercentage: 0.1)
let thumb2 = image.resized(toWidth: 72.0)
Run Code Online (Sandbox Code Playgroud)

  • LeoDabus,我认为这是不正确的答案,但对其他问题非常好的解决方案.这里的问题是我们如何才能将12MB减少到1MB而不是基于百分比和图像大小(宽度或高度),而是基于文件大小.@EBDOKUM可能会有这样的结果,因为减小图像大小(宽度和高度)并不能保证减小文件大小.只缩小尺寸,但文件大小可以根据使用的图像增加或减少. (4认同)
  • 您应该添加“guard size.width > width else { return self }”,这样较小的图像就不会调整为更大。 (4认同)
  • 好东西,利奥。谢谢。 (2认同)
  • 你应该在下面添加swift 2语法.有人复制/粘贴你的代码并获得了9个upvotes ..如果你问我我不应该...... (2认同)
  • @ChrisW.不是谈论photoshop或网络引擎.随意发布您的答案,显示如何在iOS(Swift)中压缩PNG. (2认同)

use*_*201 68

这是我按照调整图像大小的方式.

 -(UIImage *)resizeImage:(UIImage *)image
{
   float actualHeight = image.size.height;
   float actualWidth = image.size.width;
   float maxHeight = 300.0;
   float maxWidth = 400.0;
   float imgRatio = actualWidth/actualHeight;
   float maxRatio = maxWidth/maxHeight;
   float compressionQuality = 0.5;//50 percent compression

   if (actualHeight > maxHeight || actualWidth > maxWidth)
   {
    if(imgRatio < maxRatio)
    {
        //adjust width according to maxHeight
        imgRatio = maxHeight / actualHeight;
        actualWidth = imgRatio * actualWidth;
        actualHeight = maxHeight;
    }
    else if(imgRatio > maxRatio)
    {
        //adjust height according to maxWidth
        imgRatio = maxWidth / actualWidth;
        actualHeight = imgRatio * actualHeight;
        actualWidth = maxWidth;
    }
    else
    {
        actualHeight = maxHeight;
        actualWidth = maxWidth;
    }
   }

   CGRect rect = CGRectMake(0.0, 0.0, actualWidth, actualHeight);
   UIGraphicsBeginImageContext(rect.size);
   [image drawInRect:rect];
   UIImage *img = UIGraphicsGetImageFromCurrentImageContext();
   NSData *imageData = UIImageJPEGRepresentation(img, compressionQuality);
   UIGraphicsEndImageContext();

   return [UIImage imageWithData:imageData];

}
Run Code Online (Sandbox Code Playgroud)

使用此方法,我的6.5 MB的图像减少到104 KB.

Swift 4代码:

func resize(_ image: UIImage) -> UIImage {
    var actualHeight = Float(image.size.height)
    var actualWidth = Float(image.size.width)
    let maxHeight: Float = 300.0
    let maxWidth: Float = 400.0
    var imgRatio: Float = actualWidth / actualHeight
    let maxRatio: Float = maxWidth / maxHeight
    let compressionQuality: Float = 0.5
    //50 percent compression
    if actualHeight > maxHeight || actualWidth > maxWidth {
        if imgRatio < maxRatio {
            //adjust width according to maxHeight
            imgRatio = maxHeight / actualHeight
            actualWidth = imgRatio * actualWidth
            actualHeight = maxHeight
        }
        else if imgRatio > maxRatio {
            //adjust height according to maxWidth
            imgRatio = maxWidth / actualWidth
            actualHeight = imgRatio * actualHeight
            actualWidth = maxWidth
        }
        else {
            actualHeight = maxHeight
            actualWidth = maxWidth
        }
    }
    let rect = CGRect(x: 0.0, y: 0.0, width: CGFloat(actualWidth), height: CGFloat(actualHeight))
    UIGraphicsBeginImageContext(rect.size)
    image.draw(in: rect)
    let img = UIGraphicsGetImageFromCurrentImageContext()
    let imageData = img?.jpegData(compressionQuality: CGFloat(compressionQuality)) 
    UIGraphicsEndImageContext()
    return UIImage(data: imageData!) ?? UIImage()
}
Run Code Online (Sandbox Code Playgroud)


Tun*_*Fam 25

如果有人想要使用Swift 3和4将图像大小调整小于1MB.

只需复制并粘贴此扩展程序:

extension UIImage {

func resized(withPercentage percentage: CGFloat) -> UIImage? {
    let canvasSize = CGSize(width: size.width * percentage, height: size.height * percentage)
    UIGraphicsBeginImageContextWithOptions(canvasSize, false, scale)
    defer { UIGraphicsEndImageContext() }
    draw(in: CGRect(origin: .zero, size: canvasSize))
    return UIGraphicsGetImageFromCurrentImageContext()
}

func resizedTo1MB() -> UIImage? {
    guard let imageData = UIImagePNGRepresentation(self) else { return nil }

    var resizingImage = self
    var imageSizeKB = Double(imageData.count) / 1000.0 // ! Or devide for 1024 if you need KB but not kB

    while imageSizeKB > 1000 { // ! Or use 1024 if you need KB but not kB
        guard let resizedImage = resizingImage.resized(withPercentage: 0.9),
            let imageData = UIImagePNGRepresentation(resizedImage)
            else { return nil }

        resizingImage = resizedImage
        imageSizeKB = Double(imageData.count) / 1000.0 // ! Or devide for 1024 if you need KB but not kB
    }

    return resizingImage
}
}
Run Code Online (Sandbox Code Playgroud)

使用:

let resizedImage = originalImage.resizedTo1MB()
Run Code Online (Sandbox Code Playgroud)

编辑: 请注意它是阻止UI,所以如果您认为这是适合您案例的正确方法,请转到后台主题.


Ali*_*man 18

斯威夫特 5.4 和 Xcode 12.5

我对这里的解决方案不满意,它根据给定的 KB 大小生成图像,因为它们中的大多数使用.jpegData(compressionQuality: x). 此方法不适用于大图像,因为即使将压缩质量设置为 0.0,大图像仍将保持较大,例如,在将 compressionQuality 设置为 0.0 时,新 iPhone 的纵向模式生成的 10 MB 仍将高于 1 MB。

因此,我在这里使用了一些答案并重写了一个 Helper Struct,它可以在背景队列中转换图像:

import UIKit

struct ImageCompressor {
    static func compress(image: UIImage, maxByte: Int,
                         completion: @escaping (UIImage?) -> ()) {
        DispatchQueue.global(qos: .userInitiated).async {
            guard let currentImageSize = image.jpegData(compressionQuality: 1.0)?.count else {
                return completion(nil)
            }
        
            var iterationImage: UIImage? = image
            var iterationImageSize = currentImageSize
            var iterationCompression: CGFloat = 1.0
        
            while iterationImageSize > maxByte && iterationCompression > 0.01 {
                let percantageDecrease = getPercantageToDecreaseTo(forDataCount: iterationImageSize)
            
                let canvasSize = CGSize(width: image.size.width * iterationCompression,
                                        height: image.size.height * iterationCompression)
                UIGraphicsBeginImageContextWithOptions(canvasSize, false, image.scale)
                defer { UIGraphicsEndImageContext() }
                image.draw(in: CGRect(origin: .zero, size: canvasSize))
                iterationImage = UIGraphicsGetImageFromCurrentImageContext()
            
                guard let newImageSize = iterationImage?.jpegData(compressionQuality: 1.0)?.count else {
                    return completion(nil)
                }
                iterationImageSize = newImageSize
                iterationCompression -= percantageDecrease
            }
            completion(iterationImage)
        }
    }

    private static func getPercantageToDecreaseTo(forDataCount dataCount: Int) -> CGFloat {
        switch dataCount {
        case 0..<3000000: return 0.05
        case 3000000..<10000000: return 0.1
        default: return 0.2
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

将图像压缩到最大 1 MB:

        ImageCompressor.compress(image: image, maxByte: 1000000) { image in
            guard let compressedImage = image else { return }
            // Use compressedImage
        }
    }
Run Code Online (Sandbox Code Playgroud)

  • 希望我能投票 100 次。真正的答案各位。 (4认同)
  • 该功能使性能优于其他功能。但我有一个问题,使用此功能后会创建浅边框。我怎样才能防止这种情况发生?提前谢谢您! (2认同)

Jag*_*eep 13

与Leo Answer相同,但对SWIFT 2.0的编辑很少

 extension UIImage {
    func resizeWithPercentage(percentage: CGFloat) -> UIImage? {
        let imageView = UIImageView(frame: CGRect(origin: .zero, size: CGSize(width: size.width * percentage, height: size.height * percentage)))
        imageView.contentMode = .ScaleAspectFit
        imageView.image = self
        UIGraphicsBeginImageContextWithOptions(imageView.bounds.size, false, scale)
        guard let context = UIGraphicsGetCurrentContext() else { return nil }
        imageView.layer.renderInContext(context)
        guard let result = UIGraphicsGetImageFromCurrentImageContext() else { return nil }
        UIGraphicsEndImageContext()
        return result
    }

    func resizeWithWidth(width: CGFloat) -> UIImage? {
        let imageView = UIImageView(frame: CGRect(origin: .zero, size: CGSize(width: width, height: CGFloat(ceil(width/size.width * size.height)))))
        imageView.contentMode = .ScaleAspectFit
        imageView.image = self
        UIGraphicsBeginImageContextWithOptions(imageView.bounds.size, false, scale)
        guard let context = UIGraphicsGetCurrentContext() else { return nil }
        imageView.layer.renderInContext(context)
        guard let result = UIGraphicsGetImageFromCurrentImageContext() else { return nil }
        UIGraphicsEndImageContext()
        return result
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 评论什么是差异会很好 (2认同)

Jay*_*Jay 5

这是user4261201的答案,但很快,我目前正在使用:

func compressImage (_ image: UIImage) -> UIImage {

    let actualHeight:CGFloat = image.size.height
    let actualWidth:CGFloat = image.size.width
    let imgRatio:CGFloat = actualWidth/actualHeight
    let maxWidth:CGFloat = 1024.0
    let resizedHeight:CGFloat = maxWidth/imgRatio
    let compressionQuality:CGFloat = 0.5

    let rect:CGRect = CGRect(x: 0, y: 0, width: maxWidth, height: resizedHeight)
    UIGraphicsBeginImageContext(rect.size)
    image.draw(in: rect)
    let img: UIImage = UIGraphicsGetImageFromCurrentImageContext()!
    let imageData:Data = UIImageJPEGRepresentation(img, compressionQuality)!
    UIGraphicsEndImageContext()

    return UIImage(data: imageData)!

}
Run Code Online (Sandbox Code Playgroud)


Mau*_*tel 5

斯威夫特4.2

  let imagedata = yourImage.jpegData(compressionQuality: 0.1)!
Run Code Online (Sandbox Code Playgroud)


Iro*_*ney 5

我认为这里问题的核心是如何在上传到服务器之前可靠地将 UIImage 的数据缩小到一定大小,而不是仅仅缩小 UIImage 本身。

func jpegData(compressionQuality: CGFloat) -> Data?如果您不需要压缩到特定大小,则使用效果很好。但是,对于某些情况,我发现能够压缩到低于某个指定的文件大小非常有用。在这种情况下,jpegData这是不可靠的,并且以这种方式迭代压缩图像会导致文件大小趋于稳定(并且可能非常昂贵)。相反,我更喜欢像Leo 的回答那样减小 UIImage 本身的大小,然后转换为 jpegData 并迭代检查减小的大小是否低于我选择的值(在我设置的边距内)。我根据当前文件大小与所需文件大小的比率调整压缩步骤乘数,以加快最昂贵的第一次迭代(因为此时文件大小最大)。

雨燕5

extension UIImage {
    func resized(withPercentage percentage: CGFloat, isOpaque: Bool = true) -> UIImage? {
        let canvas = CGSize(width: size.width * percentage, height: size.height * percentage)
        let format = imageRendererFormat
        format.opaque = isOpaque
        return UIGraphicsImageRenderer(size: canvas, format: format).image {
            _ in draw(in: CGRect(origin: .zero, size: canvas))
        }
    }
    
    func compress(to kb: Int, allowedMargin: CGFloat = 0.2) -> Data {
        guard kb > 10 else { return Data() } // Prevents user from compressing below a limit (10kb in this case).
        let bytes = kb * 1024
        var compression: CGFloat = 1.0
        let step: CGFloat = 0.05
        var holderImage = self
        var complete = false
        while(!complete) {
            guard let data = holderImage.jpegData(compressionQuality: 1.0) else { break }
            let ratio = data.count / bytes
            if data.count < Int(CGFloat(bytes) * (1 + allowedMargin)) {
                complete = true
                return data
            } else {
                let multiplier:CGFloat = CGFloat((ratio / 5) + 1)
                compression -= (step * multiplier)
            }
            guard let newImage = holderImage.resized(withPercentage: compression) else { break }
            holderImage = newImage
        }
        
        return Data()
    }
}
 
Run Code Online (Sandbox Code Playgroud)

及用法:

let data = image.compress(to: 1000)
Run Code Online (Sandbox Code Playgroud)