How to compress jpeg image with Cocoa?

2.9k views Asked by At

I have an jpeg image and I want to be able to incrementally compress it using Cocoa/Core Image/Core Graphics. For example, I have A.jpg (3MB), I compress A and get B.jpg (1MB), compress B and get C.jpg (400KB), and so on till the image can't be compressed anymore.

I am trying to use NSBitmapImageRep representationUsingType:properties without much success. I use an image rep created from compressed image data to try and compress further, but this doesn't work well, if I use a compression factor that is the same factor used to create the initial compressed data.

Here is the code that I have been wrangling with

NSString * fileName = @"largeImage.jpg";

NSLog(@"== Initializing ==\n");

NSData * imageData = [NSData dataWithContentsOfFile:fileName];
NSLog(@"imageData length: %d\n", [imageData length]);

NSImage * image = [[NSImage alloc] initWithData:imageData];
NSBitmapImageRep * bitmapRep = [[image representations] objectAtIndex:0];

NSData * data = [bitmapRep representationUsingType:NSJPEGFileType properties:nil];
NSLog(@"data length: %d\n", [data length]);

NSDictionary * dict = [NSDictionary dictionaryWithObject: [NSNumber numberWithFloat:0.5] forKey:NSImageCompressionFactor];
NSData * data5 = [bitmapRep representationUsingType:NSJPEGFileType properties:dict];
NSLog(@"data5 length: %d\n", [data5 length]);

NSLog(@"== End ==\n\n");

NSLog(@"== Image from Data5 ==\n");
NSImage * imageFromData5 = [[NSImage alloc] initWithData:data5];
NSBitmapImageRep * bitmapRepFromData5 = [[imageFromData5 representations] objectAtIndex:0];

NSData * dataFromImageFromData5 = [bitmapRepFromData5 representationUsingType:NSJPEGFileType properties:nil];
NSLog(@"dataFromImageFromData5 length: %d\n", [dataFromImageFromData5 length]);

dict = [NSDictionary dictionaryWithObject: [NSNumber numberWithFloat:0.5] forKey:NSImageCompressionFactor];
NSData * dataFiveFromImageWithData5 = [bitmapRepFromData5 representationUsingType:NSJPEGFileType properties:dict];
NSLog(@"dataFiveFromImageWithData5 length: %d\n", [dataFiveFromImageWithData5 length]);
NSLog(@"== End ==\n\n");

This is my result

> == Initializing ==
> imageData length: 1882694
> data length: 1888365
> data5 length: 1102461
> == End ==

> == Image from Data5 ==
> dataFromImageFromData5 length: 1646137
> dataFiveFromImageWithData5 length: 1102115
> == End ==


Try a different image
> == Initializing ==
> imageData length: 3620257
> data length: 2889458
> data5 length: 1404750
> == End ==

> == Image from Data5 ==
> dataFromImageFromData5 length: 2007951
> dataFiveFromImageWithData5 length: 1405398
> == End ==
1

There are 1 answers

0
Rhult On

You need to decrease the factor every time. Recompressing a JPEG image that hasn't been edited, using the same quality level actually doesn't change the result much at all, which should explain why you aren't seeing any decrease in size.

(If you want to make sure the quality doesn't degrade more than necessary, it could make sense to recompress the original image over and over with decreasing factor instead of chaining them like in your example.)