Как использовать AVCaptureSession для чтения видео из файла?
Я пишу приложение, которое обрабатывает видео в реальном времени с помощью AVCaptureSession
с выходом AVCaptureVideoDataOutput
и AVCaptureDeviceInput
с видеофайлом (его больше не нужно в режиме реального времени), поскольку вход.
Возможно ли использовать видеофайл, как и вход в AVCaptureSession
вместо камеры?
Если это невозможно, каков наилучший способ обработки видеофайла с помощью видеозахвата opencv on iOS
(одновременно или последовательно)?
Ответы
Ответ 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];
}
Надеюсь, это поможет!