I am currently grabbing a photo when a user takes a picture:
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info {
UIImage *image = info[UIImagePickerControllerOriginalImage];
// create a jpeg
NSData *jpegData = UIImageJPEGRepresentation(image, 1.0f);
// write jpeg image to file in app space
NSString *filePath =
// create file path in app space
[imageData writeToFile:filePath atomically:NO];
}
This works great, the file is created a a jpeg with its EXIF data.
Now I would like to scale the image down a bit to make it a little smaller. However I would like to keep some or all of the EXIF data that existed in the original UIImage and copy it over to the scaled image.
Currently scaling the image:
UIImage *scaledImage = [renderer imageWithActions:^(UIGraphicsImageRendererContext*_Nonnull myContext) {
[image drawInRect:(CGRect) {.origin = CGPointZero, .size = size}];
}];
This creates a scaled image just fine, however it does not contain any EXIF data.
Is there a way to scale the image and retain the original image's EXIF data? Can I grab the EXIF data from the original image and copy it over to the scaled image?
Also I have searched through a lot of answers using ALAssetsLibrary which is now deprecated. Seems like the alternative is PhotoKit. Which states:
In iOS and macOS, PhotoKit provides classes that support building
photo-editing extensions for the Photos app. In iOS and tvOS, PhotoKit
also provides direct access to the photo and video assets managed by
the Photos app.
However I am not using the Photos app, my image is not coming from the local photo library or icloud as I only want to store the photo in my private app space.
The metadata from a camera capture using UIImagePickerController arrives in the info dictionary under the UIImagePickerControllerMediaMetadata key. It can be copied into the data for another UIImage using the ImageIO framework (you will need to import ImageIO). My code for this is Swift, but it uses Objective-C Cocoa classes and ImageIO C functions, so you should easily be able to translate it into Objective-C:
let jpeg = im!.jpegData(compressionQuality:1) // im is the new UIImage
let src = CGImageSourceCreateWithData(jpeg as CFData, nil)!
let data = NSMutableData()
let uti = CGImageSourceGetType(src)!
let dest = CGImageDestinationCreateWithData(data as CFMutableData, uti, 1, nil)!
CGImageDestinationAddImageFromSource(dest, src, 0, m) // m is the metadata
CGImageDestinationFinalize(dest)
After that, data is the data for the image im together with the metadata m from the capture.
Related
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.
I'm working on an iOS app with Parse that requires profile pictures to be in the shape of hexagons. Right now, I'm downloading the PFFile from Parse, grabbing that image and then masking it with a hexagon. This works well for simple views like the profile screen (masking is only required once), but the app suffers severe performance issues when masking a collection view with a list of followers' profile images.
In my mind, the best solution would be to upload the profile pictures to Parse already masked correctly, so all I have to do is pull them down and display them. Here's the code that I used to do this in my sign up view controller:
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info {
[picker dismissViewControllerAnimated:YES completion:nil];
UIImage *chosenPicture = info[UIImagePickerControllerEditedImage];
[EVNUtility maskImage:chosenPicture withMask:[UIImage imageNamed:#"MaskImage"] withCompletionBlock:^(UIImage *maskedImage) {
self.profileImageView.image = maskedImage;
self.pictureData = UIImagePNGRepresentation(maskedImage);
}];
}
The image comes back correctly masked from my utility function, however when I use UIImagePNGRepresentation (or the JPG equivalent) to convert the UIImage to data (this data is then uploaded to Parse), the image loses its mask and is square again.
How can I keep the mask when converting a UIImage to NSData?
I've tried a couple of things, but I'm guessing this is due to my fuzzy understanding of how masking is done and if it affects the underlying image. Here's the reference I used for masking my image: http://iosdevelopertips.com/cocoa/how-to-mask-an-image.html
pass in the view that contains your image and mask to this method (I use this as an extension of UIImage)
+ (UIImage *) imageWithView:(UIView *)view
{
UIGraphicsBeginImageContextWithOptions(view.bounds.size, view.opaque, 0.0);
[view.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage * img = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return img;
}
this will return your 'flattened', masked image (make sure the UIView has a clear background)
you will then need to convert to PNG (not JPG) before creating a PFFile, this will retain the transparency.
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).
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);
I've noticed that line drawings from the drawing app I'm making are very low quality when saved using this code:
UIImage *imageToSave = drawImage.image;
UIImageWriteToSavedPhotosAlbum(imageToSave, nil, nil, nil);
As far as I can understand, you can't set the JPG quality when using UIImageWriteToSavedPhotosAlbum.
Is there a way to save the UIImage as a PNG to the photo library directly, or some way to increase the JPG quality? When doing a screenshot (pressing on/off+home on the iPad) the quality of the grabbed picture is perfect, but I can't expect people to save images that way.
Any help greatly appreciated!
Try this
NSData* pngdata = UIImagePNGRepresentation (drawImage.image); //PNG wrap
UIImage* img = [UIImage imageWithData:pngdata];
UIImageWriteToSavedPhotosAlbum(img, nil, nil, nil);