Image cropping from UIImageView - ios

I have a UIImageView with size (0,0,414,471). The image with size (0,0,1236,1242) is displayed with UIViewContentModeScaleAspectFit mode in the UIImageView. So the image is nicely displayed. Then I make a rectangle (0,0,100,100) on the image and I like to crop it. Even though the rectangle covered (100,100) on the UIImageView, on the actual image size, (100,100) does not cover the size as shown on the UIImageView. So I make xratio (actual width/display width) and yratio(actual height/display height), then I multiply the rectangle size, to all x,y,width and height of the rectangle. This approach looks like I can crop the area as close as possible as shown on the UIImageView. But still have a bit of area offset and distortion. What could be the best way to crop in this scenario?
My code is shown below
- (void)saveCroppedImage:(UIImage *)image withcroppedRectangle:(CGRect)clippedRect
{
float xratio = image.size.width/displayWidth;
float yratio = image.size.height/displayHeight;
// Crop logic
clippedRect.origin.x *= xratio;
clippedRect.origin.y *= yratio;
clippedRect.size.width *= xratio;
clippedRect.size.height *= yratio;
CGImageRef imageRef = CGImageCreateWithImageInRect([image CGImage], clippedRect);
UIImage * croppedImage = [UIImage imageWithCGImage:imageRef];
CGImageRelease(imageRef);
NSString *imageName =#"Documents/";
imageName = [imageName stringByAppendingString:[CurrentToddlerProfile getUserID]];
imageName = [imageName stringByAppendingString:#".png"];
NSString *pngPath = [NSHomeDirectory() stringByAppendingPathComponent:imageName];
// Write image to PNG
[UIImagePNGRepresentation(croppedImage) writeToFile:pngPath atomically:YES];
// Create file manager
// NSFileManager *fileMgr = [NSFileManager defaultManager];
// NSError *error;
// // Point to Document directory
// NSString *documentsDirectory = [NSHomeDirectory() stringByAppendingPathComponent:#"Documents"];
// // Write out the contents of home directory to console
// NSLog(#"Documents directory: %#", [fileMgr contentsOfDirectoryAtPath:documentsDirectory error:&error]);
}

Are you sure you need two ratios ? If you want your image to have the right proportions, you should use only one ratio.
If you need to fill a rectangle ( Even if UIImageView does it for you ), then first compute the ratio on x axis. Compute the scaled height using this ratio, if it is smaller than the height you need to cover, then compute the ratio on y axis.
Finally, the ratio just computed is the one to use to scale your image.

Related

Image is not showing according to the frame of UIImage after zooming and rotating

I add pinch to zoom and rotate gestures to UIImageView (myImageView). After zooming and rotating, there is apply button to save the image. Below is the apply method. It saves the image correctly with exact rotation and scaling . As it is just like screenshot.
UIGraphicsBeginImageContextWithOptions(view.frame.size,view.opaque,0.0);
[view.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *viewImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
NSData *data=UIImageJPEGRepresentation(viewImage, 100);
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *strPath = [documentsDirectory stringByAppendingPathComponent:#"BorderedImage.jpg"];
[data writeToFile:strPath atomically:YES];
But after doing this , i reassign the saved image to myImageView.
myImageView.image=[self loadImage:#"BorderedImage.jpg"];
what it does, if image was zoomed out , image becomes more small, if image was zoomed in , image becomes too big. if image was rotated, then image shows with totally wrong rotation .
I know this is some kind of UIImageView problem, because i checked the image in documents folder , it is same just like when i press apply button.
i am new to iOS development . kindly help me out.
After a great headache , i solved it myself. just before re-assigning the transformed image, i change the UIImageView transform to the original transform values.
myImageView.transform=defaultTranform;
where defaultTransform is
CGAffineTransform rotTran;
rotTran=myImageView.transform; // view did load

UIImageView ignores content mode form image data

I am trying load an image from a data :
NSError *error ;
NSString *imagePath = [[NSBundle mainBundle] pathForResource:imageName ofType:#"png"];
NSData *encryptedData = [NSData dataWithContentsOfFile:imagePath];
NSData *decryptedData = [RNDecryptor decryptData:encryptedData
withPassword:PASSWORD
error:&error];
UIImage *image = [UIImage imageWithData:decryptedData];
//adding image
UIImageView *movingImageView = [[UIImageView alloc]initWithImage:image];
[movingImageView setContentMode:UIViewContentModeScaleAspectFit];
[self.scrollView addSubview:movingImageView];
now movingImageView ignores content mode !!! it will be fixed if I declare its frame ! but the problem is my datas are different images with different width size . Any solution ?
EDIT :
Found a solution :
movingImageView.frame = CGRectMake(0, 0, image.size.width/2, image.size.height/2);
According to the documentation:
initWithImage: adjusts the frame of the receiver to match the size of the specified image. It also disables user interactions for the image view by default.`
Obviously the contentMode will have no effect if the imageView and image have the same size.
I guess what you need is to specify the imageView's width (probably the same as scrollView?), and calculate the height according to the image's dimension.
use the following lines. it will fill the image to frame. if large image came it will show image with aspect ratio and some part may clips. try once.
movingImageView.contentMode = UIViewContentModeScaleAspectFill;
movingImageView.clipsToBounds = YES;[self.scrollView addSubview:movingImageView];

Issue with VPImageCropperViewController API

I am going to share my image to Instagram , but before sharing in need to user crop their own photo , so I used VPImageCropperViewController (https://github.com/windshg/VPImageCropper) to crop the image first then share it to Instagram but the result is over scaled image :
Crop area :
and the result :
here is my codes :
- (IBAction)shareIt:(id)sender {
UIGraphicsBeginImageContextWithOptions(captureView.bounds.size, NO, 0.0);
[captureView.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage * screenshot = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
VPImageCropperViewController *imgCropperVC =
[[VPImageCropperViewController alloc]
initWithImage:image
cropFrame:CGRectMake(0, 100.0f, self.view.frame.size.width,self.view.frame.size.width)
limitScaleRatio:3.0];
imgCropperVC.delegate = self;
[self presentViewController:imgCropperVC animated:YES completion:nil];
}
VPImageCropperDelegate
- (void)imageCropper:(VPImageCropperViewController *)cropperViewController didFinished:(UIImage *)editedImage {
[cropperViewController dismissViewControllerAnimated:YES completion:^{
NSURL *instagramURL = [NSURL URLWithString:#"instagram://location?id=1"];
if ([[UIApplication sharedApplication] canOpenURL:instagramURL]) {
NSURL *url;
docFile.delegate = self;
//Save image to directory
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *savedImagePath = [documentsDirectory stringByAppendingPathComponent:#"savedImage.jpg"];
NSData *imageData = UIImagePNGRepresentation(editedImage);
[imageData writeToFile:savedImagePath atomically:NO];
//load image
NSString *getImagePath = [documentsDirectory stringByAppendingPathComponent:#"savedImage.jpg"];
UIImage *tempImage = [UIImage imageWithContentsOfFile:getImagePath];
//Hook it with Instagram
NSString *jpgPath = [NSHomeDirectory() stringByAppendingPathComponent:#"Documents/Image.ig"];
[UIImageJPEGRepresentation(tempImage, 1.0) writeToFile:jpgPath atomically:YES];
url = [[NSURL alloc] initFileURLWithPath:jpgPath];
docFile = [UIDocumentInteractionController interactionControllerWithURL:url];
[docFile setUTI:#"com.instagram.photo"];
docFile.annotation = #{#"InstagramCaption" : #" #geometrica" };
[docFile presentOpenInMenuFromRect:self.view.bounds inView:self.view animated:YES];
}else {
UIAlertView *errorToShare = [[UIAlertView alloc] initWithTitle:#"Instagram unavailable " message:#"You need to install Instagram" delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil];
[errorToShare show];
}
}];
}
It seems like the error here lies in the instatiation of your imgCropperVC. You are telling the cropper to crop out a specific rectangle (with your yellow border's size) from the image you are providing. Since you said in a comment here that the size of the picture is the same size as the screen, and since your result seems to be EXACTLY half of the width (it's cropped directly through his face), I sugges trying
[[VPImageCropperViewController alloc]
initWithImage:image
cropFrame:CGRectMake(0, 100.0f, self.view.frame.size.width*2,self.view.frame.size.width*2)
limitScaleRatio:3.0];
This might have something to do with #2x, though I'm not really sure this solves your issue for other images and in general.
I suspect the original picture to be bigger than the screen, and scaled down to fit it. If this is correct, weird things(not really) happen.
Your screen's size is probably 640px wide. Let's assume the original picture(picture.jpg) has a width of 1280 pixels or something. When showing this in your UIImageView in your ViewController or UIScrollView, it will obviously scale to fit, or else you wouldn't be able to see the entire picture. If you now use your line of code, then you are asking the cropper to crop out a width of 640 (the width of your own screen and rectangle) from a picture that is 1280px wide. The cropper doesn't care how wide your screen is, it just crops out 640px from a 1280px wide image because that's what you told it to do. This will result in the image being cut in half. If your image is scaled down (which it is by default, I guess), you'll need to use this scale in your line of code. If the original picture actually IS 1280px wide, then my above code will work, because I double the rectangle's size (The width of your view is probably 320, this is the same as 640 retina I believe).
If you multiply self.view.fram.size.width and height by the amount the picture has been zoomed, you should get the correct image. This also applies to the "static" 100.0f you send. For your VIEW it's 100f, but if the image is scaled up or down, this will not be correct. You need to multiply this with the same value as well. I'm sure you can get the zoom-scale from the scrollView or imageView or something.
I suggest testing your app with different image sizes. If an image with the exact same pixel-size as your UIView is tested, I think it will work perfectly, and will confirm my suspicion.
Finally I figure it out to crop image size there is a method in project demo called :
- (UIImage *)imageByScalingAndCroppingForSourceImage:(UIImage *)sourceImage targetSize:(CGSize)targetSize;
you can mention your target images size with it some thing like this :
image = [self imageByScalingAndCroppingForSourceImage:image targetSize:CGSizeMake(1024, 1024)];

How to crop an image and set its cropping bounds from the original image

I've a UIImageView *userImage whose size is full screen and UIImageView *imageSquare whose size is 320x320. The user will be able to play with userImage to make it bigger, change position, etc. imageSquare is static and should be seen as the cropping view
The code below can crop userImage as the imageSquare.frame.size. My problem is that it crops it from the top of userImage and not from imageSquare.frame.origin, meaning I need to crop it from X and Y coordinates. It's my first time trying to do this and every things I've tried so far can't make it crop from imageSquare.frame.origin.
How could I crop the current view (the one the user is manipulating) of userImage from imageSquare.frame.origin?
CGSize pageSize = imageSquare.frame.size;
UIGraphicsBeginImageContext(pageSize);
CGContextRef resizedContext = UIGraphicsGetCurrentContext();
CGContextTranslateCTM(resizedContext, userImage.frame.origin.x, userImage.frame.origin.y);
[userImage.layer renderInContext:resizedContext];
image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
if (image != nil) {
NSLog(#"is not nil");
NSData *imgData = UIImagePNGRepresentation(image);
imageSquare.image = [[UIImage alloc]initWithData:imgData];
}
You'll need to translate by negative x and y:
CGContextTranslateCTM(resizedContext,
-userImage.frame.origin.x,
-userImage.frame.origin.y);
[userImage.layer renderInContext:resizedContext];

Objective C / Drawing and resolution questions

I need to create a calendar of an image (they are size 640 x 960 or 960 x 640). Now one approach I tried was to create a view, add an image view to it, present the image, "draw" on it with subviews, then save to view to a file.
Now this works as planned, but testing it in simulator, the resolution of the saved image is 306 x 462 (size of the view I'm saving). Now I just lost half my original resolution...
Is there any way to work around this?
Code that saved the view:
UIGraphicsBeginImageContext(self.backgroundImageView.bounds.size);
[self.backgroundImageView.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
NSArray * paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString * basePath = ([paths count] > 0) ? [paths objectAtIndex:0] : nil;
NSString *fullPath = [NSString stringWithFormat:#"%#/Calendar.png",basePath];
NSData * binaryImageData = UIImagePNGRepresentation(image);
[binaryImageData writeToFile:fullPath atomically:YES];
You're not accounting for device screen scale, that should do it: UIGraphicsBeginImageContextWithOptions(self.backgroundImageView.bounds.size, YES, [UIScreen mainScreen].scale);.
If anyone has similar problem, I've come up with 2 solutions:
1) Enlarge everything before saving (you'll have to play around with AutoresizingMasks).
2) Put everything into a scrollView and display it in the large size so the user scrolls around if he wants to see everything.

Resources