Reduce UIImage size to a manageable size (reduce bytes) - iphone

I want to reduce the number of bytes of an image captured by the device, since i believe the _imageScaledToSize does not reduce the number of bytes of the picture (or does it?) - i want to store a thumbnail of the image in a local dictionary object and can't afford to put full size images in the dictionary. Any idea?

If you wish to simply compress your UIImage, you can use
NSData *dataForPNGFile = UIImagePNGRepresentation(yourImage);
to generate an NSData version of your image encoded as a PNG (easily inserted into an NSDictionary or written to disk), or you can use
NSData *dataForPNGFile = UIImageJPEGRepresentation(yourImage, 0.9f);
to do the same, only in a JPEG format. The second parameter is the image quality of the JPEG. Both of these should produce images that are smaller, memory-wise, than your UIImage.
Resizing a UIImage to create a smaller thumbnail (pixels-wise) using published methods is a little trickier. _imageScaledToSize is from the private API, and I'd highly recommend you not use it. For a means that works within the documented methods, see this post.

I ran into this problem the other day and did quite a bit of research. I found an awesome solution complete with code here:
http://vocaro.com/trevor/blog/2009/10/12/resize-a-uiimage-the-right-way/

You need to draw the image into a graphics context at a smaller size. Then, release the original image.

When you say 'physical size', are you talking about a print? Because you can just change the printer page size.
Are you talking about the number of pixels used to capture the image? As in, if you have a pixel array of 3000x2000, and you only want 150x150, then you can crop the images. At the time of capture, if you have a scientific imager, then you can just set the area that will be captured. The camera driver would include instructions for that. If you want to capture 3000x2000 in 1500x1000, you can try to bin the image, if that's what you need.
Or, you can use resampling post-capture in order to make the image smaller. One such algorithm is bicubic resampling, also linear resampling-- there are many variations.
I'm thinking this last is what you're most interested in... in which case, check out this Wikipedia page on the algorithm. Or, you can go to FreeImage and get a library that will read in the image and can also resize images.

UIImageJPEGRepresentation does the trick but I find that using the ImageIO framework often gets significantly better compression results for the same quality setting. It may be slower, but depending on your use case this may not be an issue.
(Code adapted for NSData from this blog post by Zachary West).
#import <MobileCoreServices/MobileCoreServices.h>
#import <ImageIO/ImageIO.h>
...
+ (NSData*)JPEGDataFromImage:(UIImage*)image quality:(double)quality
{
CFMutableDataRef outputImageDataRef = CFDataCreateMutable(kCFAllocatorDefault, 0);
CGImageDestinationRef imageDestinationRef = CGImageDestinationCreateWithData(outputImageDataRef, kUTTypeJPEG, 1, NULL);
NSDictionary* properties = #{
(__bridge NSString*)kCGImageDestinationLossyCompressionQuality: #(quality)
};
CGImageDestinationSetProperties(imageDestinationRef, (__bridge CFDictionaryRef)properties);
CGImageDestinationAddImage(imageDestinationRef, image.CGImage, NULL);
CGImageDestinationFinalize(imageDestinationRef);
CFRelease(imageDestinationRef);
NSData* imageData = CFBridgingRelease(outputImageDataRef);
return imageData;
}

Related

objective c how to change image quality?

I've googled a lot about this question, but couldn't find anything interesting for me.
Q: Is there any class or method to change image quality(not size or scale, but the quality keeping the same size and scale).
As I understand there is no native(default) classes or methods to do this, am I right?
Any help would be appreciated.
If you have an image as a UIImage, you can use the UIImageJPEGRepresentation function, specifying a compression quality,to create an NSData object. This data object can then be used to create a new UIImage.
See the Apple Docs
This functionality is already available in iOS SDK itself to set image quality.
In the UIImage class, there is one method UIImageJPEGRepresentation.
In this function pass your image and image compression quality. It will not change dimension of the image but it will change the dip of the image to maintain the quality. It will help to maintain the size in memory as well.
NSData * UIImageJPEGRepresentation (
UIImage *image,
CGFloat compressionQuality
);
Parameters:
- image : The original image data.
- compressionQuality: The quality of the resulting JPEG image, expressed as a value from 0.0 to 1.0. The value 0.0 represents the maximum compression (or lowest quality) while the value 1.0 represents the least compression (or best quality).
Hope this is what you required.
Enjoy Coding :)

