如何计算视场角(FOV)?

28

初始环境

我正在开发一款基于位置的增强现实应用程序,需要获取视野范围[FOV](仅在方向更改时更新值,因此我正在寻找一种可以在调用时获取此值的方法)

目标是制作一个与现实相关的“度量尺”,如下所示: Degree Ruler - AR App

我已经使用AVCaptureSession来显示相机流;并使用与CAShapeLayer耦合的路径来绘制标尺。这很有效,但现在我必须使用视野值将我的元素放置在正确的位置(例如选择160°和170°之间的正确空间!)。

实际上,我正在使用以下来源硬编码这些值:https://dev59.com/r3A65IYBdhLWcg3w2yk2#3594424(特别感谢@hotpaw2!)但我不确定它们是否完全准确,而且这也无法处理iPhone 5等。我无法从官方来源(苹果!)获得值,但有一个链接显示了我认为需要的所有iDevice的值(4、4S、5、5S):AnandTech | Some thoughts about the iphone 5s camera improvements

注意:经过个人测试和在线研究,我相当确定这些值是不准确的!这也迫使我使用外部库来检查我使用的iPhone型号以手动初始化我的FOV...并且我必须检查所有支持的设备的值。

###我更喜欢一个"代码解决方案"!###

阅读了这篇文章:iPhone:实时视频颜色信息,焦距,光圈?之后,我试图像建议的那样从AVCaptureStillImageOutput获取exif数据。在我读取到exif数据中的焦距后,可以通过公式计算水平和垂直视场!(或者可能直接获取FOV,就像这里展示的一样:http://www.brianklug.org/2011/11/a-quick-analysis-of-exif-data-from-apples-iphone-4s-camera-samples/-- 注意:更新一定数量后,似乎我们无法直接从exif中获取视野!)


实际要点

来源:http://iphonedevsdk.com/forum/iphone-sdk-development/112225-camera-app-working-well-on-3gs-but-not-on-4s.htmlModified EXIF data doesn't save properly

这是我正在使用的代码:

AVCaptureDevice* camera = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
if (camera != nil)
{
    captureSession = [[AVCaptureSession alloc] init];
    
    AVCaptureDeviceInput *newVideoInput = [[AVCaptureDeviceInput alloc] initWithDevice:camera error:nil];
    
    [captureSession addInput:newVideoInput];
    
    captureLayer = [[AVCaptureVideoPreviewLayer alloc] initWithSession:captureSession];
    captureLayer.frame = overlayCamera.bounds;
    [captureLayer setVideoGravity:AVLayerVideoGravityResizeAspectFill];
    previewLayerConnection=captureLayer.connection;
    [self setCameraOrientation:[[UIApplication sharedApplication] statusBarOrientation]];
    [overlayCamera.layer addSublayer:captureLayer];
    [captureSession startRunning];
    
    AVCaptureStillImageOutput *stillImageOutput = [[AVCaptureStillImageOutput alloc] init];
    NSDictionary *outputSettings = [[NSDictionary alloc] initWithObjectsAndKeys: AVVideoCodecJPEG, AVVideoCodecKey, nil];
    [stillImageOutput setOutputSettings:outputSettings];
    [captureSession addOutput:stillImageOutput];
    
    AVCaptureConnection *videoConnection = nil;
    for (AVCaptureConnection *connection in stillImageOutput.connections)
    {
        for (AVCaptureInputPort *port in [connection inputPorts])
        {
            if ([[port mediaType] isEqual:AVMediaTypeVideo] )
            {
                videoConnection = connection;
                break;
            }
        }
        if (videoConnection) { break; }
    }
    
    [stillImageOutput captureStillImageAsynchronouslyFromConnection:videoConnection
                                                         completionHandler:^(CMSampleBufferRef imageSampleBuffer, NSError *error)
     {
         NSData *imageNSData = [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:imageSampleBuffer];
         
         CGImageSourceRef imgSource = CGImageSourceCreateWithData((__bridge_retained CFDataRef)imageNSData, NULL);

         NSDictionary *metadata = (__bridge NSDictionary *)CGImageSourceCopyPropertiesAtIndex(imgSource, 0, NULL);

         NSMutableDictionary *metadataAsMutable = [metadata mutableCopy];
         
         NSMutableDictionary *EXIFDictionary = [[metadataAsMutable objectForKey:(NSString *)kCGImagePropertyExifDictionary]mutableCopy];
        
         if(!EXIFDictionary)
             EXIFDictionary = [[NSMutableDictionary dictionary] init];

         [metadataAsMutable setObject:EXIFDictionary forKey:(NSString *)kCGImagePropertyExifDictionary];
         
         NSLog(@"%@",EXIFDictionary);
     }];
}

这里是输出:

{
    ApertureValue = "2.52606882168926";
    BrightnessValue = "0.5019629837352776";
    ColorSpace = 1;
    ComponentsConfiguration =     (
        1,
        2,
        3,
        0
    );
    ExifVersion =     (
        2,
        2,
        1
    );
    ExposureMode = 0;
    ExposureProgram = 2;
    ExposureTime = "0.008333333333333333";
    FNumber = "2.4";
    Flash = 16;
    FlashPixVersion =     (
        1,
        0
    );
    FocalLenIn35mmFilm = 40;
    FocalLength = "4.28";
    ISOSpeedRatings =     (
        50
    );
    LensMake = Apple;
    LensModel = "iPhone 4S back camera 4.28mm f/2.4";
    LensSpecification =     (
        "4.28",
        "4.28",
        "2.4",
        "2.4"
    );
    MeteringMode = 5;
    PixelXDimension = 1920;
    PixelYDimension = 1080;
    SceneCaptureType = 0;
    SceneType = 1;
    SensingMethod = 2;
    ShutterSpeedValue = "6.906947890818858";
    SubjectDistance = "69.999";
    UserComment = "[S.D.] kCGImagePropertyExifUserComment";
    WhiteBalance = 0;
}

我认为我拥有计算视场角所需的所有内容。但是它们是否是正确的值?因为在阅读了很多给出不同焦距值的不同网站后,我有点困惑!另外,我的像素尺寸似乎是错误的!
通过http://en.wikipedia.org/wiki/Angle_of_view这个链接,这是我计划使用的公式:
FOV = (IN_DEGREES(   2*atan( (d) / (2  * f) )   ));
// d = sensor dimensions (mm)
// f = focal length (mm)

我的问题

我的方法和公式是否正确,如果是,我需要向函数传递哪些值?


精度

  • FOV(视野)是我认为需要使用的,如果您有任何建议,可以让尺子与现实匹配;我会接受答案!
  • 在增强现实视图控制器中,缩放被禁用,因此当摄像头初始化时,我的视野是固定的,并且直到用户旋转手机之前都无法更改!


也许尝试获得更多的声望,这样你就可以投资更高的赏金...也许这会帮助获得更多的关注。 - Pavan
1
ExifTool可以从Exif数据中计算FOV和其他一些内容。也许您可以查看ExifTool的输出并进行反向工程。 - Jim Merkel
@Pavan:我想这就是我要做的事情!JimMerkel:这是个好主意,我不知道Phil Harvey的ExifTool,谢谢你!如果你有评论,我会给你赏金的。 - Humbertda
像素尺寸可能是正确的:相机硬件处于视频模式下,预设录制为1080p。假设以36×24mm的“35mm”帧大小为前提,FOV应该可以从EXIF数据中计算出来,但显然iPhone 4S传感器是4.54×3.42mm,因此数字并不完全匹配(假设没有水平裁剪)。 - tc.
5个回答

28

iOS 7及以上版本可以按以下方式操作:

float FOV = camera.activeFormat.videoFieldOfView;

其中 camera 是您的 AVCaptureDevice。根据您选择的视频会话预设,这甚至可以在同一设备上发生变化。它是水平视场(以度为单位),因此您需要根据显示尺寸计算垂直视场。

这里是 苹果公司的参考资料


1
太完美了!这个答案应该被所有人知道...你让我的一天变得美好,谢谢!(希望你能赢得赏金!) - Humbertda
1
这是两个视野中更宽的一个,即在横向方向上工作时的水平视野。 - Nestor

3
回答您的问题:
“我的方法和公式看起来正确吗?”也许是正确的,但它们看起来过于复杂。
“如果是正确的话,我应该向函数传递哪些值?”我不知道,但如果目标是计算HFOV和VFOV,这里有一个代码示例,可以通过编程方式找到水平视角,这是目前在Swift中唯一可以访问的视角,然后根据iPhone 6的纵横比16:9计算垂直视角。
    let devices = AVCaptureDevice.devices()
    var captureDevice : AVCaptureDevice?
    for device in devices {
        if (device.hasMediaType(AVMediaTypeVideo)) {
            if(device.position == AVCaptureDevicePosition.Back) {
                captureDevice = device as? AVCaptureDevice
            }
        }
    }
    if let retrievedDevice = captureDevice {
        var HFOV : Float = retrievedDevice.activeFormat.videoFieldOfView
        var VFOV : Float = ((HFOV)/16.0)*9.0
    }

还要记得导入AVFoundation,如果你想让它工作!

3
苹果还发布了一份包含全部相机规格详细信息的列表,其中包括视野范围(FOV)。这些数值与使用以下代码可检索到的数值相匹配:`float FOV = camera.activeFormat.videoFieldOfView;`。请参考此链接查看相关内容。

1
从FOV方程中可以得知需要焦距和传感器尺寸。Exif数据中有焦距但没有传感器尺寸。我只找到一个相机制造商(佳能)在元数据中提供传感器尺寸,而且这是在他们的CRW原始文件中。因此,您将需要根据相机或智能手机进行传感器尺寸的查找表。以下维基百科链接列出了众多相机和一些新款iPhone的传感器尺寸。列表位于Wiki文章的末尾。http://en.wikipedia.org/wiki/Image_sensor_format 另一个获取此类信息的来源可能是各种摄影博客。希望这可以帮助您。
如果使用数字变焦,则将焦距乘以变焦值。 Exif数据包含变焦因子。
摄影博客和网站,如http://www.kenrockwell.com,提供大量有关相机传感器尺寸的信息。
一个修正:实际上,对于焦平面X分辨率、焦平面Y分辨率(以像素为单位)和焦平面分辨率单位,存在Exif标签。通过这些标签和图像尺寸,您可以计算传感器的大小。但并非所有相机都提供这些Exif标签。例如,iPhone 4不提供这些标签。

好的,我想最简单的方法是将所有iPhone视野大小硬编码到数组中!谢谢你的回答! - Humbertda

0

视野直径是当你看进显微镜时能够看到的光圈直径。

如果约有10个圆形细胞可以横着放在视野直径上,而且如果你知道直径是多少毫米,那么你可以将总直径除以适合的细胞数量,来计算每个细胞的大小。

总直径除以适合的细胞数量等于每个细胞的大小(直径)。

例子:

如果视野直径为1.5毫米,而且如果有十个细胞能够排成一行,那么每个细胞的大小为:

1.5毫米/10 = 0.15毫米。

0.15毫米。

请记住,每次改变放大倍数都会改变你所能看到的光圈直径。因此,每个放大倍数都有自己的直径。

如果你在显微镜下面放一把尺子,放大倍数越高,你能看到的刻度线就越少。


所以放大倍数不会改变,缩放不适用于该应用程序,最好禁用它,我认为!实际上这是我想的方式,但是以像素为单位:如果我有FOV和屏幕尺寸,我就可以知道两个度之间有多少像素。(就像你提出的1.5mm / 10一样)。我也可以用毫米来做,然后再将其转换为像素,但我的问题仍然悬而未决:如何在iOS中获得相机FOV?(以毫米或度为单位)感谢您的回答! - Humbertda

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接