Why does AVCaptureVideoOrientation landscape modes result in upside down still images? - iphone
I am using AVFoundation classes to implement a custom camera in my app. I am only capturing still images, not video. I have everything working but am stumped by something. I take into account the device orientation when a still image is captured and set the videoOrientation of the video connection appropriately. A code snippet:
// set the videoOrientation based on the device orientation to
// ensure the pic is right side up for all orientations
AVCaptureVideoOrientation videoOrientation;
switch ([UIDevice currentDevice].orientation) {
case UIDeviceOrientationLandscapeLeft:
// Not clear why but the landscape orientations are reversed
// if I use AVCaptureVideoOrientationLandscapeLeft here the pic ends up upside down
videoOrientation = AVCaptureVideoOrientationLandscapeRight;
break;
case UIDeviceOrientationLandscapeRight:
// Not clear why but the landscape orientations are reversed
// if I use AVCaptureVideoOrientationLandscapeRight here the pic ends up upside down
videoOrientation = AVCaptureVideoOrientationLandscapeLeft;
break;
case UIDeviceOrientationPortraitUpsideDown:
videoOrientation = AVCaptureVideoOrientationPortraitUpsideDown;
break;
default:
videoOrientation = AVCaptureVideoOrientationPortrait;
break;
}
videoConnection.videoOrientation = videoOrientation;
Note my comments in the landscape cases. I have to reverse the orientation mapping or the resulting image is upside down. I capture and save the image with the following code:
[self.stillImageOutput captureStillImageAsynchronouslyFromConnection:videoConnection
completionHandler:^(CMSampleBufferRef imageSampleBuffer, NSError *error)
{
NSData *imageData = [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:imageSampleBuffer];
self.stillImage = [UIImage imageWithData:imageData];
// notify observers (image gets saved to the camera roll)
[[NSNotificationCenter defaultCenter] postNotificationName:CaptureSessionManagerDidCaptureStillImageNotification object:self];
self.stillImage = nil;
}];
There is no other image processing or manipulation.
My app works with the code above. I'm just trying to understand why the orientation constants must be reversed for landscape orientations. Thanks!
Heh, it seems nobody felt like chiming in on this one. Turns out the answer is straightforward. Images captured via the stillImageOutput captureStillImageAsynchronouslyFromConnection:... method always end up with the following properties:
UIImage orientation = always UIImageOrientationRight regardless of device orientation
UIImage size = W x H (e.g. portrait width x portrait height, depends on your camera resolution)
CGImage size = depends on device orientation (e.g. portrait or landscape)
So the solution to rotate the image up is to use the device orientation in conjunction with the CGImage size to apply an appropriate affine transform. As I'm answering my own question, I'm not the solution in code but I ended up writing a routine called:
- (UIImage *)imageRotatedUpForDeviceOrientation:(UIDeviceOrientation)deviceOrientation
in a UIImage category containing various image processing enhancements.
EDIT - Implementation Example
I've received a number of requests for functional code on this. I've extracted the relevant implementation from a working app.
// this method is implemented in your capture session manager (wherever AVCaptureSession is used)
// capture a still image and save the device orientation
- (void)captureStillImage
{
UIDeviceOrientation currentDeviceOrientation = UIDevice.currentDevice.orientation;
[self.stillImageOutput
captureStillImageAsynchronouslyFromConnection:self.videoConnection
completionHandler:^(CMSampleBufferRef imageSampleBuffer, NSError *error) {
NSData *imageData = [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:imageSampleBuffer];
if (imageData) {
UIImage *image = [UIImage imageWithData:imageData];
NSDictionary *captureInfo = {
#"image" : image,
#"deviceOrientation" : #(currentDeviceOrientation)
};
// TODO: send image & orientation to delegate or post notification to observers
}
else {
// TODO: handle image capture error
}
}];
}
// this method rotates the UIImage captured by the capture session manager based on the
// device orientation when the image was captured
- (UIImage *)imageRotatedUpFromCaptureInfo:(NSDictionary *)captureInfo
{
UIImage *image = [captureInfo objectForKey:#"image"];
UIDeviceOrientation deviceOrientation = [[captureInfo objectForKey:#"deviceOrientation"] integerValue];
UIImageOrientation rotationOrientation = [self rotationNeededForImageCapturedWithDeviceOrientation:deviceOrientation];
// TODO: scale the image if desired
CGSize newSize = image.size;
return [imageScaledToSize:newSize andRotatedByOrientation:rotationOrientation];
}
// return a scaled and rotated an image
- (UIImage *)imageScaledToSize:(CGSize)newSize andRotatedByOrientation:(UIImageOrientation)orientation
{
CGImageRef imageRef = self.CGImage;
CGRect imageRect = CGRectMake(0.0, 0.0, newSize.width, newSize.height);
CGRect contextRect = imageRect;
CGAffineTransform transform = CGAffineTransformIdentity;
switch (orientation)
{
case UIImageOrientationDown: { // rotate 180 deg
transform = CGAffineTransformTranslate(transform, imageRect.size.width, imageRect.size.height);
transform = CGAffineTransformRotate(transform, M_PI);
} break;
case UIImageOrientationLeft: { // rotate 90 deg left
contextRect = CGRectTranspose(contextRect);
transform = CGAffineTransformTranslate(transform, imageRect.size.height, 0.0);
transform = CGAffineTransformRotate(transform, M_PI / 2.0);
} break;
case UIImageOrientationRight: { // rotate 90 deg right
contextRect = CGRectTranspose(contextRect);
transform = CGAffineTransformTranslate(transform, 0.0, imageRect.size.width);
transform = CGAffineTransformRotate(transform, 3.0 * M_PI / 2.0);
} break;
case UIImageOrientationUp: // no rotation
default:
break;
}
CGBitmapInfo bitmapInfo = CGImageGetBitmapInfo(imageRef);
CGColorSpaceRef colorSpaceRef = CGImageGetColorSpace(imageRef);
// madify bitmapInfo to work with PNG if necessary
if (bitmapInfo == kCGImageAlphaNone) {
bitmapInfo = kCGImageAlphaNoneSkipLast;
}
else if (bitmapInfo == kCGImageAlphaLast) {
bitmapInfo = kCGImageAlphaPremultipliedLast;
}
// Build a context that's the same dimensions as the new size
CGContextRef context = CGBitmapContextCreate(NULL,
contextRect.size.width,
contextRect.size.height,
CGImageGetBitsPerComponent(imageRef),
0,
colorSpaceRef,
bitmapInfo);
CGContextConcatCTM(context, transform);
CGContextDrawImage(context, imageRect, imageRef);
// Get the rotated image from the context and a UIImage
CGImageRef rotatedImageRef = CGBitmapContextCreateImage(context);
UIImage *rotatedImage = [UIImage imageWithCGImage:rotatedImageRef];
// Clean up
CGImageRelease(rotatedImageRef);
CGContextRelease(context);
return rotatedImage;
}
// return the UIImageOrientation needed for an image captured with a specific deviceOrientation
- (UIImageOrientation)rotationNeededForImageCapturedWithDeviceOrientation:(UIDeviceOrientation)deviceOrientation
{
UIImageOrientation rotationOrientation;
switch (deviceOrientation) {
case UIDeviceOrientationPortraitUpsideDown: {
rotationOrientation = UIImageOrientationLeft;
} break;
case UIDeviceOrientationLandscapeRight: {
rotationOrientation = UIImageOrientationDown;
} break;
case UIDeviceOrientationLandscapeLeft: {
rotationOrientation = UIImageOrientationUp;
} break;
case UIDeviceOrientationPortrait:
default: {
rotationOrientation = UIImageOrientationRight;
} break;
}
return rotationOrientation;
}
As for why you need AVCaptureVideoOrientationLandscapeRight when the device's orientation is UIDeviceOrientationLandscapeLeft, that's because, for some reason, UIDeviceOrientationLandscapeLeft == UIInterfaceOrientationLandscapeRight, and the AVCaptureVideoOrientations are following the UIInterfaceOrientation convention.
Also, UIDeviceOrientation incudes other options like UIDeviceOrientationFaceUp, UIDeviceOrientationFaceDown, and UIDeviceOrientationUnknown. If you're having your interface rotate to match the device's orientation, you could try getting the UIDeviceOrientation from [UIApplication sharedApplication].statusBarOrientation instead.
Actually, there is no mistake in your implementation and you can eliminate the entire switch case.
Why switch case can be eliminated:
The corressponding values of device and video orientations are numerically equal, i.e. as long as device orientation is not unknown, face up or face down, you can equate UIDeviceOrientation and AVCaptureVideoOrientation
About the confusing nomenclature:
Device orientation LandscapeLeft => the top of your phone is on the left.
So, just imagine where the starting point of video is..... that's right, TOP RIGHT corner => Landscape right.
Similarly in the other landscape orientation, the video orientation is away from the top of the phone.
This isn't confusing in case of portrait and upside down
Proof
When you capture a still Image, check the orientation EXIF value.
For landscape left, the EXIF orientation is 3 (180 degrees inversion)
For landscape right, the EXIF orientation is 1 (no rotation)
When you are displaying, your exif orientation is converted to corresponding UIImageOrientation, so there should be no inversion.
In the case of front camera, I have noticed that EXIF values, simply follow the video orientation values (mirror effect is not taken care of). Only 1, 3, 6, 8 values of EXIF are used.
Also, the mirror is always along the line from top of device to bottom
So, landscape will seem upside down while portrait and upside down portrait is just mirrored
Related
video rotation using AVMutableVideoCompositionLayerInstruction
I am merging multiple videos and I want to detect which ones are in portrait mode and rotate them in landscape so that all movies are in landscape mode... I have done everything and works perfectly except the actual rotate, I guess it's something with the center of the rotation or a composed rotation. AVMutableVideoCompositionLayerInstruction *videoTrackLayerInstruction = [AVMutableVideoCompositionLayerInstruction videoCompositionLayerInstructionWithAssetTrack:compositionVideoTrack]; if([[AppDelegate sharedInstance] orientationForTrack:avAsset] == UIDeviceOrientationPortrait) { CGAffineTransform rotation = CGAffineTransformMakeRotation(M_PI/2); //CGAffineTransform translateToCenter = CGAffineTransformMakeTranslation(640, 480); //CGAffineTransform mixedTransform = CGAffineTransformConcat(rotation, translateToCenter); [videoTrackLayerInstruction setTransform:rotation atTime:nextClipStartTime]; } else if([[AppDelegate sharedInstance] orientationForTrack:avAsset] == UIDeviceOrientationPortraitUpsideDown) { CGAffineTransform rotation = CGAffineTransformMakeRotation(-M_PI/2); [videoTrackLayerInstruction setTransform:rotation atTime:nextClipStartTime]; } else if([[AppDelegate sharedInstance] orientationForTrack:avAsset] == UIDeviceOrientationLandscapeLeft) { CGAffineTransform rotation = CGAffineTransformMakeRotation(M_PI); [videoTrackLayerInstruction setTransform:rotation atTime:nextClipStartTime]; } else if([[AppDelegate sharedInstance] orientationForTrack:avAsset] == UIDeviceOrientationLandscapeRight) { [videoTrackLayerInstruction setTransform:CGAffineTransformIdentity atTime:nextClipStartTime]; } How can I rotate them properly? I have tried multiple sources but nothing rotates them as they should. I am not interested in the 320 scale fit solution I want the video to keep as much resolution as possible before exporting using AVAssetExportSession. A solution like this: CGAffineTransform rotationTransform = CGAffineTransformMakeRotation(degreesToRadians(90.0)); CGAffineTransform rotateTranslate = CGAffineTransformTranslate(rotateTransform,320,0); won't suit my needs as I have tried it. Do you have any ideas? Some help will be pretty much appreciated.
Issues taking pictures with ZBarReader in landscape orientation and flat iPhone position
I'm using ZBar to detect codes but also, I want to enable taking pictures from the same screen. I detected an odd behaviour taking pictures in landscape orientation. If I put my mobile in vertical landscape position the image comes out ok, but If I move my iPhone to the flat landscape position, the picture comes out upside down. I checked UIImage metadata and the image orientation has different values, despite the fact that the device orientation is the same in both cases. Any idea why this happens?
My solution is to change the image orientation metadata in the wrong cases: -(void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info { [....] UIImage *image = [info objectForKey: UIImagePickerControllerOriginalImage]; if(image){ // This fixes a bug in ZBarReader taking picture in landscape orientation and device in flat position. NSLog(#"Image: %d, Device: %d",image.imageOrientation,self.interfaceOrientation); UIImageOrientation imgOrientation = image.imageOrientation; UIInterfaceOrientation interfaceOrientation = self.interfaceOrientation; if(interfaceOrientation == UIInterfaceOrientationLandscapeLeft && imgOrientation == UIImageOrientationUp){ image = [UIImage imageWithCGImage:[image CGImage] scale:1.0 orientation:UIImageOrientationDown]; }else if(interfaceOrientation == UIInterfaceOrientationLandscapeRight && imgOrientation == UIImageOrientationDown){ image = [UIImage imageWithCGImage:[image CGImage] scale:1.0 orientation:UIImageOrientationUp]; } [self hideScanner]; [self performSegueWithIdentifier:#"mySegue" sender:image]; } } } }
iOS PNG Image rotated 90 degrees
In my iOS application I'm writing, I deal with PNGs because I deal with the alpha channel. For some reason, I can load a PNG into my imageView just fine, but when it comes time to either copy the image out of my application (onto the PasteBoard) or save the image to my camera roll, the image rotates 90 degrees. I've searched everywhere on this, and one of the things I learned is that if I used JPEGs, I wouldn't have this problem (it sounds), due to the EXIF information. My app has full copy/paste functionality, and here's the kicker (I'll write this in steps so it is easier to follow): Go to my camera roll and copy an image Go into my app and press "Paste", image pastes just fine, and I can do that all day Click the copy function I implemented, and then click "Paste", and the image pastes but is rotated. I am 100% sure my copy and paste code isn't what is wrong here, because if I go back to Step 2 above, and click "save", the photo saves to my library but it is rotated 90 degrees! What is even more strange is that it seems to work fine with images downloaded from the internet, but is very hit or miss with images I manually took with the phone. Some it works, some it doesn't... Does anybody have any thoughts on this? Any possible work arounds I can use? I'm pretty confident in the code being it works for about 75% of my images. I can post the code upon request though.
For those that want a Swift solution, create an extension of UIImage and add the following method: func correctlyOrientedImage() -> UIImage { if self.imageOrientation == .up { return self } UIGraphicsBeginImageContextWithOptions(size, false, scale) draw(in: CGRect(x: 0, y: 0, width: size.width, height: size.height)) let normalizedImage = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); return normalizedImage ?? self; }
If you're having trouble due to the existing image imageOrientation property, you can construct an otherwise identical image with different orientation like this: CGImageRef imageRef = [sourceImage CGImage]; UIImage *rotatedImage = [UIImage imageWithCGImage:imageRef scale:1.0 orientation:UIImageOrientationUp]; You may need to experiment with just what orientation to set on your replacement images, possibly switching based on the orientation you started with. Also keep an eye on your memory usage. Photography apps often run out, and this will double your storage per picture, until you release the source image.
Took a few days, but I finally figured it out thanks to the answer #Dondragmer posted. But I figured I'd post my full solution. So basically I had to write a method to intelligently auto-rotate my images. The downside is that I have to call this method everywhere throughout my code and it is kind of processor intensive, especially when working on mobile devices, but the plus side is that I can take images, copy images, paste images, and save images and they all rotate properly. Here's the code I ended up using (the method isn't 100% complete yet, still need to edit memory leaks and what not). I ended up learning that the very first time an image was insert into my application (whether that be due to a user pressing "take image", "paste image", or "select image", for some reason it insert just fine without auto rotating. At this point, I stored whatever the rotation value was in a global variable called imageOrientationWhenAddedToScreen. This made my life easier because when it came time to manipulate the image and save the image out of the program, I simply checked this cached global variable and determined if I needed to properly rotate the image. - (UIImage*) rotateImageAppropriately:(UIImage*) imageToRotate { //This method will properly rotate our image, we need to make sure that //We call this method everywhere pretty much... CGImageRef imageRef = [imageToRotate CGImage]; UIImage* properlyRotatedImage; if (imageOrientationWhenAddedToScreen == 0) { //Don't rotate the image properlyRotatedImage = imageToRotate; } else if (imageOrientationWhenAddedToScreen == 3) { //We need to rotate the image back to a 3 properlyRotatedImage = [UIImage imageWithCGImage:imageRef scale:1.0 orientation:3]; } else if (imageOrientationWhenAddedToScreen == 1) { //We need to rotate the image back to a 1 properlyRotatedImage = [UIImage imageWithCGImage:imageRef scale:1.0 orientation:1]; } return properlyRotatedImage; } I am still not 100% sure why Apple has this weird image rotation behavior (try this... Take your phone and turn it upside down and take a picture, you'll notice that the final picture turns out right side up - perhaps this is why Apple has this type of functionality?). I know I spent a great deal of time figuring this out, so I hope it helps other people!
This "weird rotation" behavior is really not that weird at all. It is smart, and by smart I mean memory efficient. When you rotate an iOS device the camera hardware rotates with it. When you take a picture that picture will be captured however the camera is oriented. The UIImage is able to use this raw picture data without copying by just keeping track of the orientation it should be in. When you use UIImagePNGRepresentation() you lose this orientation data and get a PNG of the underlying image as it was taken by the camera. To fix this instead of rotating you can tell the original image to draw itself to a new context and get the properly oriented UIImage from that context. UIImage *image = ...; //Have the image draw itself in the correct orientation if necessary if(!(image.imageOrientation == UIImageOrientationUp || image.imageOrientation == UIImageOrientationUpMirrored)) { CGSize imgsize = image.size; UIGraphicsBeginImageContext(imgsize); [image drawInRect:CGRectMake(0.0, 0.0, imgsize.width, imgsize.height)]; image = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); } NSData *png = UIImagePNGRepresentation(image);
Here is one more way to achieve that: #IBAction func rightRotateAction(sender: AnyObject) { let imgToRotate = CIImage(CGImage: sourceImageView.image?.CGImage) let transform = CGAffineTransformMakeRotation(CGFloat(M_PI_2)) let rotatedImage = imgToRotate.imageByApplyingTransform(transform) let extent = rotatedImage.extent() let contex = CIContext(options: [kCIContextUseSoftwareRenderer: false]) let cgImage = contex.createCGImage(rotatedImage, fromRect: extent) adjustedImage = UIImage(CGImage: cgImage)! UIView.transitionWithView(sourceImageView, duration: 0.5, options: UIViewAnimationOptions.TransitionCrossDissolve, animations: { self.sourceImageView.image = self.adjustedImage }, completion: nil) }
You can use Image I/O to save PNG image to file(or NSMutableData) with respect to the orientation of the image. In the example below I save the PNG image to a file at path. - (BOOL)savePngFile:(UIImage *)image toPath:(NSString *)path { NSData *data = UIImagePNGRepresentation(image); int exifOrientation = [UIImage cc_iOSOrientationToExifOrientation:image.imageOrientation]; NSDictionary *metadata = #{(__bridge id)kCGImagePropertyOrientation:#(exifOrientation)}; NSURL *url = [NSURL fileURLWithPath:path]; CGImageSourceRef source = CGImageSourceCreateWithData((__bridge CFDataRef)data, NULL); if (!source) { return NO; } CFStringRef UTI = CGImageSourceGetType(source); CGImageDestinationRef destination = CGImageDestinationCreateWithURL((__bridge CFURLRef)url, UTI, 1, NULL); if (!destination) { CFRelease(source); return NO; } CGImageDestinationAddImageFromSource(destination, source, 0, (__bridge CFDictionaryRef)metadata); BOOL success = CGImageDestinationFinalize(destination); CFRelease(destination); CFRelease(source); return success; } cc_iOSOrientationToExifOrientation: is a method of UIImage category. + (int)cc_iOSOrientationToExifOrientation:(UIImageOrientation)iOSOrientation { int exifOrientation = -1; switch (iOSOrientation) { case UIImageOrientationUp: exifOrientation = 1; break; case UIImageOrientationDown: exifOrientation = 3; break; case UIImageOrientationLeft: exifOrientation = 8; break; case UIImageOrientationRight: exifOrientation = 6; break; case UIImageOrientationUpMirrored: exifOrientation = 2; break; case UIImageOrientationDownMirrored: exifOrientation = 4; break; case UIImageOrientationLeftMirrored: exifOrientation = 5; break; case UIImageOrientationRightMirrored: exifOrientation = 7; break; default: exifOrientation = -1; } return exifOrientation; } You can alternatively save the image to NSData using CGImageDestinationCreateWithData and pass NSMutableData instead of NSURL in CGImageDestinationCreateWithURL.
How to know if photo in Landscape or Portrait mode?
I load photo from iPhone/iPad library, most of them in Portrait mode, I want to know How can I check the photo in Landscape or Portrait mode?
Use the imageOrientation property of UIImage instances. It is going to return you one of these constants. Example: UIImage *image = // loaded from library if (image.imageOrientation == UIImageOrientationUp) { NSLog(#"portrait"); } else if (image.imageOrientation == UIImageOrientationLeft || image.imageOrientation == UIImageOrientationRight) { NSLog(#"landscape"); }
I tested this piece of code on tens of actual picture on iPhone 4 running iOS 5.0 and was able to successfully make them all in portrait mode. This is how you fix/check if (image.imageOrientation == UIImageOrientationUp || image.imageOrientation == UIImageOrientationDown ) { NSLog(#"Image is in Landscape Fix it to portrait ...."); backgroundView.frame = self.view.bounds; backgroundView.autoresizingMask=UIViewAutoresizingFlexibleWidth|UIViewAutoresizingFlexibleHeight; backgroundView.contentMode = UIViewContentModeScaleAspectFill; } else { NSLog(#"Image is in Portrait everything is fine ..."); } Here is a fool proof way of doing this check -(void)imagePickerController:(UIImagePickerController *)picker didFinishPickingImage : (UIImage *)image editingInfo:(NSDictionary *)editingInfo { // Get the data for the image NSData* imageData = UIImageJPEGRepresentation(image, 1.0); if ([UIImage imageWithData:imageData].size.width > [UIImage imageWithData:imageData].size.height) { NSLog(#"Select Image is in Landscape Mode ...."); } else { NSLog(#"Select Image is in Portrait Mode ..."); } }
Rendering MKMapView to UIImage with real resolution
I am using this function for rendering MKMapView instance into image: #implementation UIView (Ext) - (UIImage*) renderToImage { UIGraphicsBeginImageContext(self.frame.size); [self.layer renderInContext:UIGraphicsGetCurrentContext()]; UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); return image; } This works fine. But with iphone4 the rendered image doesn't have same resolution as it has really on device. On device I have the 640x920 map view quality, and rendered image has the resolution 320x460. Then I doubled the size that is provided to UIGraphicsBeginImageContext() function but that filled the only top-left image part. Question: Is there any way to get map rendered to image with full resolution 640x920?
Try using UIGraphicsBeginImageContextWithOptions instead of UIGraphicsBeginImageContext: UIGraphicsBeginImageContextWithOptions(self.frame.size, NO, 0.0); See QA1703 for more details. It says: Note: Starting from iOS 4, UIGraphicsBeginImageContextWithOptions allows you to provide with a scale factor. A scale factor of zero sets it to the scale factor of the device's main screen. This enables you to get the sharpest, highest-resolustion snapshot of the display, including a Retina Display.
iOS 7 introduced a new method to generate screenshots of a MKMapView. It is now possible to use the new MKMapSnapshot API as follows: MKMapView *mapView = [..your mapview..] MKMapSnapshotOptions *options = [[MKMapSnapshotOptions alloc]init]; options.region = mapView.region; options.mapType = MKMapTypeStandard; options.showsBuildings = NO; options.showsPointsOfInterest = NO; options.size = CGSizeMake(1000, 500); MKMapSnapshotter *snapshotter = [[MKMapSnapshotter alloc]initWithOptions:options]; [snapshotter startWithQueue:dispatch_get_main_queue() completionHandler:^(MKMapSnapshot *snapshot, NSError *error) { if( error ) { NSLog( #"An error occurred: %#", error ); } else { [UIImagePNGRepresentation( snapshot.image ) writeToFile:#"/Users/<yourAccountName>/map.png" atomically:YES]; } }]; Currently all overlays and annotations are not rendered. You have to render them afterwards onto the resulting snapshot image yourself. The provided MKMapSnapshot object has a handy helper method to do the mapping between coordinates and points: CGPoint point = [snapshot pointForCoordinate:locationCoordinate2D];