Can I use JPEG compression for images in pdf files generated on iOS?

Currently I am dealing with the problem that I have pdf file with lots of 1024x768 images in it and am trying to optimize the pdf's file size, but the only solution that I thought is good enough for now is compressing the images with jpeg compression. The problem is that I did not find any way to do that with iOS APIs. Am I missing something, is there a way?
I`m welcome to suggestions on how to optimize the pdf with other means (lowering the resolution of the images is not a good solution for me).
Edit: If someone knows another API to use for pdf generation on iOS - links would be much appreciated.
Thanks in advance.
Actually you can use a UIImageJPEGRepresentation. But there's another step, to then use a JPEG data provider to draw the image:
NSData *jpegData = UIImageJPEGRepresentation(sourceImage, 0.75);
CGDataProviderRef dp = CGDataProviderCreateWithCFData((__bridge CFDataRef)jpegData);
CGImageRef cgImage = CGImageCreateWithJPEGDataProvider(dp, NULL, true, kCGRenderingIntentDefault);
[[UIImage imageWithCGImage:cgImage] drawInRect:drawRect];
This worked well for me when drawing to a PDF context. 0.75 compression quality reduced the size of my image-laden PDF by about 75% and it still looked fine.
If anyone is interested I found a solution to my problem by using a free pdf library - libHaru. It gave me the needed functionality to add a JPEG compressed image to the generated pdf file.
If you'd like to compress images you can use UIImageJPEGRepresentation
NSData * UIImageJPEGRepresentation (
UIImage *image,
CGFloat compressionQuality
);
you could jpeg convert/compress a PNG image with
NSData *someImageData = UIImageJPEGRepresentation(pngImage, 0.6); // quality 0.6 (60%) (from 0.0-1.0).
UIImage *newCompressedJPG = [UIImage imageWithData:someImageData];
But i don't think so this will reduce your PDF size. Because when you place the UIImage to your pdf the RAW image get's placed (as far as i know).
Update 1:
its compressibility varies from 0.0 to 1.0 (thanks to Leena)

Question about rotating UIImage

The size of A UIImage in my app is (320,460)
I created another UIImage object using
- (id)initWithCGImage:(CGImageRef)imageRef scale:(CGFloat)scale orientation:(UIImageOrientation)orientation
I assigned orientation to UIImageOrientationLeft.
Then I printed the new UIImage object's size, the result was (460,320).
It has rotated to left already.
I needed to store the UIImage in my document directory.
NSData *imageData = UIImagePNGRepresentation(rotateImageView);
NSString * path = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
[imageData writeToFile:[path stringByAppendingPathComponent:#"test.png"] atomically:NO];
But when I got the UIImage object from "test.png"
the size of it was changed to (320,460),it has rotated to its orignal status.
I wanted that it can be stored in (460,320)
Did I make some mistakes?
Thanks!
I've run into this problem as well. When you pass around image orientations within Apple code, you don't actually rotate any pixel data. Rather, there is basically an enum value stored with the image. Many of Apple's image renderer's are smart enough to read this enum value, and use it to display the image properly. So the code snippets you share just change this enum value. The renderers that respect this value will display what you want, while many other renderers will ignore it.
There are a couple solutions available.
First, if you're displaying an image through iOS, you can use the transform property of UIImageView along with CGAffineTransformMakeRotation to get the desired orientation.
Second, you could actually rotate the raw pixel data, which can be accomplished like this:
How to rotate image file?
I would recommend the first solution, since it's easier to code, and more efficient. However, if you will be sharing these images outside of iOS, the second approach will give more reliable results.

Is it possible to tile images in a UIScrollView without having to manually create all the tiles?

In the iPhone sample code "PhotoScroller" from WWDC 2010, they show how to do a pretty good mimmic of the Photos app with scrolling, zooming, and paging of images. They also tile the images to show how to display high resolution images and maintain good performance.
Tiling is implemented in the sample code by grabbing pre scaled and cut images for different resolutions and placing them in the grid which makes up the entire image.
My question is: is there a way to tile images without having to manually go through all your photos and create "tiles"? How is it the Photos app is able to display large images on the fly?
Edit
Here is the code from Deepa's answer below:
- (UIImage *)tileForScale:(float)scale row:(int)row col:(int)col size:(CGSize)tileSize image:(UIImage *)inImage
{
CGRect subRect = CGRectMake(col*tileSize.width, row * tileSize.height, tileSize.width, tileSize.height);
CGImageRef tiledImage = CGImageCreateWithImageInRect([inImage CGImage], subRect);
UIImage *tileImage = [UIImage imageWithCGImage:tiledImage];
return tileImage;
}
Here goes the piece of code for tiled image generation:
In PhotoScroller source code replace tileForScale: row:col: with the following:
inImage - Image that you want to create tiles
- (UIImage *)tileForScale: (float)scale row: (int)row column: (int)col size: (CGSize)tileSize image: (UIImage*)inImage
{
CGRect subRect = CGRectMake(col*tileSize.width, row * tileSize.height, tileSize.width, tileSize.height);
CGImageRef tiledImage = CGImageCreateWithImageInRect([inImage CGImage], subRect);
UIImage *tileImage = [UIImage imageWithCGImage: tiledImage];
return tileImage;
}
Regards,
Deepa
I've found this which may be of help: http://www.mikelin.ca/blog/2010/06/iphone-splitting-image-into-tiles-for-faster-loading-with-imagemagick/
You just run it in the Terminal as a shell script on your Mac.
Sorry Jonah, but I think that you cannot do what you want to.
I have been implementing a comic app using the same example as a reference and had the same doubt. Finally, I realized that, even if you could load the image and cut it into tiles the first time that you use it, you shouldn't. There are two reasons for that:
You do the tiling to save time and be more responsive. Loading and tiling takes time for a large image.
Previous reason is particularly important the first time the user runs the app.
If these two reasons make no sense to you, and you still want to do it, I would use Quartz to create the tiles. CGImage function CGImageCreateWithImageInRect would be my starting point.
Deepa's answer above will load the entire image into memory as a UIImage (the input variable in his function), defeating the purpose of tiling.
Many image formats support region-based decoding. Instead of loading the whole image into memory, decompressing the whole thing, and discarding all but the region of interest (ROI), you can load and decode only the ROI, on-demand. For the most part, this eliminates the need to pre-generate and save image tiles. I've never worked with ImageMagick but I'd be amazed if it couldn't do it. (I have done it using the Java Advanced Imaging (JAI) API, which isn't going to help you on the iPhone...)
I've played with the PhotoScroller example and the way it works with pre-generated tiles is only to demonstrate the idea behind CATiledLayer, and make a working-self contained project. It's straightforward to replace the image tile loading strategy - just rewrite the TilingView tileForScale:row:col: method to return a UIImage tile from some other source, be it Quartz or ImageMagick or whatever.

How to reduce UIImage size to a maximum as possible

I am using following code to resize the image.
Resize a UIImage Right Way
And I use interpolation quality as kCGInterpolationLow.
And then I use UIImageJPEGRepresentation(image,0.0) to get the NSData of that image.
Still its a little bit high in size around 100kb. when I send it over the network. Can I reduce it further. If I am to reduce it more what could I do ?
Thanks and Kind Regards,
you image compress and image data stroed NSData format. The function is
UIImageJPEGRepresentation(UIImage * _Nonnull image, CGFloat compressionQuality);
Example:
NSData *objImgData = UIImageJPEGRepresentation(objImg,1.0);