The Imaging & Video Guru Reporting: Lossless Video Zooming in iOS7

Discussion in 'iOS 7' started by Menneisyys2, Jun 25, 2013.

  1. Menneisyys2, Jun 25, 2013
    Last edited: Jun 25, 2013

    Menneisyys2 macrumors 603

    Jun 7, 2011
    This article isn't meant for absolute beginners or people not interested in shooting videos. It's HIGHLY recommended for programmers as it contains a lot of good advice.

    Executive Summary
    As of iOS7 beta 2, the stock (built-in) Camera application can't make use of the extra pixels of the sensor when zooming. It's only custom-written, third-party apps that currently can. Hopefully Camera is fixed in the final version of iOS7; before that, should you want to use zooming while shooting video, avoid using the Camera app and make sure you write or install an iOS7-specific video recorder application for best results.
    The entire article:
    Many of my readers have asked for a complete elaboration on the brand new zooming features of iOS7, both in the stock Camera application and the programmatic API support (AVCaptureDevice.videoZoomFactor and the related, highly useful properties AVCaptureDevice.activeFormat.videoZoomFactorUpscaleThreshold and AVCaptureDevice.activeFormat.videoMaxZoomFactor).

    My main aim was to find out whether iOS7 has implemented anything that the venerable, stunning Nokia PureView 808, which can losslessly zoom up to the zoom factor of four when recording Full HD video. (And even more when shooting lower-res footage.)

    I have some GREAT news for you all: while the stock Camera client (as of the just-released iOS7b2) doesn't make use of the extra pixels on the sensor when zooming, if you write your own video recorder and set the zoom yourself programmatically, the system will.

    Let's start, after a quick intro to the question of lossless zooming, with my measurement results of the stock Camera; then, continue with those of my custom test app and, finally, let's take a look at a real iOS7 app's source code that zooms.

    As usual, I used the ISO 12233 test chart for measuring resolution. As the stock Camera client only records full HD videos, I used the same full HD output resolution in my programmatic tests too so that the results can be directly compared.

    1. The theoretical maximum of lossless zoom

    If you've followed the tech press in the last 16 months, you've already heard of the Nokia PureView 808 and its excellent camera module. It allows for (almost) lossless zoom up to 4x when shooting Full HD video. (You can find some excellent, highly recommended comparative shots between the 1x and 4x zoom HERE, right at the bottom of the page.)

    The iPhone 4S and 5 “only” have a 8 Mpixel sensor (while the Nokia 808 has a 41 Mpixel one), which means they, theoretically, could only use lossless zoom up to 1.7x (3264 horizontal source pixels / 1920 horizontal target pixels = 1.7). In practice, however, the upper limit is a bit lower, 1.454545.

    When properly implemented, video zooming should be able to produce (almost) as good results at this zoom factor (1.454545x) than without zooming in at all. Let's see how iOS7 fares in this respect!

    2. The stock Camera app

    As has already been mentioned, the built-in zoom slider in the stock Camera client, in both iOS7b1 and b2, fails to make use of the extra pixels to deliver lossless zoom.

    While creating a test video footage, I've set a value on the zoom slider close (but not higher than) to the above-mentioned maximum, that is, 1.454545x (note the slider at the bottom of the screen - in Portrait orientation):


    (as with all the other images in this article, click the image for the full-sized, full-quality, original version!)
    With the above setting, the reschart result is as follows:


    The point where the lines are no longer distinguishable is around the 8 mark. Beyod that, there's absolutely no detail. (For comparison: without zooming, it's at 10.8 corresponding to 1080 pixel rows).

    As expected, it has very bad resulution at the maximal (3x) zoom:


    The point where the parallel lines are no longer distinguishable can't even be seen in this crop - here, marks start at 5.5. By scrutinizing the full, original image (again, click the thumbnail to get it!), you can easily see it's around the 5 mark.
    Finally, at the default 1x zoom, the results are, as has always been the case with all iPhone and iPad models manufactured after 2009, excellent and indeed makes almost full use of the resolution capabilities of the output file:


    All this tested numerous times on the GSM version of the iPhone 5 (model A1429, manufacture date: week 22 of 2013), but as this is a software and non-hardware-specific issue, it must behave in the same way on other, iOS7-capable models.

    3. Zooming In Your Own Apps

    As has already been explained, the above problem does not apply to zooming in your own camera recorder client. My (many times re-tested) results are as follows:

    1.454545x zoom (that is, the theoretical maximum of lossless zoom range):


    3x zoom:


    As you can see (make sure you compare the results to those of the stock Camera app in Section 2 above!), both are immensely better than those of the stock Camera app. Actually, the 1.4x zoom is only a little bit worse than the 1x one (see the last shot in Section 2), in which it's comparable to the pretty little quality loss on the Nokia PureView 808 when zoomed in to 4x on it (again, see the GSM Arena review's comparison).

    4. Programmers Only: Utilizing Zoom in your Own Apps

    Making use of the zoom is really easy – basically, all you have to do is wiring the videoZoomFactor property of your actual AVCaptureDevice instance to GUI controls; for example, a slider. In the code below, I just pre-programmed it to have the value of AVCaptureDevice.activeFormat.videoZoomFactorUpscaleThreshold (a readonly property specific to the current video mode); that is, the maximal zoom factor that can still be zoomed into without quality loss:

    self.videoDevice.videoZoomFactor = videoDevice.activeFormat.videoZoomFactorUpscaleThreshold;

    Note that there is another zoom-specific, readonly property, AVCaptureDevice.activeFormat.videoMaxZoomFactor. It's, generally, set up in a way to provide a 10x10-pixel area to zoom into. This is why it has the value of 108 for Full HD, which corresponds to a whopping 108x zoom. Of course, you won't want to use that large values.

    Also note that, as low-res 4:3 modes are all binned (along with the 720p60 mode, see my next article), they essentially halve the maximal lossless zoom factor. For example, mode 0, which has the (lowest) resolution of 192x 144, could use a lossless zoom up to 17x (3264p / 192p = 17) without binning. However, as mode 0 is binned, this value is halved; this is why you can only use a lossless zoom factor of 8.5 (see its videoZoomFactorUpscaleThreshold) only. This (binning) is why the 720p60 modes can only use a lossless zoom factor of 1.05, while the (non-binned) 720p30 modes can zoom up to 2.18x losslessly.

    An example (the full project is HERE) is as follows. It presents a Start button in the upper right corner. After tapping it, the method startVideoRecording is called. Only then will the zoom level be set. You can stop recording at tapping the button again (which will be renamed to “Stop” when recording). The recorded videos will be in the Documents directory of the app accessible via iTunes File Sharing (I've enabled UIFileSharingEnabled in the main plist file).

    As I'm programmatically creating and displaying the start/stop button, I only modified (in addition to adding UIFileSharingEnabled to the main plist, of course) the View Controller of the project.

    #import <UIKit/UIKit.h>
    #import <AVFoundation/AVFoundation.h>
    @interface iOS760fpsRecorderViewController : UIViewController  <AVCaptureFileOutputRecordingDelegate>
    @property (retain) AVCaptureVideoPreviewLayer *previewLayer;
    @property (retain) AVCaptureSession *captureSession;
    @property (retain) AVCaptureDevice *videoDevice;
    @property (retain) AVCaptureMovieFileOutput* fo;
    @property (retain) UIButton* startStopButton;
    The .m file:

    #import "iOS760fpsRecorderViewController.h"
    @interface iOS760fpsRecorderViewController ()
    @implementation iOS760fpsRecorderViewController
    @synthesize captureSession;
    @synthesize previewLayer, fo, videoDevice, startStopButton;
    - (void)viewDidLoad
        [super viewDidLoad];
        self.startStopButton = [[UIButton alloc] initWithFrame:CGRectMake(40, 40, 80, 60)];
        [startStopButton setTitle:@"Start" forState:UIControlStateNormal];
        [startStopButton addTarget:self action:@selector(buttonPressed) forControlEvents:UIControlEventTouchUpInside];
        // 1. session
        self.captureSession = [[AVCaptureSession alloc] init];
        // 2. in
        self.videoDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
        NSError *error;
        AVCaptureDeviceInput *videoIn = [AVCaptureDeviceInput deviceInputWithDevice:videoDevice error:&error];
        if (!error) {
            if ([self.captureSession canAddInput:videoIn])
                [self.captureSession addInput:videoIn];
                NSLog(@"Video input add-to-session failed");
            NSLog(@"Video input creation failed");
        // 3. out = [[AVCaptureMovieFileOutput alloc] init];
        // 4. display preview
        self.previewLayer = [[AVCaptureVideoPreviewLayer alloc] initWithSession:self.captureSession];
        previewLayer.frame = CGRectMake(0, 0, self.view.frame.size.width,self.view.frame.size.height);
        previewLayer.contentsGravity = kCAGravityResizeAspectFill;
        [self.view.layer addSublayer:self.previewLayer];
        [self.view addSubview:self.startStopButton];
        [self.captureSession startRunning];
        return UIInterfaceOrientationMaskPortrait;
        int    selectedAVCaptureDeviceFormatIdx = 15; // Full HD
        [videoDevice lockForConfiguration:nil];
        AVCaptureDeviceFormat* currdf = [videoDevice.formats objectAtIndex:selectedAVCaptureDeviceFormatIdx];
        videoDevice.activeFormat = currdf;
        self.videoDevice.videoZoomFactor = videoDevice.activeFormat.videoZoomFactorUpscaleThreshold;
    //        self.videoDevice.videoZoomFactor = 3;
        [videoDevice unlockForConfiguration];
        int fileNamePostfix = 0;
        NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
        NSString *documentsDirectory = [paths objectAtIndex:0];
        NSString *filePath = nil;
            filePath =[NSString stringWithFormat:@"/%@/%i.mp4", documentsDirectory, fileNamePostfix++];
        while ([[NSFileManager defaultManager] fileExistsAtPath:filePath]);
        NSURL* fileURL = [NSURL URLWithString:[@"file://" stringByAppendingString:filePath]];
        [ startRecordingToOutputFileURL:fileURL recordingDelegate:self];
    -  (void)captureOutput:(AVCaptureFileOutput *)captureOutput  didStartRecordingToOutputFileAtURL:(NSURL *)fileURL  fromConnections:(NSArray *)connections
    - (void)buttonPressed
        if ([self.startStopButton.titleLabel.text isEqualToString:@"Start"])
            [startStopButton setTitle:@"Stop" forState:UIControlStateNormal];
            [self startVideoRecording];
            [startStopButton setTitle:@"Start" forState:UIControlStateNormal];
            [ stopRecording];
    - (void)didReceiveMemoryWarning
        [super didReceiveMemoryWarning];

Share This Page