iOS: Cropping a still image grabbed from a UIImagePickerController camera with overlay

10,880

When you can, it is easier to skip drawing images with Core Graphics:

- (UIImage *)cropImage:(UIImage *)oldImage {
    CGSize imageSize = oldImage.size
    UIGraphicsBeginImageContextWithOptions( CGSizeMake( imageSize.width,
                                                        imageSize.height - 200),
                                            NO,
                                            0.);
    [oldImage drawAtPoint:CGPointMake( 0, -100)
                blendMode:kCGBlendModeCopy
                    alpha:1.];
    UIImage *croppedImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return croppedImage;
}
Share:
10,880
Sagar Hatekar
Author by

Sagar Hatekar

Random Fact: My name means the Ocean :)

Updated on June 15, 2022

Comments

  • Sagar Hatekar
    Sagar Hatekar almost 2 years

    I am new to iOS and I have been bashing head against the wall for this for the past week looking around online for tutorials such as: Dealing with Exif Images, Resizing images, and many more random questions here on StackOverflow. From these, I figured that >=iOS 4.0, all images taken from the camera contain EXIF-based rotation information.

    What's not working: After trying different image cropping techniques, all I end up with is cropping of the image at some random corners and also, the resulting image appears to be zoomed in :( When I use a png image from the internet (which don't contain EXIF data), the cropping is working. By random corners, I mean - the image ends up being cropped at the top-right/top-left and zoomed-in.

    What I am trying to accomplish:
    I am trying to crop an image 100 px from top and 100 px from bottom. Essentially, I am using two overlay strips - 1 at the top, and 1 at the bottom with the CGRect(0.0, 0.0, SCREEN_WIDTH, 100.0) [a 100.0 px tall strip at the top] and another CGRect(0.0, SCREEN_HEIGHT - 100, SCREEN_WIDTH, 100.0) [another 100 px tall strip at the bottom]. I need to get the image between these two strips: I assume the height of the image is: SCREEN_HEIGHT - 200.0.

    Displaying UIImagePickerController for camera with overlay:

    
        //SCREEN_HEIGHT = 480 and SCREEN_WIDTH = 320
        UIImagePickerController *imagePicker = [[UIImagePickerController alloc] init];
    
        if([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera] == NO) 
        {
            NSLog(@"Camera not available");
            return;
        }
    
        imagePicker.sourceType = UIImagePickerControllerSourceTypeCamera;
        imagePicker.delegate = self;
        imagePicker.allowsEditing = NO;
    
    
        // Hide the controls
        imagePicker.showsCameraControls = NO;
        imagePicker.navigationBarHidden = YES;
    
        // Make camera view full screen
        imagePicker.wantsFullScreenLayout = YES;
        //imagePicker.cameraViewTransform = CGAffineTransformScale(imagePicker.cameraViewTransform, CAMERA_TRANSFORM_X, CAMERA_TRANSFORM_Y);
    
    
        //Overlay
        //OverlayView is a plain UIView with the CGRects mentioned in the question.
        OverlayView *overlay = [[OverlayView alloc] initWithFrame:CGRectMake(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT)]; 
        [overlay initOverlay:self];
        imagePicker.cameraOverlayView = overlay;
        [self presentModalViewController:imagePicker animated:YES];
    
    

    Code to rotate image based on EXIF imageOrientation property and cropping it

    
    - (UIImage *) cropImage: (UIImage *) originalImage 
    {
    
        CGRect cropRect = CGRectMake(0, 100.0, SCREEN_WIDTH, SCREEN_HEIGHT - 100);
    
        CGRect transformedRect = [self TransformCGRectForUIImageOrientation:cropRect :originalImage.imageOrientation :originalImage.size];
    
        CGImageRef resultImageRef = CGImageCreateWithImageInRect(originalImage.CGImage, transformedRect);
    
        UIImage *newImage = [[[UIImage alloc] initWithCGImage:resultImageRef scale:1.0 orientation:originalImage.imageOrientation] autorelease];
    
        return newImage;
    }
    
    - (CGRect) TransformCGRectForUIImageOrientation: (CGRect) source: (UIImageOrientation) orientation: (CGSize) imageSize {
    
        switch (orientation) {
            case UIImageOrientationLeft: { // EXIF #8
                CGAffineTransform txTranslate = CGAffineTransformMakeTranslation(
                                                                                 imageSize.height, 0.0);
                CGAffineTransform txCompound = CGAffineTransformRotate(txTranslate,
                                                                       M_PI_2);
                return CGRectApplyAffineTransform(source, txCompound);
            }
            case UIImageOrientationDown: { // EXIF #3
                CGAffineTransform txTranslate = CGAffineTransformMakeTranslation(
                                                                                 imageSize.width, imageSize.height);
                CGAffineTransform txCompound = CGAffineTransformRotate(txTranslate,
                                                                       M_PI);
                return CGRectApplyAffineTransform(source, txCompound);
            }
            case UIImageOrientationRight: { // EXIF #6
                CGAffineTransform txTranslate = CGAffineTransformMakeTranslation(
                                                                                 0.0, imageSize.width);
                CGAffineTransform txCompound = CGAffineTransformRotate(txTranslate,
                                                                       M_PI + M_PI_2);
                return CGRectApplyAffineTransform(source, txCompound);
            }
            case UIImageOrientationUp: // EXIF #1 - do nothing
            default: // EXIF 2,4,5,7 - ignore
                return source;
        }
    
    
    

    The cropImage method seems to work for images downloaded from the internet (which don't contain any orientation info). I am running out of options. Could someone PLEASE help me out?

    Thanks for reading!

  • Sagar Hatekar
    Sagar Hatekar over 12 years
    +1 Thanks, Matt. Trying to understand the code: Line #2 "selects" the image with the bottom 200 px clipped and then places the new images 100 px upwards of the original image, thereby clipping the top 100px, and the bottom 100 px right? It worked for me! Thanks a lot for your efforts!! Although the clipped image isn't exactly similar to the one seen with the camera overlay but I will probably have to tweak the nos. a bit. BTW, this code isn't thread-safe right? And I guess the CG lib provides a thread-safe way of doing this - so would be great to use it in a thread-safe manner.
  • Mats
    Mats over 12 years
    It is thread safe on iOS 4.0 and later, QA 1637.
  • Sagar Hatekar
    Sagar Hatekar over 12 years
    +1, Accepted! Awesome, this works then - beautifully! THANKS A TON! Much Appreciated!
  • Duck
    Duck over 12 years
    perfect! I have tried several methods, none worked as expected considering the image orientation. I always ended with the image rotated. thanks.
  • Eugene
    Eugene over 12 years
    @Mats, is there a way to do a similar cropping, but also include x offset points? I mean something like crop a rect, not only top and bottom parts?
  • Evan Layman
    Evan Layman over 11 years
    OMG I'd give you +1000 if I could. This finally works!! thanks!
  • Dejell
    Dejell about 11 years
    @Mats I am trying the function, but it increases the allocation size significantly. Any reason?
  • quantumpotato
    quantumpotato almost 11 years
    @Odelya UIImages take a lot of memory