My app stores images as NSData objects. However, when these are loaded on an iPhone 4, they are displayed at double the size because the default scale factor is 1. I have 2 questions I would appreciate help with please:
Is there any way to set the scale of the UIImage without using initWithCGImage:scale:orientation:
If the answer to 1 is no, what is the most efficient way to load the NSData into a UIImage using the method above? At present it seems I will have to create a UIImage from the NSData and then create another UIImage using the method noted in 1 above.
Thank you.
UIImage is immutable, so I guess there is no way to do so without hacking.
UIImage is just a wrapper of CGImage , so I think using initWithCGImage: as you describe won't have any noticeable performance impact. If you really worry about that, you can load it to CGImageRef first.
Related
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.
I've noticed examples of two ways to put images into core graphics. One is much simpler than the other. So what's the advantage of the second, more sophisticated, approach? Is it faster?
example 1
UIImage *myImage = [UIImage imageNamed:#"picture.png"];
CGRect imageRect = CGRectMake(70, 330, 40, 40);
[myImage drawInRect:imageRect];
[myImage release];
example 2
// Load image from application bundle
NSString* imageFileName = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:#"picture.png"];
CGDataProviderRef provider = CGDataProviderCreateWithFilename([imageFileName UTF8String]);
CGImageRef image = CGImageCreateWithPNGDataProvider(provider, NULL, true, kCGRenderingIntentDefault);
CGDataProviderRelease(provider);
// Draw image
CGContextTranslateCTM(context, 70, 370 );
CGContextScaleCTM(context, 1.0, -1.0);
CGContextDrawImage(context, CGRectMake(0, 0, 40, 40), image);
CGImageRelease(image);
Directly comparing the two, the advantage of the second method is that it doesn't put the image into the +[UIImage imageNamed:] cache. It's not possible for you to remove images from that cache, so you may be better off not using imageNamed:.
However, you can get the best of both worlds.
First off, use -[NSBundle URLForResource:withExtension:] to get the URL to the image. This simplifies that step over getting the resource directory path or URL and appending the filename to it yourself.
Second, use +[UIImage imageWithContentsOfURL:] to create the image, passing the URL you obtained from the NSBundle.
From there, continue on as in your first example. The total will be only five lines (two of them replacing the first line of your first example).
In example 1 you are just gonna show the image in the view as it is, and it is good too to show static images.
But in example 2 you are using core graphics to display the image so you can do more operations on it, more than just a static image. You can rotate it, scale it, translate it, reduce its alpha value and many more that i also not aware of.
so it depends on your choice of use. whether you want to just show the image or want it to do something more than that.if you are begineer and want to develop utility apps than i dont think you will ever use 2 option. but if you want to develop games or want to animate the image you will use should 2 option.
Cheers
I don't know any advantages for the second way. It has the disadvantage of having more steps. If you use the first way and have a UIImage and you need a CGImageRef you can access it through the CGImage property. I find myself using the first most often. When you have a UIImage you can use your own UIImageView to add it to a view hierarchy or you can assign it directly to some of the UIKit objects like UITableViewCell's imageView. If you get the CGImageRef (second way) then you would need to imageWithCGImage: first...
I have these codes:
UIImage * img = [[UIImage alloc] initWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:IMAGEURL]]];
[self.imageView setImage:img];
But the IMAGEURL contains a high resolution picture so it takes much time to load. Can I resize the image data smaller to load faster?
Any help will be appreciated.
Thanks.
No, in order to resize the image you should at least read it in once, so unless the server has a low-quality version for you, there's nothing you can do.
Unless you're brave and the image is JPEG: libjpeg has a function to read in images downsampled by a factor of 2, 4 or 8. E.g. for scale 1/8 it reads DCT blocks and takes only the constant component. But this will be a little more complex. Read time will be drastically reduced.
See iphone-reading-an-area-of-an-image about this as well.
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);
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;
}