Подтвердить что ты не робот

Как использовать AVCaptureSession для чтения видео из файла?

Я пишу приложение, которое обрабатывает видео в реальном времени с помощью AVCaptureSession с выходом AVCaptureVideoDataOutput и AVCaptureDeviceInput с видеофайлом (его больше не нужно в режиме реального времени), поскольку вход.

Возможно ли использовать видеофайл, как и вход в AVCaptureSession вместо камеры? Если это невозможно, каков наилучший способ обработки видеофайла с помощью видеозахвата opencv on iOS (одновременно или последовательно)?

4b9b3361

Ответ 1

Поскольку у вас есть доступ к необработанным видеофайлам (из AVCaptureVideoDataOutput), вы можете конвертировать каждый кадр в объект cv::Mat (матрица opencv, представляющая изображение). Затем выполните обработку изображений на каждом отдельном фрейме.

Отъезд https://developer.apple.com/library/ios/qa/qa1702/_index.html для примера в реальном времени с использованием камеры; вы можете преобразовать ваш UIImage в cv::Mat с помощью cvMatFromUIImage.

Ответ 2

Так получается, что это не слишком сложно сделать. Основной план:

  • Создайте cv::VideoCapture для чтения из файла
  • Создайте CALayer для приема и отображения каждого кадра.
  • Выполнить метод с заданной скоростью, который считывает и обрабатывает каждый кадр.
  • После выполнения обработки преобразуйте каждый cv::Mat в CGImageRef и покажите его на CALayer.

Фактическая реализация такова:

Шаг 1: Создайте cv:: VideoCapture

std::string filename = "/Path/To/Video/File";
capture = cv::VideoCapture(filename);
if(!capture.isOpened()) NSLog(@"Could not open file.mov");

Шаг 2. Создание выходного CALayer

self.previewLayer = [CALayer layer];
self.previewLayer.frame = CGRectMake(0, 0, width, height);
[self.view.layer addSublayer:self.previewLayer];

Шаг 3. Создание цикла обработки с GCD

int kFPS = 30;

dispatch_queue_t queue = dispatch_queue_create("timer", 0);
self.timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
dispatch_source_set_timer(self.timer, dispatch_walltime(NULL, 0), (1/kFPS) * NSEC_PER_SEC, (0.5/kFPS) * NSEC_PER_SEC);

dispatch_source_set_event_handler(self.timer, ^{
    dispatch_async(dispatch_get_main_queue(), ^{
        [self processNextFrame];
    });
});

dispatch_resume(self.timer);

Шаг 4: Метод обработки

-(void)processNextFrame {
    /* Read */
    cv::Mat frame;
    capture.read(frame);

    /* Process */
    ...

    /* Convert and Output to CALayer*/
    cvtColor(frame, frame, CV_BGR2RGB);
    NSData *data = [NSData dataWithBytes:frame.data
                              length:frame.elemSize()*frame.total()];

    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
    CGBitmapInfo bitmapInfo = (frame.elemSize() == 3) ? kCGImageAlphaNone : kCGImageAlphaNoneSkipFirst;
    CGDataProviderRef provider = CGDataProviderCreateWithCFData((__bridge CFDataRef) data);

    CGImageRef imageRef = CGImageCreate(frame.cols,
                                        frame.rows,
                                        8,
                                        8 * frame.elemSize(),
                                        frame.step[0],
                                        colorSpace,
                                        bitmapInfo,
                                        provider,
                                        NULL,
                                        false,
                                        kCGRenderingIntentDefault);

    self.previewLayer.contents = (__bridge id)imageRef;

    CGImageRelease(imageRef);
    CGColorSpaceRelease(colorSpace);
}

Ответ 3

Я реализовал решение pasawaya... PreviewLayer не был обновлен... Я нашел, откуда возникла проблема:

На шаге 4 замените:

self.previewLayer.contents = (__bridge id)imageRef;

С:

[self performSelectorOnMainThread:@selector(displayFrame:) withObject:(__bridge id)imageRef waitUntilDone:YES];

И добавить:

- (void)displayFrame:(CGImageRef)frame {
    _previewLayer.contents = (__bridge id)frame;
    [CATransaction flush];
}

Надеюсь, это поможет!