I am writing an app that does some real-time video processing using an AVCaptureSession with a AVCaptureVideoDataOutput as output and an AVCaptureDeviceInput with the video file (it no longer needs to be in real-time) as input.
Is it possible to use the video file as in input to the AVCaptureSession instead of the camera?
If it is not possible, what is the best method to process a video file using video capture of opencv on iOS (either simultaneously or sequentially)?
An object that configures capture behavior and coordinates the flow of data from input devices to capture outputs. iOS 4.0+ iPadOS 4.0+ macOS 10.7+ Mac Catalyst 14.0+
Since you have access to the raw video file frames (from your AVCaptureVideoDataOutput), you can convert each frame to a cv::Mat object (a opencv matrix, representing an image). Then do your image processing on each individual frame.
Check out https://developer.apple.com/library/ios/qa/qa1702/_index.html
for a real time example using the camera; you can convert your UIImage to a cv::Mat using cvMatFromUIImage.
So it turns out it's not too difficult to do. The basic outline is:
cv::VideoCapture to read from a fileCALayer to receive and display each frame.cv::Mat to a CGImageRef and display it on the CALayer.The actual implementation is as follows:
Step 1: Create cv::VideoCapture
std::string filename = "/Path/To/Video/File";
capture = cv::VideoCapture(filename);
if(!capture.isOpened()) NSLog(@"Could not open file.mov");
Step 2: Create the Output CALayer
self.previewLayer = [CALayer layer];
self.previewLayer.frame = CGRectMake(0, 0, width, height);
[self.view.layer addSublayer:self.previewLayer];
Step 3: Create Processing Loop w/ 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);
Step 4: Processing Method
-(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);
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With