我试着从摄像机和相关的运动数据中捕捉帧。对于同步,我使用时间戳。将视频和运动写入文件,然后进行处理。在这个过程中,我可以计算每个视频的运动帧偏移量。
结果显示,同一时间戳的运动数据和视频数据被从0.2秒到0.3秒之间的不同时间相互抵消。对于一个视频,这个偏移量是恒定的,但每个视频的偏移量是不同的。如果它是相同的偏移,每次我将能够减去一些校准值,但它不是。
有同步时间戳的好方法吗?也许我记错了?是否有更好的方法使他们达到相同的参照标准?
CoreMotion返回与系统正常运行时间相关的时间戳,因此我添加偏移以获得unix时间:
uptimeOffset = [[NSDate date] timeIntervalSince1970] -
[NSProcessInfo processInfo].systemUptime;
CMDeviceMotionHandler blk =
^(CMDeviceMotion * _Nullable motion, NSError * _Nullable error){
if(!error){
motionTimestamp = motion.timestamp + uptimeOffset;
...
}
};
[motionManager startDeviceMotionUpdatesUsingReferenceFrame:CMAttitudeReferenceFrameXTrueNorthZVertical
toQueue:[NSOperationQueue currentQueue]
withHandler:blk];为了获得高精度的帧时间戳,我使用了AVCaptureVideoDataOutputSampleBufferDelegate。它还与unix时间相抵:
-(void)captureOutput:(AVCaptureOutput *)captureOutput
didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer
fromConnection:(AVCaptureConnection *)connection
{
CMTime frameTime = CMSampleBufferGetOutputPresentationTimeStamp(sampleBuffer);
if(firstFrame)
{
firstFrameTime = CMTimeMake(frameTime.value, frameTime.timescale);
startOfRecording = [[NSDate date] timeIntervalSince1970];
}
CMTime presentationTime = CMTimeSubtract(frameTime, firstFrameTime);
float seconds = CMTimeGetSeconds(presentationTime);
frameTimestamp = seconds + startOfRecording;
...
}发布于 2021-02-18 12:37:41
实际上,将这些时间戳关联起来非常简单--尽管没有明确的文档记录,但是相机帧和运动数据时间戳都是基于mach_absolute_time()时间戳的。
这是一个单调的计时器,在启动时被重置,但重要的是,当设备处于休眠状态时也停止计数。因此,要把它转换成标准的“挂钟”时间,没有一种简单的方法。
谢天谢地,您不需要这样做,因为时间戳是可直接比较的-- motion.timestamp以秒为单位,您可以在回调中注销mach_absolute_time(),以确定它是相同的时间基。我的快速测试显示,在处理程序中,运动时间戳通常在mach_absolute_time之前约为2ms,这似乎适合于将数据报告给应用程序所需的时间。
注mach_absolute_time()以滴答单位表示,需要转换为纳秒;在iOS 10及更高版本上,您只需使用等效的clock_gettime_nsec_np(CLOCK_UPTIME_RAW); ( 做同样的事 )即可。
[_motionManager
startDeviceMotionUpdatesUsingReferenceFrame:CMAttitudeReferenceFrameXArbitraryZVertical
toQueue:[NSOperationQueue currentQueue]
withHandler:^(CMDeviceMotion * _Nullable motion, NSError * _Nullable error) {
// motion.timestamp is in seconds; convert to nanoseconds
uint64_t motionTimestampNs = (uint64_t)(motion.timestamp * 1e9);
// Get conversion factors from ticks to nanoseconds
struct mach_timebase_info timebase;
mach_timebase_info(&timebase);
// mach_absolute_time in nanoseconds
uint64_t ticks = mach_absolute_time();
uint64_t machTimeNs = (ticks * timebase.numer) / timebase.denom;
int64_t difference = machTimeNs - motionTimestampNs;
NSLog(@"Motion timestamp: %llu, machTime: %llu, difference %lli", motionTimestampNs, machTimeNs, difference);
}];对于相机来说,时差也是一样的:
// In practice gives the same value as the CMSampleBufferGetOutputPresentationTimeStamp
// but this is the media's "source" timestamp which feels more correct
CMTime frameTime = CMSampleBufferGetPresentationTimeStamp(sampleBuffer);
uint64_t frameTimestampNs = (uint64_t)(CMTimeGetSeconds(frameTime) * 1e9);在这里,时间戳和被调用的处理程序之间的延迟要大一点,通常以10毫秒为单位。
我们现在需要考虑的是,相机框上的时间戳实际上意味着什么--这里有两个问题:有限曝光时间和滚动快门。
滚动快门意味着图像的所有扫描线并不是同时被捕获的--上面的行首先被捕获,下面的行是最后的。这种数据的滚动读出分布在整个帧时间,因此在30 FPS摄像机模式下,最终scanline的曝光开始/结束时间几乎正好是第一条扫描线各自的开始/结束时间之后的1/30秒。
我的测试表明,AVFoundation帧中的表示时间戳是帧读出的开始,即第一条扫描线曝光的结束。所以最后一条扫描线曝光的结束是在这之后的frameDuration秒,而第一条扫描线曝光的开始是在这之前的exposureTime秒。因此,帧曝光中心的时间戳(图像中扫描线曝光的中点)可以计算为:
const double frameDuration = 1.0/30; // rolling shutter effect, depends on camera mode
const double exposure = avCaptureDevice.exposureDuration;
CMTime frameTime = CMSampleBufferGetPresentationTimeStamp(sampleBuffer);
double midFrameTime = CMTimeGetSeconds(frameTime) - exposure * 0.5 + frameDuration * 0.5;在室内环境中,曝光通常结束于完整的帧时间,所以从上面的midFrameTime结束与frameTime相同。区别是明显的(在极快的运动)与短曝光,你通常从明亮的户外场景。
为什么最初的方法有不同的补偿
我认为造成偏移的主要原因是,假设第一个帧的时间戳是处理程序运行的时间--也就是说,它不考虑从捕获数据到将数据传递到应用程序之间的任何延迟。特别是如果您对这些处理程序使用主队列,我可以想象第一个帧的回调会被您提到的0.2到0.3s延迟。
发布于 2017-11-27 18:16:22
对于这个问题,我能找到的最好的解决方案是在录制的视频上运行一个特征跟踪器,选择其中一个强大的特征并绘制它沿着X轴移动的速度,然后将这个图与加速度计Y数据相关联。
当沿着横坐标有两个相似的图互相偏移时,有一种叫做互相关的技术可以找到偏移量。
这种方法有一个明显的缺点-它是缓慢的,因为它需要一些视频处理。
https://stackoverflow.com/questions/42116314
复制相似问题