Example code of iOS picture compression method

  • 2021-10-24 23:57:43
  • OfStack

Two methods of compressing pictures: compressed picture quality (Quality) and compressed picture size (Size).

Compressed picture quality


NSData *data = UIImageJPEGRepresentation(image, compression);
UIImage *resultImage = [UIImage imageWithData:data];

Through the conversion of UIImage and NSData, the image quality of JPEG is reduced to compress the image. UIImageJPEGRepresentation:: The value of the second parameter compression is 0.0 ~ 1.0. The smaller the value, the lower the picture quality and the smaller the picture file.

Compressed picture size


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

Given the required picture size size, resultImage is a picture drawn from the original image to the size of size.

Compress the picture so that the picture file is smaller than the specified size

If the picture definition requirements are not high, and the uploading and downloading speed of the picture is fast, the picture needs to be compressed before uploading the picture. To what extent it is compressed depends on the specific situation, but 1 will generally set the maximum value of 1 picture file, such as 100 KB. There are two ways to compress pictures by appealing. Assuming that the NSData object converted from the picture is data, it is passed through data.length You can get the byte size of the picture.

Compressed picture quality

It is easy to think of a way to gradually reduce the picture quality through a loop until the picture is slightly smaller 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;
}

In this way, there are many cycles, low efficiency and long time consumption.

It can be optimized by 2-point method.


+ (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;
}

When the image size is smaller than maxLength and larger than maxLength * 0.9, no further compression is performed. Compress up to 6 times, 1/(2 ^ 6) = 0.015625 < 0.02, and the effect of reducing compression by 0.02 per cycle can also be achieved. This compression number is less than the cyclic reduction compression, and it takes less time. It should be noted that when the picture quality is lower than 1 certain level, continuous compression has no effect. That is to say, compression continues to decrease, and data does not continue to decrease. The advantage of compressed picture quality is that the picture clarity is preserved as much as possible, and the picture will not be obviously blurred; The disadvantage is that there is no guarantee that the picture will be smaller than the specified size after compression.

Compressed picture size

As before, it is easier to think of a way to gradually reduce the size of the picture through a loop until the picture is slightly smaller than the specified size (maxLength). Specific code is omitted. The same problem is that there are many cycles, low efficiency and long time consumption. You can use the 2-point method to improve efficiency, and the specific code is omitted. Here is another method, which is better than the 2-point method, has less compression times, and can make the picture just smaller than the specified size after compression (not only < 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;
}

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

指定大小(KB) 原图绘制压缩后大小(KB) 原图绘制压缩次数 新图绘制压缩后大小(KB) 新图绘制压缩次数
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 rendering methods is very close to the specified size, but the compression times of the original rendering can reach twice that of the new rendering. It is recommended to use new drawing to reduce the number of compressions. The compressed picture is obviously blurred than the compressed picture.

It is important to note that the code for drawing dimensions CGSize size = CGSizeMake((NSUInteger)(resultImage.size.width * sqrtf(ratio)), (NSUInteger)(resultImage.size.height * sqrtf(ratio))); For each drawing size size, the width width and the height height should be converted into integers to prevent the drawn picture from having white edges.

Compressed picture size can make the picture smaller than the specified size, but it will make the picture obviously blurred (blurred by the quality of compressed picture).

Combination of two image compression methods

If you want to ensure the clarity of the picture, it is recommended to choose compressed picture quality. If you want to make Picture 1 smaller than the specified size, the compressed picture size can be satisfied. For the latter one, you can also compress the picture quality first. If it is less than the specified size, you can get a clear picture, otherwise, you can compress the picture size again.


+ (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;
}


Related articles: