I would like to compress images (camera/photo library) and then send it to the server. I know I can compress by height and width, but I would like to compress the images by
One way to do it, is to re-compress the file in a loop, until you find the desired size. You could first find height and width, and guess the compression factor (larger image more compression) then after you compress it, check the size, and split the difference again.
I know this is not super efficient, but I do not believe there is a single call to achieve a image of a specific size.
Swift 4:
extension UIImage {
func compressTo(bytes: Int) -> UIImage {
var compression: CGFloat = 0.9
let maxCompression: CGFloat = 0.1
let maxSize: Int = bytes * 1024
var imageData = jpegData(compressionQuality: compression)!
while imageData.count > maxSize && compression > maxCompression {
compression -= 0.1
imageData = jpegData(compressionQuality: compression)!
}
return UIImage(data: imageData)!
}
}
Heres some example code that will attempt to compress an image for you so that it doesn't exceed either a max compression or maximum file size
CGFloat compression = 0.9f;
CGFloat maxCompression = 0.1f;
int maxFileSize = 250*1024;
NSData *imageData = UIImageJPEGRepresentation(yourImage, compression);
while ([imageData length] > maxFileSize && compression > maxCompression)
{
compression -= 0.1;
imageData = UIImageJPEGRepresentation(yourImage, compression);
}
After doing a few tests, I was able to find A relationship between image size and compression value. Since this relationship is linear for all values where the compression is less than 1, I created an algorithm to try to always compress images to a certain value.
//Use 0.99 because at 1, the relationship between the compression and the file size is not linear
NSData *image = UIImageJPEGRepresentation(currentImage, 0.99);
float maxFileSize = MAX_IMAGE_SIZE * 1024;
//If the image is bigger than the max file size, try to bring it down to the max file size
if ([image length] > maxFileSize) {
image = UIImageJPEGRepresentation(currentImage, maxFileSize/[image length]);
}
I took the answer of @kgutteridge and made a similar solution for Swift 3.0 using recursive:
extension UIImage {
static func compress(image: UIImage, maxFileSize: Int, compression: CGFloat = 1.0, maxCompression: CGFloat = 0.4) -> Data? {
if let data = UIImageJPEGRepresentation(image, compression) {
let bcf = ByteCountFormatter()
bcf.allowedUnits = [.useMB] // optional: restricts the units to MB only
bcf.countStyle = .file
let string = bcf.string(fromByteCount: Int64(data.count))
print("Data size is: \(string)")
if data.count > (maxFileSize * 1024 * 1024) && (compression > maxCompression) {
let newCompression = compression - 0.1
let compressedData = self.compress(image: image, maxFileSize: maxFileSize, compression: newCompression, maxCompression: maxCompression)
return compressedData
}
return data
}
return nil
}
}
Here, JPEGRepresentation is quite memory consuming and if we use in Loop so it is extremely high memory consuming. So use below code & ImageSize won't be more then 200KB.
UIImage* newImage = [self captureView:yourUIView];
- (UIImage*)captureView:(UIView *)view {
CGRect rect = view.bounds;
UIGraphicsBeginImageContext(rect.size);
CGContextRef context = UIGraphicsGetCurrentContext();
[view.layer renderInContext:context];
UIImage* img = [UIImage alloc]init];
img = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
NSLog(@"img=%@",img);
return img;
}