Let's say I want to find out the size of an image, so if a user tries to load a 10,000x10,000 pixel image in my iPad app I can present them with a dialog and not crash. If I do [UIImage imageNamed:] or [UIImage imageWithContentsOfFile:] that will load my potentially large image into memory immediately.
If I use Core Image instead, say like this:
CIImage *ciImage = [CIImage imageWithContentsOfURL:[NSURL fileURLWithPath:imgPath]];
Then ask my new CIImage for its size:
CGSize imgSize = ciImage.extent.size;
Will that load the entire image into memory to tell me this, or will it just look at the metadata of the file to discover the size of the image?
The imageWithContentsOfURL function loads the image into memory, yes.
Fortunately Apple implemented CGImageSource for reading image metadata without loading the actual pixel data into memory in iOS4, you can read about how to use it in this blog post (conveniently it provides a code sample on how to get image dimensions).
EDIT: Pasted code sample here to protect against link rot:
#import <ImageIO/ImageIO.h>
NSURL *imageFileURL = [NSURL fileURLWithPath:...];
CGImageSourceRef imageSource = CGImageSourceCreateWithURL((CFURLRef)imageFileURL, NULL);
if (imageSource == NULL) {
// Error loading image
...
return;
}
NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithBool:NO], (NSString *)kCGImageSourceShouldCache,nil];
CFDictionaryRef imageProperties = CGImageSourceCopyPropertiesAtIndex(imageSource, 0, (CFDictionaryRef)options);
if (imageProperties) {
NSNumber *width = (NSNumber *)CFDictionaryGetValue(imageProperties, kCGImagePropertyPixelWidth);
NSNumber *height = (NSNumber *)CFDictionaryGetValue(imageProperties, kCGImagePropertyPixelHeight);
NSLog(#"Image dimensions: %# x %# px", width, height);
CFRelease(imageProperties);
}
The full API reference is also available here.
Related
Why doesn't the UIImage in this code snippet get restored back to its original state when I try to encode and decode it using NSKeyedArchiver?
I expect "decodedImage" to contain the image after decoding, but instead it is just NULL.
// Any image here seems to repro the issue
UIImage *image = [UIImage imageNamed:#"soda.jpg"];
// This prints YES (1), just a sanity check.
NSLog(#"Confirms %d", [[UIImage class] conformsToProtocol:#protocol(NSCoding)]);
NSMutableData *data = [[NSMutableData alloc] init];
NSKeyedArchiver *coder = [[NSKeyedArchiver alloc] initForWritingWithMutableData:data];
[coder encodeObject:image forKey:#"image"];
[coder finishEncoding];
// I would expect this to be large, instead it's < 1kb.
NSLog(#"Data length is: %zu", (unsigned long)data.length);
NSKeyedUnarchiver *decoder = [[NSKeyedUnarchiver alloc] initForReadingWithData:data];
// This prints YES (1)
NSLog(#"containsValueForKey returns %d", [decoder containsValueForKey:#"image"]);
// decodedImage is NULL here, even though containsValueForKey returned YES
UIImage *decodedImage = [decoder decodeObjectForKey:#"image"];
[decoder finishDecoding];
In this case, I'm not looking for a workaround like converting the UIImage to NSData first and encoding that. The reason is that I'm trying to reproduce an unrelated piece of code which uses something like this and I'm trying to understand it.
The code works as expected if I roundtrip the image first through nsdata and back to uiimage, why??
UIImage *originalImage = [UIImage imageNamed:#"soda.jpg"];
NSData *imageData = UIImagePNGRepresentation(originalImage);
UIImage *image = [UIImage imageWithData:imageData];
Check this
Decode Data to image:
+(NSData *)decodeBase64ToImage:(NSString *)strEncodeData
{
NSData *data = [[NSData alloc]initWithBase64EncodedString:strEncodeData options:NSDataBase64DecodingIgnoreUnknownCharacters];
return data;
}
self.btnLicenseFront.image=[UIImage imageWithData:[Themes decodeBase64ToImage:licenseFront]];
I used your code and tried with 2 images:
1. A correct image file
The output is
> [36133:5889153] Confirms 1
> [36133:5889153] Data length is: 68267
> [36133:5889153] containsValueForKey returns 1
> [36133:5889153] decodedImage is 1879681920
2. Incorrect/corrupt image file
The output is
> [36130:5888794] Confirms 1
> [36130:5888794] Data length is: 136
> [36130:5888794] containsValueForKey returns 1
> [36130:5888794] decodedImage is 0
So looks like your source JPG file is corrupt or invalid.
I have found a solution.
It turns out that this only happens the image is loaded through the [UIImage imageNamed:]. If the UIImage is created through[UIImage imageWithContentsOfFile:], the issue does not happen.
I believe this must be a bug on the ios side. The imageNamed: way of creating a UIImage is specifically for images inside the bundle. There must be some optimization they have which causes NSCoder to not function as intended since the UIImage seems to not actually contain the image data (since decoding seems to return nil instead of recreating the UIImage with the image from the bundle as expected).
I'm trying to load RAW image to an iOS project for displaying. Currently, I'm using this piece of code to load the raw image.
CFDictionaryRef optionsRef = (__bridge CFDictionaryRef) #{
// (id) kCGImageSourceShouldAllowFloat: #YES,
(id) kCGImageSourceCreateThumbnailWithTransform : #YES,
(id) kCGImageSourceCreateThumbnailFromImageAlways : #YES,
(id) kCGImageSourceThumbnailMaxPixelSize : #(MAX(SCREEN_HEIGHT, SCREEN_WIDTH) * ([UIScreen mainScreen].scale))
};
CGImageSourceRef imageSourceRef = CGImageSourceCreateWithURL((CFURLRef)imageUrl, NULL);
if (!imageSourceRef)
return nil;
NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithBool:NO], (NSString *)kCGImageSourceShouldCache,
nil];
CFDictionaryRef imageProperties = CGImageSourceCopyPropertiesAtIndex(imageSourceRef, 0, (CFDictionaryRef)options);
if (imageProperties) {
NSNumber *width = (NSNumber *)CFDictionaryGetValue(imageProperties, kCGImagePropertyPixelWidth);
NSNumber *height = (NSNumber *)CFDictionaryGetValue(imageProperties, kCGImagePropertyPixelHeight);
NSLog(#"Image dimensions: %# x %# px", width, height);
CFRelease(imageProperties);
}
The problem is, this code works flawlessly with some images, while it behaves strangely with others. For example, with one image I load, it show the width is 128 and the height is 96 , while the correct width height are 4320 × 3240.
I'm not sure what is the problem here because all I did was just load the image to CGImageSourceRef. :(
By raw you mean camera raw? (E.g. Nikon NEF files?)
As far as I know iOS does not have a raw image parsing engine. The only thing it will be able to do is to load the embedded JPEG thumbnail. You are probably seeing the size of the thumbnail, not the size of the full raw image.
Disclaimer: I've never tried to open a camera raw image in iOS, so my answer about what iOS is doing is based on imperfect memory of what I've read.
Nikon has libraries available for download, and I think they are in source form. Probably C or C++, so it would take a little work to compile them and figure out how to use them from iOS but it should be possible. You should be able to find them on the Nikon website.
I have a big table in SQLite where photos are stored with good resolution so they have a big size… so I am trying to resize these images and updating the DB table in the same process. I am using FMDB wrapper for working with SQLite DB.
Well, with instruments I can see NSData and UIImage is not being released and memory grows quickly so it makes my app close.
What could I do?
Here it is the code:
FMResultSet *aFMResultSet = [database executeQuery:#"SELECT id, image FROM Images WHERE LENGTH(image)> 1000000;" ];
while([aFMResultSet next]){
int aId = [aFMResultSet intForColumn:#"id"];
NSData *aDataImage = [aFMResultSet dataForColumn:#"image"];
UIImage* aImage = [UIImage imageWithData:aDataImage];
UIImage *aResizedImage = [Utils resizedImage:aImage withRect:CGRectMake(0, 0, 324, 242)]; //(2592x1936)/16
NSData *aDataResizedThumbnail = UIImageJPEGRepresentation(aResizedImage,0.5f);
[database executeUpdate:#"UPDATE Images SET image = ? WHERE id = ?;", aDataResizedThumbnail, [NSNumber numberWithInt:aId],nil];
}
Due to the loop, the system might never get a chance to free the memory not needed anymore.
To force the system to do so, wrap the inside of your loop in an autoreleasepool, like this:
FMResultSet *aFMResultSet = [database executeQuery:#"SELECT id, image FROM Images WHERE LENGTH(image)> 1000000;" ];
while([aFMResultSet next]){
int aId = [aFMResultSet intForColumn:#"id"];
#autoreleasepool {
NSData *aDataImage = [aFMResultSet dataForColumn:#"image"];
UIImage* aImage = [UIImage imageWithData:aDataImage];
UIImage *aResizedImage = [Utils resizedImage:aImage withRect:CGRectMake(0, 0, 324, 242)]; //(2592x1936)/16
NSData *aDataResizedThumbnail = UIImageJPEGRepresentation(aResizedImage,0.5f);
[database executeUpdate:#"UPDATE Images SET image = ? WHERE id = ?;", aDataResizedThumbnail, [NSNumber numberWithInt:aId],nil];
}
}
I want to find the DPI for an image that has been captured from iPhone/iPad Camera
this is how i am trying to get the DPI
CFDictionaryRef exifDict = CMGetAttachment(imageDataSampleBuffer,
kCGImagePropertyExifDictionary ,
NULL);
originalExifDict = (__bridge NSMutableDictionary *)(exifDict);
[originalExifDict objectForKey:(NSString *)kCGImagePropertyDPIHeight]
[originalExifDict objectForKey:(NSString *)kCGImagePropertyDPIWidth]
However both the entries in the dictionary come to be 0.
What is the correct way to find the DPI ?
Thanks in advance for the help
CGSize size;
NSNumber *width = (NSNumber *)CFDictionaryGetValue(exifDict, kCGImagePropertyDPIWidth);
NSNumber *height = (NSNumber *)CFDictionaryGetValue(exifDict, kCGImagePropertyDPIHeight);
size.width = [width floatValue];
size.height = [height floatValue];
//Tell me its work or not.
The information isn't in the metadata that comes with your imageDataSampleBuffer. It is written (72 dpi) at the time the image is saved, unless you have, first, manually set it yourself when editing the metadata, before the save.
For most purposes, it is meaningless, However, some software uses it to calculate the "correct size" of an image when placing it in a document. A 3000 pixel square image at 300 dpi will thus appear 10 inches (c.25.4 cm) square; at 72 dpi it will be nearly 42 inches (c.105.8 cm) square. Also, some online image uploaders (especially those used by stock photo libraries and the like) insist on images having high-ish dpi.
If you are using imagePickerController use this below code
NSURL *assetURL = [info objectForKey:UIImagePickerControllerReferenceURL];
ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init];
[library assetForURL:assetURL
resultBlock:^(ALAsset *asset) {
NSMutableDictionary *imageMetadata = nil;
NSDictionary *metadata = asset.defaultRepresentation.metadata;
imageMetadata = [[NSMutableDictionary alloc] initWithDictionary:metadata];
NSLog (#"imageMetaData from AssetLibrary %#",imageMetadata);
NSString *dpi = [imageMetadata objectForKey:#"DPIHeight"];
NSLog (#"Dpi: %#",dpi);
}
failureBlock:^(NSError *error) {
NSLog (#"error %#",error);
}];
We are using the following code to generate GIF file from a set of JPEG images, for the setting on doing lossless compression, it doesn't seem to generate a smaller sized file at all. Are we doing the right thing here?
CGImageDestinationRef imageDestination = CGImageDestinationCreateWithURL((CFURLRef)pathUrl, CFSTR("com.compuserve.gif"), images.count, NULL);
// image/frame level properties
NSDictionary *imageProperties = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithFloat:delayTime], (NSString *)kCGImagePropertyGIFDelayTime,
nil];
NSDictionary *properties = [NSDictionary dictionaryWithObjectsAndKeys:
imageProperties, (NSString *)kCGImagePropertyGIFDictionary,
nil];
for (UIImage *image in [images objectEnumerator]) {
CGImageDestinationAddImage(imageDestination, image.CGImage, (CFDictionaryRef)properties);
}
// gif level properties
NSDictionary *gifProperties = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithInt:0], (NSString *)kCGImagePropertyGIFLoopCount,
[NSNumber numberWithInt:1.0], kCGImageDestinationLossyCompressionQuality,
nil];
properties = [NSDictionary dictionaryWithObjectsAndKeys:
gifProperties, (NSString *)kCGImagePropertyGIFDictionary,
nil];
CGImageDestinationSetProperties(imageDestination, (CFDictionaryRef)properties);
CGImageDestinationFinalize(imageDestination);
CFRelease(imageDestination);
GIF does not support the kCGImageDestinationLossyCompressionQuality property. No built in support for compressing gifs on iOS as far as I can tell -- I haven't been able to get the color map to work.
const CFStringRef kCGImagePropertyGIFLoopCount;
const CFStringRef kCGImagePropertyGIFDelayTime;
const CFStringRef kCGImagePropertyGIFImageColorMap;
const CFStringRef kCGImagePropertyGIFHasGlobalColorMap;
const CFStringRef kCGImagePropertyGIFUnclampedDelayTime;
Reference: https://developer.apple.com/library/ios/documentation/graphicsimaging/Reference/CGImageProperties_Reference/Reference/reference.html#//apple_ref/doc/constant_group/GIF_Dictionary_Keys
The GIF image format IS lossless compression. However you are compressing a (lossy) compressed format. File size may go up.
Jpeg images contain many very similar but not identical pixels, which are very hard for lossless compression schemes to compress. To get better compression you have to quantize the colors first. Gif images will be lossless after you've taken the hit of losing information to make the image compressible.