2012-01-08 14 views
6

Estoy intentando filtrar video en iPhone. Aquí está la estructura de mi programa y el código fuente:El filtrado de video en iPhone es lento

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

El archivo de AppDelegate es el mismo que el predeterminado. Aquí está mi 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); 
} 

El filtro sepia funciona, pero el video es un poco más lento. Cuando no aplique filtro, el video es normal. ¿Alguna idea sobre cómo puedo mejorar el video y hacerlo más rápido?

Gracias.

+0

Quizás haya un trabajo computacional que pueda descargar a un hilo separado. Puede leer en 'NSThread',' NSOperation' y bloques. –

+0

hace alguna diferencia, ya que estoy filtrando y mostrando el video en la pantalla, delegando la tarea de filtrado a otro hilo y luego obteniendo salida filtrada de ese hilo, y mostrándolo en la pantalla, ¿no sería lo mismo que hacer todo en el mismo hilo?Usar un hilo de fondo sería útil si no fuera en tiempo real, supongo. Por favor recomiende. Gracias. – rookieRailer

+0

Enhebrar probablemente sea útil en dispositivos de doble núcleo. Haga el cálculo en un subproceso de fondo y las actualizaciones de la interfaz de usuario en el hilo principal. Perfil con una versión más pequeña de tu aplicación, tal vez. –

Respuesta

11

Como describo here, el filtro sepia en Core Image no fue capaz de ejecutarse en tiempo real, pero sí otros filtros. Depende de las capacidades de hardware del dispositivo de destino, así como de la versión de iOS (Core Image ha mejorado significativamente en rendimiento en las últimas versiones de iOS).

Sin embargo, si puedo volver a conectar mi marco de código abierto, GPUImage le permite hacer esto mucho, mucho más rápido. Puede aplicar un filtro de tono sepia en un fotograma de video de 640x480 en 2,5 ms en un iPhone 4, que es más que suficientemente rápido para el video de 30 FPS de esa cámara.

El siguiente código hará un filtrado en directo de vídeo de la cámara trasera montada en un dispositivo iOS, mostrando que el vídeo dentro de una vista con orientación vertical:

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

esto no es solo un framework de procesador de imagen ... es The Framework. Buen trabajo –

+0

¿Podemos agregar filtro después de elegir el video de la galería. – Imran

3

Comprendo que esto es una vieja pregunta ahora , pero ...

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

esa línea está haciendo que su devolución de llamada de video sea llamada en el hilo principal (UI).

Si cambia a algo así como:

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

Luego, en su devolución de llamada si necesita actualizar la interfaz de usuario que debe hacer:

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

que le ayudará mucho como material costoso computacionalmente se ejecutará en un hilo de fondo, y el dibujo de la imagen no afectará a la captura.

Nota al margen:

A ciegas utilizando código de ejemplo que encuentre en Internet sin leer sobre cómo funciona la tecnología no es una buena manera de desarrollar aplicaciones (una gran cantidad de personas son culpables de esto)

+0

También tengo una velocidad más alta cuando no uso la main_queue. Sin embargo, descubrí que ni DISPATCH_QUEUE_SERIAL ni DISPATCH_QUEUE_CONCURRENT fueron tan rápidos como configurar el dispatch_queue_attr_t para dispatch_queue_create en NULL. Curiosamente, el encabezado de queue.h tiene esta definición: "# define DISPATCH_QUEUE_SERIAL NULL". Entonces no entiendo lo que está sucediendo allí. En un iPad 3/iOS 6, obtengo 10 fps en la cola principal, unos 15 fps con SERIAL, 20 fps con CONCURRENT y unos 25 fps con NULL capturando una captura de video frontal de 640x480 y haciendo un simple procesamiento de imágenes en un sombreador de fragmentación. ¡Ojalá fuera más rápido! –

+1

no debe usar una cola simultánea ya que sus marcos pueden procesarse fuera de servicio. –

2

la siguiente:

CIFilter filterWithName:@"CISepiaTone" 

se llama cada vez que reciba un buffer/marco. Solo necesita crear el filtro UNA VEZ. Así que mueve esto fuera y aún puedes usar el filtro.

Cuestiones relacionadas