Is there any way to dynamically compress an UIImage in iPhone [duplicate] - ios

I m taking images from photo library.I have large images of 4-5 mb but i want to compress those images.As i need to store those images in local memory of iphone.for using less memory or for getting less memory warning i need to compress those images.
I don't know how to compress images and videos.So i want to know hot to compress images?
UIImage *image = [info objectForKey:#"UIImagePickerControllerOriginalImage"];
NSData* data = UIImageJPEGRepresentation(image,1.0);
NSLog(#"found an image");
NSString *path = [destinationPath stringByAppendingPathComponent:[NSString stringWithFormat:#"%#.jpeg", name]];
[data writeToFile:path atomically:YES];
This is the code for saving my image. I dont want to store the whole image as its too big. So, I want to compress it to a much smaller size as I'll need to attach multiple images.
Thanks for the reply.

You can choose a lower quality for JPEG encoding
NSData* data = UIImageJPEGRepresentation(image, 0.8);
Something like 0.8 shouldn't be too noticeable, and should really improve file sizes.
On top of this, look into resizing the image before making the JPEG representation, using a method like this:
+ (UIImage *)imageWithImage:(UIImage *)image scaledToSize:(CGSize)newSize {
UIGraphicsBeginImageContext(newSize);
[image drawInRect:CGRectMake(0, 0, newSize.width, newSize.height)];
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return newImage;
}
Source: The simplest way to resize an UIImage?

UIImageJPEGRepresentation(UIImage,Quality);
1.0 means maximum Quality and 0 means minimum quality.
SO change the quality parameter in below line to reduce file size of the image
NSData* data = UIImageJPEGRepresentation(image,1.0);

NSData *UIImageJPEGRepresentation(UIImage *image, CGFloat compressionQuality);
OR
NSData *image_Data=UIImageJPEGRepresentation(image_Name,compressionQuality);
return image as JPEG. May return nil if image has no CGImageRef or invalid bitmap format. compressionQuality is 0(most) & 1(least).

Related

How to compress images in iOS?

I have been looking through every stack overflow post and video online and I can't find a solution that works correctly. Right now the user selects a photo that I want to compress before uploading to an AWS S3 bucket. The upload works perfectly but for some reason, the compressed image is larger than the original image! For example, if the user selects a 9KB photo, when I upload to S3 the photo is 28.5KB. I tried a different photo and it's 48KB and after "compression" on S3 its 378.9KB! (I am using the latest software version of everything and compiling with the simulator)
I want to compress the original image as much as I can before uploading.
This is what I have so far:
How I "compress" the image:
UIImage *compressedProductImage;
NSData *NSproductImage;
NSUInteger productImageSize;
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary<NSString *,id> *)info{
self.productImage = info[UIImagePickerControllerOriginalImage];
[self.productImageImageView setImage:self.productImage];
[self dismissViewControllerAnimated:YES completion:nil];
NSproductImage = UIImageJPEGRepresentation(self.productImage, 0.5f);
productImageSize = [NSproductImage length];
compressedProductImage = [UIImage imageWithData: NSproductImage];
How I upload the photo:
//Convert product UIImage
NSArray *productImagePaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *productImageFilePath = [[productImagePaths objectAtIndex:0] stringByAppendingPathComponent:[NSString stringWithFormat:#".png"]];
[UIImagePNGRepresentation(compressedProductImage) writeToFile:productImageFilePath atomically:YES];
NSURL *productImageFileUrl = [NSURL fileURLWithPath:productImageFilePath];
uploadRequest.body = productImageFileUrl;//Needs to be a NSURL
uploadRequest.bucket = AWS_BUCKET_NAME;
uploadRequest.key = productImageKey;
uploadRequest.contentType = #"image/png";
uploadRequest.ACL = AWSS3BucketCannedACLPublicRead;
[[transferManager upload:uploadRequest] continueWithExecutor:[AWSExecutor mainThreadExecutor] withBlock:^id(AWSTask *task) {
if (task.error != nil) {
NSLog(#"%s %#","Error uploading (product image):", uploadRequest.key);
}else{
NSLog(#"Product image upload completed");
}
return nil;
}];
As rmaddy points out, you're taking the picked image, converting it to JPEG, converting back to a UIImage (losing any benefit of the JPEG compression), and then converting it to a PNG, which offers modest compression, generally far less compression than the original JPEG from the users photo library.
You have a few options.
You can retrieve the original imageData from the asset in your photos library as shown in https://stackoverflow.com/a/32845656/1271826, thereby avoiding the round-tripping it through a UIImage at all. Thus, you preserve the quality of the original image, preserve the meta data associated with this image, and enjoy the decent compression of the original asset.
You could take the picked image as a UIImage and do a combination of:
reduce the dimensions of the image before you call UIImageJPEGRepresentation (see https://stackoverflow.com/a/10491692/1271826 for sample algorithm); and/or
use UIImageJPEGRepresentation with a quality less than 1.0, where the smaller the number, the more compression but the more image quality loss.
You don't actually compress anything. You start with a UIImage. This is a full pixel by pixel representation that takes (typically) width x height x 4 bytes.
You then convert that to a JPG with some compression. So NSproductImage is a much smaller representation, in memory, of the JPG version of the image. This is the supposed smaller size you see where you think it is now compressed.
But then you convert that JPG data back into a UIImage as compressedProductImage. This new UIImage still has the same width and heigh of the original UIImage. As a result, it still takes the same width x height x 4 bytes as the original. It's just of lower quality than the original due to the JPG compression.
Now you convert the updated UIImage into a PNG. Since PNG is lossless, it doesn't compress nearly as much as the JPG attempt. You then send this larger PNG version of the image to Amazon.
You should first remove the pointless code that first converts to JPG and then back to UIImage.
At this point you should either live with the size of the PNG or use JPG instead and send the smaller JPG to Amazon.
Another option would be to scale the image before sending it to Amazon.

How to compress image size using UIImagePNGRepresentation - iOS?

I'm using UIImagePNGRepresentation to save an image. The result image is of size 30+ KB and this is BIG in my case.
I tried using UIImageJPEGRepresentation and it allows to compress image, so image saves in < 5KB size, which is great, but saving it in JPEG gives it white background, which i don't want (my image is circular, so I need to save it with transparent background).
How can I compress image size, using UIImagePNGRepresentation?
PNG uses lossless compression, that's why UIImagePNGRepresentation does not accept compressionQuality parameter like UIImageJPEGRepresentation does. You might get a bit smaller PNG file with different tools, but nothing like with JPEG.
May be this will help you out:
- (void)resizeImage:(UIImage*)image{
NSData *finalData = nil;
NSData *unscaledData = UIImagePNGRepresentation(image);
if (unscaledData.length > 5000.0f ) {
//if image size is greater than 5KB dividing its height and width maintaining proportions
UIImage *scaledImage = [self imageWithImage:image andWidth:image.size.width/2 andHeight:image.size.height/2];
finalData = UIImagePNGRepresentation(scaledImage);
if (finalData.length > 5000.0f ) {
[self resizeImage:scaledImage];
}
//scaled image will be your final image
}
}
Resizing image
- (UIImage*)imageWithImage:(UIImage*)image andWidth:(CGFloat)width andHeight:(CGFloat)height
{
UIGraphicsBeginImageContext( CGSizeMake(width, height));
[image drawInRect:CGRectMake(0,0,width,height)];
UIImage* newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext() ;
return newImage;
}

Compressing UIImage into JPEG image data multiple times

I am debugging a piece of code where an UIImage may be gone through UIImageJPEGRepresentation multiple times, I thought that must be a bug and the image quality will get worsen, but surprisingly we can't see the difference visually.
So I did a test, loading an image, and try to let it go through UIImageJPEGRepresentation 1000 times, surprisingly, whether 1 or 1000 times doesn't really make a difference in the image quality visually, why is that so?
This is the testing code:
UIImage *image = [UIImage imageNamed:#"photo.jpeg"];
// Create a data reference here for the for loop later
// First JPEG compression here
// I would imagine the data here already has low image quality
NSData *data = UIImageJPEGRepresentation(image, 0);
for(int i=0; i<1000; i++)
{
// Convert the data with low image quality to UIImage
UIImage *image = [UIImage imageWithData:data];
// Compress the image into a low quality data again
// at this point i would imagine the image get even more low quality, like u resaved a jpeg twice in phootshop
data = UIImageJPEGRepresentation(image, 0);
}
// up to this point I would imagine the "data" has gone through JPEG compression 1000 times
// like you resave a jpeg as a jpeg in photoshop 1000 times, it should look like a piece of crap
UIImage *imageFinal = [UIImage imageWithData:data];
UIImageView *view = [[UIImageView alloc] initWithImage:imageFinal];
[self.view addSubview:view];
// but it didn't, the final image looks like it has only gone through the jpeg compression once.
EDIT: my doubt can be summarised into a simpler code, if you do this in objectiveC:
UIImage *image1 = an image..
NSData *data1 = UIImageJPEGRepresentation(image1, 0);
UIImage *image2 = [UIImage imageWithData:data1];
NSData *data2 = UIImageJPEGRepresentation(image2, 0);
UIImage *imageFinal = [UIImage imageWithData:data2];
Did imageFinal gone through JPEG compression twice?
As you know, JPG compression works by altering the image to produce smaller file size. The reason why you don't see progressively worse quality is because you're using the same compression setting each time.
The algorithm alters the source image just enough to fit into the compression profile - in other words, compressing the result of 50% JPG again at 50% will produce the same image, because the image doesn't need to be altered any more.
You can test this in Photoshop - save a photo out at say 30% quality JPG. Reopen the file you just saved, and go to Save for Web - flip between PNG (uncompressed/original) and JPG 30% - there will be no difference.
Hope this helps.
All types of compression will ideally reduce the size of an image. There are two types of compression which describes how they affects images:
Lossy Compression:
Lossy compression will reduces the size of the image by removing some data from it. This generally cause, effect the quality of the image, which means it reduce your image quality
Lossless Compression:
Lossless compression reduce the size of the image by changing the way in which the data is stored. Therefore this type of compression will make no change in the image quality.
Please check out the compression type you are using.
This may help you in decrease the image size. put the number from yourself how many times you want to perform loop;
UIImage *image = [UIImage imageNamed:#"photo.jpeg"];
for(int i=100; i>0; i--)
{
UIImage *image = [UIImage imageWithData:data];
NSData *data = UIImageJPEGRepresentation(image, (0.1 * i);
NSLog(#"%d",data.length);
}

Xcode iOS Generate .png file and save it using NSFileManager

I am making an iOS app and I got a UIImage - I want to compress it into .png file and save it to the app's documents folder - I already have the path and all I need is how to convert the UIImage to .png and save it.
Thanks,
Matan.
so the code is:
UIImage *yourImage = ...; //the image you have
NSString *targetPath = ...; // something like ~/Library/Documents/myImage.png
[UIImagePNGRepresentation(yourImage) writeToFile:targetPath atomically:YES];
For PNG:
UIImagePNGRepresentation
Returns the data for the specified image in PNG format
NSData * UIImagePNGRepresentation (
UIImage *image
);
If you wanted JPEG instead:
UIImageJPEGRepresentation
Returns the data for the specified image in JPEG format.
NSData * UIImageJPEGRepresentation (
UIImage *image,
CGFloat compressionQuality
);
Image compression form is JPEG you can use different quality of jpg image
// for getting png image
NSData*theImageData=UIImagePNGRepresentation(theImage);
// for JPG compression change fill value between less than 1
NSData*theImageData=UIImageJPEGRepresentation(theImage, 1);
// for converting raw data image to UIImage
UIImage *imageOrignal=[UIImage imageWithData:theImageData];
// for saving in to photos gallery
UIImageWriteToSavedPhotosAlbum(imageOrignal,nil,nil,nil);

Can I store a decompressed image in Core Data

I'm working on a carrousel of large amount of big images and I am making some tests trying to improve the performance loading the images. Right now, even if I am already decompressing the jpg on a different queue, it's still taking a little bit, mostly comparing with the photo album app which is included in the iOS. Furthermore, if I pass the images very fast, I can produce memoryWarnings.
So what I am trying to do is to store the CGImageRef (or the UIImage already decompressed: raw data) into Core Data. But all the answers and options I found are using UIImageJPegRepresentation, but doing that I would compress the image again, wouldn't I?
Anybody knows if there is a way? Am I focusing the problem wrongly?
Yes, you can convert the image to NSData and store that. Example:
Entity *testEntity = [NSEntityDescription insertNewObjectForEntityForName:#"Entity" inManagedObjectContext:__managedObjectContext];
NSString *photoPath = [[NSBundle mainBundle] pathForResource:#"photo" ofType:#"png"];
if ([[NSFileManager defaultManager] fileExistsAtPath:photoPath]) {
NSData *data = [NSData dataWithContentsOfFile:photoPath];
[testEntity setPhoto:data];
}
This stores the image as BLOB data in the sqlite file.
Ideally you never keep large number of UIImage objects of large images in memory.They will give u memory warnings.
If the Images are local files, you can do one thing, using a background thread to scale the big images to the size which is ideal for the carousel.Save these thumb nails and map them to the original image.
Load the thumb nails for carousel, and use the original image file for detailed image viewing.The thumb nails would be png for max performance.Jpeg decoding is not the native of iOS, and require more cpu to decode them than png.You dont have to keep the thumb nail data in core data, a .png file would do a nice job in my experience.You can use following code to load the image
UIImage * image = [UIImage imageWithContentsOfFile:filePath];
Here is the code to resize image
- (UIImage *)resizeImage:(UIImage*)image newSize:(CGSize)newSize {
CGRect newRect = CGRectIntegral(CGRectMake(0, 0, newSize.width, newSize.height));
CGImageRef imageRef = image.CGImage;
UIGraphicsBeginImageContextWithOptions(newSize, NO, 0);
CGContextRef context = UIGraphicsGetCurrentContext();
// Set the quality level to use when rescaling
CGContextSetInterpolationQuality(context, kCGInterpolationHigh);
CGAffineTransform flipVertical = CGAffineTransformMake(1, 0, 0, -1, 0, newSize.height);
CGContextConcatCTM(context, flipVertical);
// Draw into the context; this scales the image
CGContextDrawImage(context, newRect, imageRef);
// Get the resized image from the context and a UIImage
CGImageRef newImageRef = CGBitmapContextCreateImage(context);
UIImage *newImage = [UIImage imageWithCGImage:newImageRef];
CGImageRelease(newImageRef);
UIGraphicsEndImageContext();
return newImage;
}

Resources