2012-01-08 6 views
6

Sto provando a filtrare i video in iPhone. Ecco la struttura del programma e il codice sorgente:Il filtraggio dei video in iPhone è lento

AppDelegate.h 
AppDelegate.m 
ViewController.h 
ViewController.m 

Il file AppDelegate è lo stesso di default. Ecco il mio ViewController.

//ViewController.h 

#import <UIKit/UIKit.h> 
#import <GLKit/GLKit.h> 
#import <AVFoundation/AVFoundation.h> 
#import <CoreMedia/CoreMedia.h> 
#import <CoreVideo/CoreVideo.h> 
#import <QuartzCore/QuartzCore.h> 
#import <CoreImage/CoreImage.h> 
#import <ImageIO/ImageIO.h> 

@interface ViewController : GLKViewController <AVCaptureVideoDataOutputSampleBufferDelegate>{ 
    AVCaptureSession *avCaptureSession; 
    CIContext *coreImageContext; 
    CIImage *maskImage; 
    CGSize screenSize; 
    CGContextRef cgContext; 
    GLuint _renderBuffer; 
    float scale; 
} 

@property (strong, nonatomic) EAGLContext *context; 

-(void)setupCGContext; 

@end 

// ViewController.m 
#import "ViewController.h" 

@implementation ViewController 

@synthesize context; 

- (void)viewDidLoad 
{ 
    [super viewDidLoad]; 
    self.context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2]; 
    if (!self.context) { 
     NSLog(@"Failed to create ES context"); 
    } 

    GLKView *view = (GLKView *)self.view; 
    view.context = self.context; 
    view.drawableDepthFormat = GLKViewDrawableDepthFormat24; 

    coreImageContext = [CIContext contextWithEAGLContext:self.context]; 

    glGenRenderbuffers(1, &_renderBuffer); 
    glBindRenderbuffer(GL_RENDERBUFFER, _renderBuffer); 

    NSError *error; 
    AVCaptureDevice *videoDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo]; 
    AVCaptureDeviceInput *input = [AVCaptureDeviceInput deviceInputWithDevice:videoDevice error:&error]; 
    AVCaptureVideoDataOutput *dataOutput = [[AVCaptureVideoDataOutput alloc] init]; 

    [dataOutput setAlwaysDiscardsLateVideoFrames:YES]; 
    [dataOutput setVideoSettings:[NSDictionary dictionaryWithObject:[NSNumber numberWithInt:kCVPixelFormatType_32BGRA] 
                   forKey:(id)kCVPixelBufferPixelFormatTypeKey]]; 
    [dataOutput setSampleBufferDelegate:self queue:dispatch_get_main_queue()]; 

    avCaptureSession = [[AVCaptureSession alloc] init]; 
    [avCaptureSession beginConfiguration]; 
    [avCaptureSession setSessionPreset:AVCaptureSessionPreset1280x720]; 
    [avCaptureSession addInput:input]; 
    [avCaptureSession addOutput:dataOutput]; 
    [avCaptureSession commitConfiguration]; 
    [avCaptureSession startRunning]; 

    [self setupCGContext]; 
    CGImageRef cgImg = CGBitmapContextCreateImage(cgContext); 
    maskImage = [CIImage imageWithCGImage:cgImg]; 
    CGImageRelease(cgImg); 
} 

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

    CVPixelBufferRef pixelBuffer = (CVPixelBufferRef)CMSampleBufferGetImageBuffer(sampleBuffer); 
    CIImage *image = [CIImage imageWithCVPixelBuffer:pixelBuffer]; 
    image = [CIFilter filterWithName:@"CISepiaTone" keysAndValues:kCIInputImageKey, 
         image, @"inputIntensity", 
         [NSNumber numberWithFloat:0.8], 
         nil].outputImage; 

    [coreImageContext drawImage:image atPoint:CGPointZero fromRect:[image extent] ]; 

    [self.context presentRenderbuffer:GL_RENDERBUFFER]; 
} 

-(void)setupCGContext { 
    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); 

    NSUInteger bytesPerPixel = 4; 
    NSUInteger bytesPerRow = bytesPerPixel * screenSize.width; 
    NSUInteger bitsPerComponent = 8; 
    cgContext = CGBitmapContextCreate(NULL, screenSize.width, screenSize.height, bitsPerComponent, bytesPerRow, colorSpace, kCGImageAlphaPremultipliedLast); 

    CGColorSpaceRelease(colorSpace); 
} 

Il filtro seppia funziona, ma il video è un po 'più lento. Quando non applico il filtro, il video è normale. Qualche idea su come posso migliorare il video e renderlo più veloce?

Grazie.

+0

Forse c'è del lavoro computazionale che è possibile scaricare in un thread separato. Si può leggere su 'NSThread',' NSOperation' e blocchi. –

+0

