Фильтрация видео в iPhone идет медленно - PullRequest
6 голосов
/ 08 января 2012

Я пытаюсь отфильтровать видео в iPhone. Вот моя структура программы и исходный код:

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

Файл AppDelegate такой же, как по умолчанию. Вот мой 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);
}

Фильтр сепии работает, но видео немного медленнее. Когда я не применяю фильтр, видео нормальное. Любая идея о том, как я могу улучшить видео и сделать его быстрее?

Спасибо.

Ответы [ 3 ]

11 голосов
/ 06 марта 2012

Как я описал здесь , фильтр сепии в Core Image не вполне мог работать в режиме реального времени, но другие фильтры могли бы.Это зависит от аппаратных возможностей целевого устройства, а также от версии iOS (Core Image значительно улучшил производительность за последние несколько версий iOS).

Однако, если я снова смогу подключить свою платформу с открытым исходным кодом, GPUImage позволяет вам делать это намного быстрее.Он может применить фильтр тона сепии к кадру видео 640x480 за 2,5 мс на iPhone 4, что более чем достаточно для видео 30 кадров в секунду с этой камеры.

Следующий код выполняет фильтрацию видео в реальном времени с камеры заднего вида на устройстве iOS, отображая это видео в портретно-ориентированном виде:

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];
3 голосов
/ 29 июля 2012

Я понимаю, что это старый вопрос сейчас, но ...

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

эта строка вызывает ваш видео-обратный вызов для вызова в основном потоке (UI).

Если вы измените его на что-то вроде:

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

Тогда в вашем обратном вызове, если вам нужно обновить ваш интерфейс, вы должны сделать:

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

Это очень поможет, поскольку в фоновом потоке будут выполняться дорогостоящие вычисления, и рисование изображения не повлияет на захват.

Примечание стороны:

Слепое использование примера кода, который вы найдете в Интернете, не читая о том, как работает технология, не является хорошим способом разработки приложений (многие люди виновны в этом)

2 голосов
/ 21 августа 2012

следующее:

CIFilter   filterWithName:@"CISepiaTone" 

вызывается каждый раз, когда вы получаете буфер / кадр. Вам нужно только создать фильтр ОДИН РАЗ. Так что переместите это наружу, и вы все еще можете использовать фильтр.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...