Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Speed up saving image - iOS

I am working on more of mini-project that will be later included into a new project. It is basically a test unit.

What I am doing is creating an AVCaptureSession and then creating a method for OutputSampleBufferDelegate. In the method, I convert the sampleBuffer into a UIImage and save the UIImage. When I run the application on my iPhone 4 it can only save 2-3 images per second. There must be a more efficient way to save the image.

Can someone help me speed it up?

Thanks!

lots of the code is from here

- (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection
{   
    UIImage *resultUIImage = [self imageFromSampleBuffer:sampleBuffer];

    NSData *imageData = [NSData dataWithData:UIImagePNGRepresentation(resultUIImage)];

    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *path = [paths objectAtIndex:0];

    CMTime frameTime = CMSampleBufferGetPresentationTimeStamp(sampleBuffer);

    NSString *filename =  [NSString stringWithFormat:@"%f.png", CMTimeGetSeconds(frameTime)];

    NSString *finalPath = [path stringByAppendingString:filename];

    [imageData writeToFile:finalPath atomically:YES];
}

// Create a UIImage from sample buffer data
- (UIImage *)imageFromSampleBuffer:(CMSampleBufferRef)sampleBuffer 
{
    // Get a CMSampleBuffer's Core Video image buffer for the media data
    CVImageBufferRef imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer); 
    // Lock the base address of the pixel buffer
    CVPixelBufferLockBaseAddress(imageBuffer, 0); 

    // Get the number of bytes per row for the pixel buffer
    void *baseAddress = CVPixelBufferGetBaseAddress(imageBuffer); 

    // Get the number of bytes per row for the pixel buffer
    size_t bytesPerRow = CVPixelBufferGetBytesPerRow(imageBuffer); 
    // Get the pixel buffer width and height
    size_t width = CVPixelBufferGetWidth(imageBuffer); 
    size_t height = CVPixelBufferGetHeight(imageBuffer); 

    // Create a device-dependent RGB color space
    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); 

    // Create a bitmap graphics context with the sample buffer data
    CGContextRef context = CGBitmapContextCreate(baseAddress, width, height, 8, bytesPerRow, colorSpace, kCGBitmapByteOrder32Little | kCGImageAlphaPremultipliedFirst); 
    // Create a Quartz image from the pixel data in the bitmap graphics context
    CGImageRef quartzImage = CGBitmapContextCreateImage(context); 
    // Unlock the pixel buffer
    CVPixelBufferUnlockBaseAddress(imageBuffer,0);

    // Free up the context and color space
    CGContextRelease(context); 
    CGColorSpaceRelease(colorSpace);

    // Create an image object from the Quartz image
    UIImage *image = [UIImage imageWithCGImage:quartzImage];

    // Release the Quartz image
    CGImageRelease(quartzImage);

    return image;
}
like image 599
SamB Avatar asked Jul 06 '11 05:07

SamB


2 Answers

Using this code I can get the time it saves the image down to 0.1 sec.

- (void)captureOutput:(AVCaptureOutput *)captureOutput
didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer
       fromConnection:(AVCaptureConnection *)connection 
{  

    double frameTime = CFAbsoluteTimeGetCurrent();
    UIImage *resultUIImage = [self imageFromSampleBuffer:sampleBuffer];

    // takes freaking forever to do.
    double pre = CFAbsoluteTimeGetCurrent();
    NSData *imageData = UIImageJPEGRepresentation(resultUIImage, 0.9);
    NSLog(@"It took to write the file:%f",CFAbsoluteTimeGetCurrent()-pre);

    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory
                                                         ,NSUserDomainMask
                                                         , YES);
    NSString *path = [paths objectAtIndex:0];
    NSString *filename =  [NSString stringWithFormat:@"%f.png", frameTime];
    NSString *finalPath = [path stringByAppendingString:filename];
    [imageData writeToFile:finalPath atomically:YES];
}
like image 195
SamB Avatar answered Oct 20 '22 10:10

SamB


Could you please see how many images you can generate if you comment out the following line in your first method:

[imageData writeToFile:finalPath atomically:YES];

The reason I say that is you are going to spend a lot of time writing that image off to disk. It would be interesting to see how this performs without writing the images to disk. At least that way you will know if all the time is spent in actually creating the image versus storing the image. Or you could do as another poster mentioned and use Instruments to time how long you are within each method.

If it turns out that writing the images to disk is taking too long, then I suggest trying to implement a caching mechanism that will cache images in memory and write them to disk later.

It might also help to try calling writeToFile:atomically: on a background thread.

like image 5
Carter Avatar answered Oct 20 '22 12:10

Carter