Compress images to reduce file size
Asked Answered
E

3

10

I'm building an app that lets the user take a photo or select one from the library on the iPhone and upload it to the Parse backend.

The problem I'm facing is regarding to the size of the file.

I've read about what big players like Facebook, Twitter, Instagram, and Google do regarding resolution and file size but I can't get close to that.

I'm sure they have the best code and tools to do that but I'll be happy to implement it as good as possible with iOS regular processes.

This is what I'm doing right now:

- (UIImage *)normalResImageForAsset:(ALAsset*)asset
{
    // Convert ALAsset to UIImage
    UIImage *image = [self highResImageForAsset:asset];

    // Determine output size
    CGFloat maxSize = 1024.0f;
    CGFloat width = image.size.width;
    CGFloat height = image.size.height;
    CGFloat newWidth = width;
    CGFloat newHeight = height;

    // If any side exceeds the maximun size, reduce the greater side to 1200px and proportionately the other one
    if (width > maxSize || height > maxSize) {
        if (width > height) {
            newWidth = maxSize;
            newHeight = (height*maxSize)/width;
        } else {
            newHeight = maxSize;
            newWidth = (width*maxSize)/height;
        }
    }

    // Resize the image
    CGSize newSize = CGSizeMake(newWidth, newHeight);
    UIGraphicsBeginImageContext(newSize);
    [image drawInRect:CGRectMake(0,0,newSize.width,newSize.height)];
    UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();

    // Set maximun compression in order to decrease file size and enable faster uploads & downloads
    NSData *imageData = UIImageJPEGRepresentation(newImage, 0.0f);
    UIImage *processedImage = [UIImage imageWithData:imageData];

    return processedImage;
}

I'm trying to make 1024px the maximun allowed size (both with ot height) to start some restrictions there and then I'm applying maximun compression to reduce size.

This works and cuts aproximately 50% of the image size without really damaging JPEGs but it's still a lot. Specially if photos are taken with the phone's camera and uploaded. The processed image can still easly have 1MB size which is way too much.

I'm guessing that I could be missing some useful step or using the wrong technique.

Any feedback would be greatly appreciated.

Embryonic answered 8/9, 2013 at 8:50 Comment(4)
I am wondering about the round trip from UIImage to NSData and back to UIImage. Did you check [imageData length] before initializing a new UIImage? Maybe you want to return the encoded NSData object from this function, so that you can upload the byte array to the server.Amathiste
Allow the user to choose the image size and warn them about the data size to upload if required.Thaumaturge
@Thaumaturge But the OP is right that at 1024x768px, a fully compressed jpeg image should not take up 1MB.Amathiste
I'm using Aviary photo editor and following the docs examples this is the way to pass them the data. Maybe not the only way but this can explain why I have the round trip. When using PNG files compression works as expected but JPEG are maybe too compressed already to work like this.Overplus
L
10

I had a similar problem and I also thought that the compression wasn't working. It turned out elsewhere in my code I was writing the file out to disk using different compression. You might be doing the same thing with the data this function returns. A good way to check that indeed the compression is effective is to do something like this:

NSData *imgData1 = UIImageJPEGRepresentation(newImage, 1.0f);
NSLog(@"1.0 size: %d", imgData1.length);

NSData *imgData2 = UIImageJPEGRepresentation(newImage, 0.7f);
NSLog(@"0.7 size: %d", imgData2.length);

NSData *imgData3 = UIImageJPEGRepresentation(newImage, 0.4f);
NSLog(@"0.4 size: %d", imgData3.length);

NSData *imgData4 = UIImageJPEGRepresentation(newImage, 0.0f);
NSLog(@"0.0 size: %d", imgData4.length);

// Don't convert NSData back to UIImage before writing to disk
[imgData4 writeToFile:imagePath atomically:YES];

I'm using an image that is 640x480 and I get file sizes ranging from 325 kB (for 1.0) down to 18 kB (for 0.0)

Livelihood answered 16/2, 2014 at 7:34 Comment(1)
Once its uploaded to server and it may not be able to reconstruct actual image. May not be useful in all scenarios.Gerundive
A
2

Swift-3 Version of @codeMonkey :-

It work perfect in compression of image .

func compressImage() -> UIImage {

        let oldImage = UIImage(named: "background.jpg")
        var imageData =  Data(UIImagePNGRepresentation(oldImage!)! )
        print("***** Uncompressed Size \(imageData.description) **** ")

        imageData = UIImageJPEGRepresentation(oldImage!, 0.025)!
        print("***** Compressed Size \(imageData.description) **** ")

        let image = UIImage(data: imageData)
        return image!

    }

enter image description here

Altogether answered 21/12, 2016 at 13:33 Comment(4)
What about the quality of the image?Gerundive
By compression there will be change in quality.Altogether
It reduces the quality of the picture. So, it may not be right solution.Gerundive
thank you for this quick easy solution... degrading the quality is not a factor for me since my compression is based on simply 'sharing' to 3rd part social mediaTanishatanitansy
B
0
Please Try below answer.

+(UIImage *)compressImage:(UIImage *)image{
float actualHeight = image.size.height;
float actualWidth = image.size.width;
float maxHeight = 1136.0f;
float maxWidth = 640.0f;
float imgRatio = actualWidth/actualHeight;
float maxRatio = maxWidth/maxHeight;
float compressionQuality = 1;//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;
    }
}else{
    actualHeight = maxHeight;
    actualWidth = maxWidth;
    compressionQuality = 1;
}

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];
}
Baryton answered 23/10, 2017 at 5:41 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.