non fa alcuna differenza, poiché sto filtrando e mostrando il video sullo schermo, delegando l'attività di filtraggio a un altro thread e ottenendo l'output filtrato da quel thread, e mostrandolo sullo schermo, non sarebbe lo stesso che fare il tutto nella stessa discussione?Usare un thread in background sarebbe utile se non fosse in tempo reale, immagino. Si prega di suggerire. Grazie. – rookieRailer

+0

Il thread potrebbe probabilmente aiutare su dispositivi dual core. Esegui calcoli su un thread in background e aggiornamenti dell'interfaccia utente sul thread principale. Profilo con una versione più piccola della tua app, forse. –

risposta

11

Mentre descrivo here, il filtro seppia in Immagine principale non era in grado di funzionare in tempo reale, ma altri filtri potrebbero. Dipende dalle capacità hardware del dispositivo di destinazione, nonché dalla versione iOS (Core Image ha migliorato significativamente le prestazioni rispetto alle ultime versioni di iOS).

Tuttavia, se è possibile ricollegare il framework open source, GPUImage consente di eseguire questa operazione molto, molto più velocemente. Può applicare un filtro a toni seppia su un frame video 640x480 in 2,5 ms su un iPhone 4, che è più che abbastanza veloce per il 30 video FPS di quella telecamera.

Il seguente codice farà un filtraggio in diretta di video dalla telecamera posteriore montato su un dispositivo iOS, mostrando che il video all'interno di una visualizzazione con orientamento verticale:

videoCamera = [[GPUImageVideoCamera alloc] initWithSessionPreset:AVCaptureSessionPreset640x480 cameraPosition:AVCaptureDevicePositionBack]; 

sepiaFilter = [[GPUImageSepiaFilter alloc] init]; 
GPUImageRotationFilter *rotationFilter = [[GPUImageRotationFilter alloc] initWithRotation:kGPUImageRotateRight]; 

[videoCamera addTarget:rotationFilter]; 
[rotationFilter addTarget:sepiaFilter]; 
filterView = [[GPUImageView alloc] initWithFrame:self.view.bounds]; 
[self.view addSubview:filterView]; 
[sepiaFilter addTarget:filterView]; 

[videoCamera startCameraCapture]; 
+1

questo non è solo un framework per l'elaborazione di immagini ... è The Framework. Ottimo lavoro –

+0

Possiamo aggiungere un filtro dopo aver scelto il video dalla galleria. – Imran

3

Mi rendo conto che questa è una vecchia questione ora , ma ...

[dataOutput setSampleBufferDelegate:self queue:dispatch_get_main_queue()]; 

quella linea sta facendo il video callback di essere chiamato sul thread principale (UI).

Se si cambia a qualcosa come:

[dataOutput setSampleBufferDelegate:self 
           queue:dispatch_queue_create("cQ", DISPATCH_QUEUE_SERIAL)]; 

Poi, nel tuo richiamata se è necessario aggiornare l'interfaccia utente si dovrebbe fare:

dispatch_async(dispatch_get_main_queue(), ^{ 
    [coreImageContext drawImage:image atPoint:CGPointZero fromRect:[image extent] ]; 
    [self.context presentRenderbuffer:GL_RENDERBUFFER]; 
}); 

che aiuterà molto come roba computazionalmente costoso verrà eseguito su un thread in background e il disegno dell'immagine non influenzerà l'acquisizione.

Nota a margine:

ciecamente utilizzando il codice di esempio a trovare su internet senza la lettura su come funziona la tecnologia non è un buon modo per sviluppare applicazioni (un sacco di persone sono colpevole di questo)

+0

Ho anche accelerato quando non utilizzo il main_queue. Tuttavia, ho riscontrato che né DISPATCH_QUEUE_SERIAL né DISPATCH_QUEUE_CONCURRENT erano veloci quanto l'impostazione semplice di dispatch_queue_attr_t per dispatch_queue_create su NULL. Stranamente, l'intestazione per queue.h ha questa definizione: "# define DISPATCH_QUEUE_SERIAL NULL". Quindi non capisco cosa sta succedendo lì. Su un iPad 3/iOS 6, ottengo 10 fps sulla coda principale, circa 15fps utilizzando SERIAL, 20fps con CONCURRENT e circa 25fps con NULL che acquisisce un'acquisizione video frontalmente 640x480 e esegue una semplice elaborazione di immagini su un shader di frammenti. Vorrei che fosse più veloce! –

+1

non si dovrebbe usare una coda concorrente poiché i frame potrebbero essere elaborati fuori servizio. –

2

la seguente:

CIFilter filterWithName:@"CISepiaTone" 

viene chiamato ogni volta che si ottiene un buffer/telaio. Devi solo creare il filtro UNA VOLTA. Quindi spostati all'esterno e puoi ancora utilizzare il filtro.

Problemi correlati