How To Combine Two Images In iOS

A question that has been asked a lot on my iOS Still Image Capture With AVCaptureSession post is how to combine the overlay image that is shown with the image that is being captured by the camera. The more general question is how to combine two images so that is what I will show first and then I will give you my updated captureStillImage method that you can add to the AROverlayImageCapture example project which is available here.

Let’s say we have two images we want to combine; say an image of a person that we want to overlay an image of a funny hat on to. Here are the two UIImages:

UIImage *personImage = [UIImage imageNamed:@"person.jpg"];
UIImage *hatImage = [UIImage imageNamed:@"hat.png];

In this case we want the resultant image to be that same size as the personImage. Let’s get the size that we want for the final image:

CGSize finalSize = [personImage size];

Also get the size of the hat image which is probably much smaller:

CGSize hatSize = [hatImage size];

Now we need to create a graphics context in which we will do our drawing:

UIGraphicsBeginImageContext(finalSize);

The graphics context is kind of like out piece of paper that we will draw to. The first thing we want to draw on it is the photo of the person:

[personImage drawInRect:CGRectMake(0,0,finalSize.width,finalSize.height)];

Now we draw the hat at the position that we want it to be on top of the other image.

[hatImage drawInRect:CGRectMake(HAT_X_POS,HAT_Y_POS,hatSize.width,hatSize.height)];

Next we create the new UIImage with:

UIImage *newImage = [UIGraphicsGetImageFromCurrentImageContext();

Finally we need to clean up and close the context as we no longer need it:

UIGraphicsEndImageContext();

That is all there is to it.

Now for all you people who have asked how to modify the AROverlayImageCapture project to include the overlay image here is a new version of captureStillImage which has now become captureStillImageWithOverlay: which takes a UIImage as an argument.

NOTE: One thing that you will notice that is different from what we did above is that to correctly position the overlay in this particular case we need to apply some scaling. This is due to the fact that the photos taken with the camera are 480x640 whereas the screen is 320x480 so if you don't scale the position and size of your overlay it will not be positioned or sized the way it was on your iPhone screen.

- (void)captureStillImageWithOverlay:(UIImage*)overlay
{  
	AVCaptureConnection *videoConnection = nil;
	for (AVCaptureConnection *connection in [[self stillImageOutput] connections]) {
		for (AVCaptureInputPort *port in [connection inputPorts]) {
			if ([[port mediaType] isEqual:AVMediaTypeVideo]) {
				videoConnection = connection;
				break;
			}
		}
		if (videoConnection) { 
      break; 
    }
	}
  
	NSLog(@"about to request a capture from: %@", [self stillImageOutput]);
	[[self stillImageOutput] captureStillImageAsynchronouslyFromConnection:videoConnection 
                                                       completionHandler:^(CMSampleBufferRef imageSampleBuffer, NSError *error) { 
                                                         CFDictionaryRef exifAttachments = CMGetAttachment(imageSampleBuffer, kCGImagePropertyExifDictionary, NULL);
                                                         if (exifAttachments) {
                                                           NSLog(@"attachements: %@", exifAttachments);
                                                         } else { 
                                                           NSLog(@"no attachments");
                                                         }
                                                         NSData *imageData = [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:imageSampleBuffer];    
                                                         UIImage *image = [[UIImage alloc] initWithData:imageData];
                                                         
                                                         CGSize imageSize = [image size];
                                                         CGSize overlaySize = [overlay size];
                                                         
                                                         UIGraphicsBeginImageContext(imageSize);
                                                
                                                         [image drawInRect:CGRectMake(0, 0, imageSize.width, imageSize.height)];
                                                         
                                                         CGFloat xScaleFactor = imageSize.width / 320;
                                                         CGFloat yScaleFactor = imageSize.height / 480;
                                                         
                                                         [overlay drawInRect:CGRectMake(30 * xScaleFactor, 100 * yScaleFactor, overlaySize.width * xScaleFactor, overlaySize.height * yScaleFactor)]; // rect used in AROverlayViewController was (30,100,260,200)
                                                         
                                                         UIImage *combinedImage = UIGraphicsGetImageFromCurrentImageContext();
                                                         
                                                         [self setStillImage:combinedImage];
                                                         
                                                         UIGraphicsEndImageContext();
                                                         
                                                         [image release];
                                                         [[NSNotificationCenter defaultCenter] postNotificationName:kImageCapturedSuccessfully object:nil];
                                                       }];
}

You will also need to declare the method in your CaptureSessionManager header file:

- (void)captureStillImageWithOverlay:(UIImage*)overlay;

Finally you will need to update the scanButtonPressed method in AROverlayViewController.m with:

- (void)scanButtonPressed {
	[[self scanningLabel] setHidden:NO];
        [[self captureManager] captureStillImageWithOverlay:[UIImage imageNamed:@"overlaygraphic.png"]];
}

There you have it folks, how to combine two images in iOS and the particulars to update the AROverlayImageCapture project to include the overlay image in your capture output.

Happy coding!

Share and Enjoy:
  • printfriendly How To Combine Two Images In iOS
  • digg How To Combine Two Images In iOS
  • stumbleupon How To Combine Two Images In iOS
  • delicious How To Combine Two Images In iOS
  • facebook How To Combine Two Images In iOS
  • yahoobuzz How To Combine Two Images In iOS
  • twitter How To Combine Two Images In iOS
  • googlebookmark How To Combine Two Images In iOS
  • reddit How To Combine Two Images In iOS

About jjob

I'm a technologist and a music producer. I make sound, write code and build things with electronics and microcontrollers.
This entry was posted in app, AR, code, iOS, iPad, iPhone, Objective-C and tagged , , , , , , . Bookmark the permalink.

36 Responses to How To Combine Two Images In iOS

  1. Thank you for another informative blog. Where else may I get that type of information written in such an ideal means? I’ve a venture that I’m simply now working on, and I have been at the look out for such info.

  2. nathan collard says:

    Great post, thanks for that. Just a few questions, when combining the overlay with the video feed my yScaleFactor seems to be off by a small fraction, my Camera view is in a navigation controller, and it also has a bottom toolbar. Do those extra on screen elements alter the screen height that i have to divide by?

    CGFloat yScaleFactor = imageSize.height / 480;

    Also would it be possible for you to write a future post where you present the user with a image preview view where they could choose to save or cancel the taken image?

    Thanks again.

    • jjob says:

      Yeah, try adjusting the screen height you are dividing by to match your video preview screen and see how that works for you.

      Showing a preview image before saving would be simple. Look at the code where we save the image. Instead of saving it to disk just present a screen to the user with the image and a cancel and save button. The save button would run the little bit of code that saves the image.

  3. Fred says:

    Very nice post
    I have one question, i want to put the current temperature on every image. how can i draw dynamic text on the captured image?

  4. Nick says:

    AWESOME article, sir. This helped me resolve something I’ve been working on for a while now. And you explained it better than any other posts I’ve seen out there on the same subject. My only question from here would be: What if the overlaying image is rotated?

    Thanks!

    • jjob says:

      I don’t think there should be any problem with rotating the overlay image. Are you having an issue with that?

      • Nick says:

        Yeah, it’s the darndest thing. When I rotate and try to save, it not only doesn’t save the rotated state of the overlay image, it also seems to make it slightly bigger than I had it on the screen. What I’m trying to accomplish differs slightly with the fact that I am just having the user choose a background image from their library, then I have an image that they overlay, which they can rotate and resize (through rotate and pinch gesture recognizers. Any help you can offer would be VERY greatly appreciated. Here’s my code:

        - (IBAction)saveButtonPressed:(id *)sender{

        // http://www.musicalgeometry.com/?p=1681
        UIImage *bgImage = backgroundsImage.image;
        UIImage *slothImage = foregroundImage.image;
        CGSize finalSize = backgroundsImage.frame.size;
        CGSize slothSize = foregroundImage.frame.size;

        UIGraphicsBeginImageContextWithOptions(backgroundsImage.bounds.size, backgroundsImage.opaque, 2.0);

        [bgImage drawInRect:CGRectMake(0,0,finalSize.width,finalSize.height)];

        [slothImage drawInRect:CGRectMake(foregroundImage.frame.origin.x,foregroundImage.frame.origin.y,slothSize.width, slothSize.height)];

        UIImage *finalImage = UIGraphicsGetImageFromCurrentImageContext();
        UIGraphicsEndImageContext();
        UIImageWriteToSavedPhotosAlbum(finalImage, nil, nil, nil);
        }

        I did change the begin image context to the one with options per some suggestions in other threads to make the image quality better. If I did it without options like this: UIGraphicsBeginImageContext(finalSize);

        the quality was really blurry. At any rate, I’ve tested with both, and neither take the rotated state of the overlay image.

        • jjob says:

          I don’t see any rotation code here so am guessing you did that outside of this method. For your image to be rotated you would have had to have created a context, applied whatever transform(s) you wanted to to the context and then drawn your image into the context and created a new UIImage from that. I suspect that you are not doing that so your reference to the [foregroundImage image] just correctly returns the regular (non transformed) UIImage.

          Alternatively you could rotate the image in your method here but you would need to have access to the amount of rotation (and scaling) you wanted to apply.

          This might be helpful: http://stackoverflow.com/questions/2856556/creating-a-uiimage-from-a-rotated-uiimageview

          • Nick says:

            Thank you so much for looking into it and giving me these suggestions. Unfortunately, I’m very new to development, especially in Objective C. So some of what was in that link was beyond me. I managed to get it to save the rotated overlay image, but the image itself was blown up way bigger than the bounding rectangle.

            I ended up kinda taking the cheater’s way out by hiding my toolbar at the bottom of the screen, and putting the entire UIView into context and saving that off. It actually works quite nicely once you use the UIGraphicsBeginImageContextWithOptions. Here’s my code in case anyone else is curious:

            menuToolbar.hidden = true;
            UIGraphicsBeginImageContextWithOptions(backgroundsImage.bounds.size, backgroundsImage.opaque,2.0);
            CGContextRef context = UIGraphicsGetCurrentContext();
            [self.view.layer renderInContext:context];
            UIImage *screenShot = UIGraphicsGetImageFromCurrentImageContext();
            UIGraphicsEndImageContext();
            menuToolbar.hidden = false;
            UIImageWriteToSavedPhotosAlbum(screenShot, nil, nil, nil);

          • Nick says:

            Oh and yes, my rotation method is elsewhere because it’s handled by a rotation gesture recognizer. Sorry I didn’t make that clear earlier. Thanks!

  5. Deepak says:

    How do i show the merged image

  6. Michael says:

    Brilliant posts on the AVFoundation framework! Thank you!

    The documentation on developer.apple.com and the sample code for the
    AVCam and the SquareCam overwhelmed me with way to much information – useless to come to grips with AVFoundation… I wonder how you found your way through? ;)
    Anyways, great work!

    One thing:
    I usualy define the completionHandlers and assign them to a variable before passing them to their functions. I find the code to be easier to read and work with afterwards:

    void (^captureStillImageCompletionHandler)(CMSampleBufferRef, NSError *) = ^(CMSampleBufferRef imageSampleBuffer, NSError *error) {
    //...
    };
    [[self stillImageOutput] captureStillImageAsynchronouslyFromConnection:videoConnection
    completionHandler:captureStillImageCompletionHandler];

  7. David A says:

    I’ve been writing code as a hobbyist for the Mac since 1994, and micro controllers for the past few years. I’ve wrote 680×0 assembly, C, RealStudio, Ansca Corona Lua, Teensyduino and AppleScript.
    I’ve never got in to Objective C, and each time I tried I was scared off by the weird syntax.
    Your code inspired me to try again. It is great to have a fully working, simple, piece of code to play around with and see how it works. I have an extremely long way to go, but I’m finally taking my first steps.
    I just wanted to thank you for taking the time to put these tutorials together. Your explanation of each piece really helps. I know all the info is available on the Apple dev sites, but sifting through it all is overwhelming .
    Your generosity with your time in creating these is very outstanding.
    I just wanted to say thank you.

  8. David A says:

    I’ve run in to a slight problem, and I’m hoping someone can point me in the right direction.
    When combining the images, I’m seeing some distortion.
    One thing I noticed, using the sample code here, is my combined image is 720×1280 dpi, yet my images taken using the Camera app are 1936×2592.
    Can anyone point me down the right road

    • Nick says:

      Hey David – See my comments from back in June. I had a couple issues, and one of them was a poor resolution/distortion that seemed to be happening. I eventually found a good workaround (though I’m not sure what disadvantages it has) by just hiding anything from the view that I didn’t want to capture, and putting the entire view into context and saving that off. Be sure you use the UIGraphicsBeginImageContextWithOptions method.

      I’m curious still if anyone sees any reason not to do it this way?

  9. David A says:

    Thanks Nick. I hadn’t seen that.
    I’ll try and figure out how to make those change to my code.
    I over complicated this for my first project, by inserting this code in to a Tab bar of a StoryBoard – thinking I aught to learn the ‘new’ way.
    My app already runs without the menu bar. I’m guessing my problem lies in the tab bar at the bottom of my app.

  10. karthi says:

    Hi,
    First of all thanks to author. I want to load images from URL. But how can i add imageNamed like this:

    UIImageView *overlayImageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"overlaygraphic.png"]];
    [overlayImageView setFrame:CGRectMake(30, 100, 260, 200)];

    [[self view] addSubview:overlayImageView];
    [overlayImageView release];

    [[self captureManager] captureStillImageWithOverlay:[UIImage imageNamed:@"Nature.png"]];

    If i load images from URL request then i add to imageView:

    UIImageView *myview=[[UIImageView alloc]init];

    myview.frame = CGRectMake(0, 0, 150, 150);

    NSURL *imgURL=[[NSURL alloc]initWithString:@”http://soccerlens.com/files/2011/03/chelsea-1112-home.png”];

    NSData *imgdata=[[NSData alloc]initWithContentsOfURL:imgURL];
    UIImage *image=[[UIImage alloc]initWithData:imgdata];
    myview.image=image;
    [self.view addSubview:myview];

    Then what can i add in this line [[self captureManager] captureStillImageWithOverlay:

    • jjob says:

      Sorry, I don’t understand what you are trying to do. If you have two images that you want to combine it makes no difference where the two images came from. Can you describe clearly what you are trying to accomplish?

      • karthi says:

        In example captureStillImageWithOverlay is UIImage imageNamed:@”overlay.png” . If i load images from my server what shall i add (or) replace instead of @”overlay.png” filed

        • jjob says:

          captureStillImageWithOverlay: takes a UIImage as input. It makes no difference where that UIImage comes from.

          If you want to use an image from a server then simply download that image data and create a UIImage object and pass it to the captureStillImageWithOverlay: method.

  11. jjob says:

    Sorry, I assumed from your first post where you described how to grab an image from a URL that you knew how to do that.

    So captureStillImageWithOverlay: takes a UIImage as its argument, that means you need to have some UIImage to pass into it. I think you are saying you want to pass in an image that you downloaded from some URL.

    In your original post you did this to get an image:

    NSURL *imgURL=[[NSURL alloc]initWithString:@”http://soccerlens.com/files/2011/03/chelsea-1112-home.png”];
    NSData *imgdata=[[NSData alloc]initWithContentsOfURL:imgURL];
    UIImage *image=[[UIImage alloc]initWithData:imgdata];

    So now you have a UIImage named image. To pass that into the captureStillImageWithOverlay: method you simply call it like this:

    [[self captureManager] captureStillImageWithOverlay:image];

    • karthi says:

      How to add more than two image data to captureStillImageWithOverlay. For my app i need to load two different URL:

      NSURL *imgURL=[[NSURL alloc]initWithString:@”http://soccerlens.com/files/2011/03/chelsea-1112-home.png”];
      NSData *imgdata=[[NSData alloc]initWithContentsOfURL:imgURL];
      UIImage *image=[[UIImage alloc]initWithData:imgdata];

      NSURL *imgURL=[[NSURL alloc]initWithString:@”http://sample.com/files/2011/03/nature.png”];
      NSData *imgdata=[[NSData alloc]initWithContentsOfURL:imgURL];
      UIImage *image1=[[UIImage alloc]initWithData:imgdata];

      Then how to add image, image1 data to stillCaptureWithoverlay: ?

      • jjob says:

        To add two images you will need to modify the captureStillImageWithOverlay: method.

        In it you will need to have a reference to both images you want to overlay. You can do that however you want.

        The line of code that adds the image is:

        [overlay drawInRect:CGRectMake(30 * xScaleFactor, 100 * yScaleFactor, overlaySize.width * xScaleFactor, overlaySize.height * yScaleFactor)];

        so you will need to call that for each of the images you want to add, and obviously you will want to adjust the position and size of the image you are adding to whatever you want it to be.

        • karthi says:

          jjob, I need to capture two overlay imges separately. when i click 1st button it load first image then i capture photo using stillcaptureimagewithoverlay:image. Then i click 2nd button it load other image fromf second url. Then how to add stillcapturewithimageoverlay:@”image”, “image1″ ? …Is it correct?

        • karthi says:

          How to capture overlay image whenever change position?

          I have code for capture overlay image using AVCapturesession. But i used overlay image size drawInRect. When i click capture button it take screenshot with what i used drawInRect for overlay image that size only captured. When i move my overlay image to some other position then i take screenshot, the output is what i used drawInRect position for overlay Image that only taking.

    • karthi says:

      [[self captureManager]captureStillImageWithOverlay=image is not working

  12. karthi says:

    I set overlay frame using drawInRect [overlay drawInRect:CGRectMake(30 * xScaleFactor, 100 * yScaleFactor, overlaySize.width * xScaleFactor, overlaySize.width* yScaleFactor)];

    But i change overlay picture size using drag. And i took picture, but overlay image still present what i set in overlay frame using drawInRect…How can i take picture with dynamically move position.

  13. karthi says:

    How to capture array of images in overlay. I have array images under scroll view. When i click one image it displays camera overlay view. I need to take picture that overlay images.

    code for button click:

    [self captureStillImageWithOverlay:[NSArray arrayWithObjects:[UIImage imageNamed:@"img1.png"],[UIImage imageNamed:@"img2.png"],[UIImage imageNamed:@"img3.png"],[UIImage imageNamed:@"img4.png"],[UIImage imageNamed:@"img5.png"], nil]];

  14. clive dancey says:

    THIS IS SUCH AN AMAZING POST…i have tried the program and it works..really well.

    I was wondering whether any of you guys know how i can move my initial image , as it is viewed on screen ( changed the scan image already ) so when i take a picture it is in the same place as i viewed it.

    I am well on the way to achieving what i want so thank you so much
    Clive

  15. clive dancey says:

    also , how to incorporate this into a storyboard..as that is my preferred way of working
    thanks again :)

  16. Amir Al Hamezh says:

    How could i insert a NavigationBarButtonItem to turn the frontcam or backcam to take the user picture?

  17. karthi says:

    How to take picture of overlay image after moving initial (default) position. For ex: I have one image on overlay, i’m capturing with [overlay drawInRect:CGRectMake(30 * xScaleFactor, 100 * yScaleFactor, overlaySize.width * xScaleFactor, overlaySize.height * yScaleFactor)];. Later i moved my overlay image to some distance in y or x. If i capture the image with overlay why i’m not getting newly moved x or y position. I’m getting only this value [overlay drawInRect:CGRectMake(30 * xScaleFactor, 100 * yScaleFactor, overlaySize.width * xScaleFactor, overlaySize.height * yScaleFactor)];. How to capture dynamically movement? Is it possible?

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>