Two image compression methods

Two image compression methods: compressed image Quality, compressed image Size.

  • Compressed picture quality

NSData *data = UIImageJPEGRepresentation(image, compression);
UIImage *resultImage = [UIImage imageWithData:data];
Copy the code
  • Compress images by reducing the quality of JPEG images by converting UIImage and NSData to each other. UIImageJPEGRepresentation: : the second parameter value compression will 0.0 ~ 1.0, the smaller the value of said picture quality is lower, the smaller image files nature
  • Compress image size

UIGraphicsBeginImageContext(size);
[image drawInRect:CGRectMake(0, 0, size.width, size.height)];
resultImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
Copy the code

Given the required image size, resultImage is the original image image drawn with size size.

  • Compress images to make the image file smaller than the specified size

If you do not have high requirements for image clarity and fast upload and download, compress the image before uploading it. How much you compress depends on the situation, but you generally set a maximum image file size, such as 100 KB. Images can be compressed in two ways. Assuming that the NSData object transformed from the image is data, the size of the image can be obtained by data.length.

  • Compressed picture quality

The easy way to think about it is to reduce the image quality gradually through a loop until the image is a little less than the specified size (maxLength).

+ (UIImage *)compressImageQuality:(UIImage *)image toByte:(NSInteger)maxLength {
    CGFloat compression = 1;
    NSData *data = UIImageJPEGRepresentation(image, compression);
    while(data.length > maxLength && compression > 0) {compression -= 0.02; data = UIImageJPEGRepresentation(image, compression); // When compression less than a value, this code dose not work } UIImage *resultImage = [UIImage imageWithData:data];return resultImage;
}
Copy the code

This cycle times, low efficiency, long time. It can be optimized by dichotomy.

+ (UIImage *)compressImageQuality:(UIImage *)image toByte:(NSInteger)maxLength {
    CGFloat compression = 1;
    NSData *data = UIImageJPEGRepresentation(image, compression);
    if (data.length < maxLength) return image;
    CGFloat max = 1;
    CGFloat min = 0;
    for (int i = 0; i < 6; ++i) {
        compression = (max + min) / 2;
        data = UIImageJPEGRepresentation(image, compression);
        if(data.length < maxLength * 0.9) {min = compression; }else if (data.length > maxLength) {
            max = compression;
        } else {
            break;
        }
    }
    UIImage *resultImage = [UIImage imageWithData:data];
    return resultImage;
}
Copy the code
static func compressImageQuality(_ image: UIImage, toByte maxLength: Int) -> UIImage {
    var compression: CGFloat = 1
    guard var data = UIImageJPEGRepresentation(image, compression),
        data.count > maxLength else { return image }
    
    var max: CGFloat = 1
    var min: CGFloat = 0
    for _ in0.. <6 { compression = (max + min) / 2 data = UIImageJPEGRepresentation(image, compression)!ifCGFloat(data.count) < CGFloat(maxLength) * 0.9 {min = compression}else if data.count > maxLength {
            max = compression
        } else {
            break}}return UIImage(data: data)!
}
Copy the code

When the image size is smaller than maxLength and larger than maxLength * 0.9, the compression is not continued. At most 6 compression times, 1/(2^6) = 0.015625 < 0.02, which can also achieve the effect of 0.02 reduction in compression per cycle. In this way, the number of compression is less than that of cyclic compression reduction, and the time is shorter. It is important to note that when the image quality is below a certain level, further compression has no effect. In other words, compression continues to decrease, and data does not continue to decrease. The advantage of compressed picture quality is that, as far as possible to retain the picture clarity, the picture will not be obvious blurred; The disadvantage is that there is no guarantee that the image will be compressed to a smaller size than specified.

Compress image size

As before, it is easy to think of a way to reduce the image size gradually through a loop until the image is slightly smaller than the specified size (maxLength). Specific code omitted. The same problem is the number of cycles, low efficiency, long time. Dichotomy can be used to improve efficiency, the specific code omitted. Here is another method, which is better than dichotomy, compresses the image less than the specified size (not just < maxLength, > maxLength * 0.9).

+ (UIImage *)compressImageSize:(UIImage *)image toByte:(NSUInteger)maxLength {
    UIImage *resultImage = image;
    NSData *data = UIImageJPEGRepresentation(resultImage, 1);
    NSUInteger lastDataLength = 0;
    while(data.length > maxLength && data.length ! = lastDataLength) { lastDataLength = data.length; CGFloat ratio = (CGFloat)maxLength / data.length; CGSize size = CGSizeMake((NSUInteger)(resultImage.size.width * sqrtf(ratio)), (NSUInteger)(resultImage.size.height * sqrtf(ratio))); // Use NSUInteger to prevent white blank UIGraphicsBeginImageContext(size); // Use image to draw (drawInRect:), image is larger but more compression time // Use result image to draw, image is smaller but less compression time [resultImage drawInRect:CGRectMake(0, 0, size.width, size.height)]; resultImage = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); data = UIImageJPEGRepresentation(resultImage, 1); }return resultImage;
}
Copy the code
static func compressImageSize(_ image: UIImage, toByte maxLength: Int) -> UIImage {
    guard var data = UIImageJPEGRepresentation(image, 1) else { return image }
    
    var resultImage: UIImage = image
    var lastDataLength: Int = 0
    whiledata.count > maxLength, data.count ! = lastDataLength { lastDataLength = data.countlet ratio: CGFloat = CGFloat(maxLength) / CGFloat(data.count)
        let size: CGSize = CGSize(width: Int(resultImage.size.width * sqrt(ratio)),
                                height: Int(resultImage.size.height * sqrt(ratio)))
        UIGraphicsBeginImageContext(size)
        resultImage.draw(in: CGRect(x: 0, y: 0, width: size.width, height: size.height))
        resultImage = UIGraphicsGetImageFromCurrentImageContext()!
        UIGraphicsEndImageContext()
        data = UIImageJPEGRepresentation(resultImage, 1)!
    }
    return resultImage
}
Copy the code

[resultImage drawInRect:CGRectMake(0, 0, size.width, size.height)]; It can be drawn using the new image resultImage, or it can be drawn using the original image. If the original image is used, the compressed image is closer to the specified size, but the compression times are more and the compression time is longer. A picture with a size of 6064 KB and compressed image size, the original drawing and the new drawing results are as follows

Specify size (KB) Size of original drawing after compression (KB) Original drawing compression times New drawing size after compression (KB) New graph plots compression times
500 498 6 498 3
300 299 4 296 3
100 99 5 98 3
50 49 6 48 3

The compressed size of the two drawing methods is very close to the specified size, but the compression times of the original drawing can reach twice that of the new drawing. It is recommended to use a new diagram to reduce the number of compression. After compression, the image is obviously blurred than the compression quality. CGSize size = CGSizeMake((NSUInteger)(resultimage.size. Width * SQRTF (ratio))), (NSUInteger)(resultImage.size.height * sqrtf(ratio))); For each drawing size, convert width and height to integers to prevent white edges in the drawn image.

Compressed image size can make the image smaller than the specified size, but will make the image significantly blurred (than the quality of the compressed image).

  • Combination of two image compression methods

To ensure image clarity, you are advised to select compressed image quality. If the image must be smaller than the specified size, the compressed image size can be satisfied. For the latter requirement, you can also compress the image quality first, if it is less than the specified size, you can get a clear image, otherwise you can compress the image size.

+ (UIImage *)compressImage:(UIImage *)image toByte:(NSUInteger)maxLength {
    // Compress by quality
    CGFloat compression = 1;
    NSData *data = UIImageJPEGRepresentation(image, compression);
    if (data.length < maxLength) return image;
    
    CGFloat max = 1;
    CGFloat min = 0;
    for (int i = 0; i < 6; ++i) {
        compression = (max + min) / 2;
        data = UIImageJPEGRepresentation(image, compression);
        if(data.length < maxLength * 0.9) {min = compression; }else if (data.length > maxLength) {
            max = compression;
        } else {
            break;
        }
    }
    UIImage *resultImage = [UIImage imageWithData:data];
    if (data.length < maxLength) return resultImage;
    
    // Compress by size
    NSUInteger lastDataLength = 0;
    while(data.length > maxLength && data.length ! = lastDataLength) { lastDataLength = data.length; CGFloat ratio = (CGFloat)maxLength / data.length; CGSize size = CGSizeMake((NSUInteger)(resultImage.size.width * sqrtf(ratio)), (NSUInteger)(resultImage.size.height * sqrtf(ratio))); // Use NSUInteger to prevent white blank UIGraphicsBeginImageContext(size); [resultImage drawInRect:CGRectMake(0, 0, size.width, size.height)]; resultImage = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); data = UIImageJPEGRepresentation(resultImage, compression); }return resultImage;
}
Copy the code
static func compressImage(_ image: UIImage, toByte maxLength: Int) -> UIImage {
    var compression: CGFloat = 1
    guard var data = UIImageJPEGRepresentation(image, compression),
        data.count > maxLength else { return image }
    
    // Compress by size
    var max: CGFloat = 1
    var min: CGFloat = 0
    for _ in0.. <6 { compression = (max + min) / 2 data = UIImageJPEGRepresentation(image, compression)!ifCGFloat(data.count) < CGFloat(maxLength) * 0.9 {min = compression}else if data.count > maxLength {
            max = compression
        } else {
            break
        }
    }
    var resultImage: UIImage = UIImage(data: data)!
    if data.count < maxLength { return resultImage }
    
    // Compress by size
    var lastDataLength: Int = 0
    whiledata.count > maxLength, data.count ! = lastDataLength { lastDataLength = data.countlet ratio: CGFloat = CGFloat(maxLength) / CGFloat(data.count)
        let size: CGSize = CGSize(width: Int(resultImage.size.width * sqrt(ratio)),
                                height: Int(resultImage.size.height * sqrt(ratio)))
        UIGraphicsBeginImageContext(size)
        resultImage.draw(in: CGRect(x: 0, y: 0, width: size.width, height: size.height))
        resultImage = UIGraphicsGetImageFromCurrentImageContext()!
        UIGraphicsEndImageContext()
        data = UIImageJPEGRepresentation(resultImage, compression)!
    }
    return resultImage
}
Copy the code

The resources

Title: iOS image compression method

Author: zhaoguian

Time: 16:55, January 09, 2019

Jane: www.jianshu.com/u/89307b77a…