DonDyck
DonDyck

Reputation: 1471

Image compression by size - iPhone SDK

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 size to a fixed size (200 KB) only and keep the original height and width. The scale factor in JPEGRepresentation does not represent the size and only compression quality. How can I achieve this (compress to a fixed size) without using any third party library?

Upvotes: 26

Views: 32481

Answers (7)

Winter
Winter

Reputation: 1114

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)!
    }
}

Upvotes: 1

Mindhavok
Mindhavok

Reputation: 457

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

Upvotes: 1

Banky Adebajo
Banky Adebajo

Reputation: 37

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]);
}

Upvotes: 0

Superdev
Superdev

Reputation: 562

- (UIImage *)resizeImageToSize:(CGSize)targetSize
{
    UIImage *sourceImage = captureImage;
    UIImage *newImage = nil;

    CGSize imageSize = sourceImage.size;
    CGFloat width = imageSize.width;
    CGFloat height = imageSize.height;

    CGFloat targetWidth = targetSize.width;
    CGFloat targetHeight = targetSize.height;

    CGFloat scaleFactor = 0.0;
    CGFloat scaledWidth = targetWidth;
    CGFloat scaledHeight = targetHeight;

    CGPoint thumbnailPoint = CGPointMake(0.0,0.0);

    if (CGSizeEqualToSize(imageSize, targetSize) == NO) {

        CGFloat widthFactor = targetWidth / width;
        CGFloat heightFactor = targetHeight / height;

        if (widthFactor < heightFactor)
            scaleFactor = widthFactor;
        else
            scaleFactor = heightFactor;

        scaledWidth  = width * scaleFactor;
        scaledHeight = height * scaleFactor;

        // make image center aligned
        if (widthFactor < heightFactor)
        {
            thumbnailPoint.y = (targetHeight - scaledHeight) * 0.5;
        }
        else if (widthFactor > heightFactor)
        {
            thumbnailPoint.x = (targetWidth - scaledWidth) * 0.5;
        }
    }

    UIGraphicsBeginImageContext(targetSize);
    CGRect thumbnailRect = CGRectZero;
    thumbnailRect.origin = thumbnailPoint;
    thumbnailRect.size.width  = scaledWidth;
    thumbnailRect.size.height = scaledHeight;

    [sourceImage drawInRect:thumbnailRect];
    newImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();

    if(newImage == nil)
        NSLog(@"could not scale image");

    return newImage ;
}

Upvotes: -1

krunal
krunal

Reputation: 482

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

Upvotes: 1

kgutteridge
kgutteridge

Reputation: 8981

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);
}

Upvotes: 63

nycynik
nycynik

Reputation: 7541

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.

Upvotes: 3

Related Questions