21

I'm trying to determine the degree size of the field-of-view of a Droid Incredible smartphone's camera. I need to know this value for an application that I'm developing. Does anyone know how I can find out/calculate it programmatically?

GobiasKoffi
  • 3,754
  • 14
  • 54
  • 65

3 Answers3

59

The Camera.Parameters getHorizontalViewAngle() and getVerticalViewAngle() functions provide you with the base view angles. I say "base", because these apply only to the Camera itself in an unzoomed state, and the values returned by these functions do not change even when the view angle itself does.

Camera.Parameters p = camera.getParameters();
double thetaV = Math.toRadians(p.getVerticalViewAngle());
double thetaH = Math.toRadians(p.getHorizontalViewAngle());

Two things cause your "effective" view angle to change: zoom, and using a preview aspect ratio that does not match the camera aspect ratio.

Basic Math

The trigonometry of field-of-view (Θ) is fairly simple:

tan(Θ/2) = x / 2z

x = 2z tan(Θ/2)

x is the linear distance viewable at distance z; i.e., if you held up a ruler at distance z=1 meter, you would be able to see x meters of that ruler.

For instance on my camera, horizontal field of view is 52.68° while vertical field of view is 40.74°. Convert these to radians and plug them into the formula with an arbitrary z value of 100m, and you get x values of 99.0m(horizontal) and 74.2m(vertical). This is a 4:3 aspect ratio.

Zoom

Applying this math to zoom levels is only slightly harder. Now, x remains constant and it is z that changes in a known ratio; we must determine Θ.

tan (Θ/2) = x / (2z)

tan (Θ'/2) = x / (2z')

Θ' = 2 atan((z / z') tan(Θ/2))

Where z is the base zoom level (100), z' is the current zoom level (from CameraParameters.getZoomRatios), Θ is the base horizontal/vertical field of view, and Θ' is the effective field of view. Adding on degree->radian conversions makes this rather verbose.

private static double zoomAngle(double degrees, int zoom) {
    double theta = Math.toRadians(degrees);
    return 2d * Math.atan(100d * Math.tan(theta / 2d) / zoom);
}

Camera.Parameters p = camera.getParameters();
int zoom = p.getZoomRatios().get(p.getZoom()).intValue();
double thetaH = zoomAngle(p.getHorizontalViewAngle(), zoom);
double thetaV = zoomAngle(p.getVerticalViewAngle(), zoom);

Aspect Ratio

While the typical camera is a 4:3 aspect ratio, the preview may also be available in 5:3 and 16:9 ratios and this seems to be accomplished by actually extending the horizontal field of view. This appears to be undocumented, hence unreliable, but by assuming that's how it works we can calculate the field of view.

The math is similar to the zoom calculations; however, in this case z remains constant and it is x that changes. By assuming that the vertical view angle remains unchanged while the horizontal view angle is varied as the aspect ratio changes, it's possible to calculate the new effective horizontal view angle.

tan(Θ/2) = v / (2z)

tan(Θ'/2) = h / (2z)

2z = v / tan(Θ/2)

Θ' = 2 atan((h/v) tan(Θ/2))

Here h/v is the aspect ratio and Θ is the base vertical field of view, while Θ' is the effective horizontal field of view.

Camera.Parameters p = camera.getParameters();
int zoom = p.getZoomRatios().get(p.getZoom()).intValue();
Camera.Size sz = p.getPreviewSize();
double aspect = (double) sz.width / (double) sz.height;
double thetaV = Math.toRadians(p.getVerticalViewAngle());
double thetaH = 2d * Math.atan(aspect * Math.tan(thetaV / 2));
thetaV = 2d * Math.atan(100d * Math.tan(thetaV / 2d) / zoom);
thetaH = 2d * Math.atan(100d * Math.tan(thetaH / 2d) / zoom);

As I said above, since this appears to be undocumented, it is simply a guess that it will apply to all devices; it should be considered a hack. The correct solution would be splitting off a new set of functions getCurrentHorizontalViewAngle and getCurrentVerticalViewAngle.

Community
  • 1
  • 1
Dorje
  • 1,037
  • 1
  • 9
  • 9
  • 3
    Can the getHorizontalViewAngle() and getVerticalViewAngle() results themselves be trusted for a wide variety of devices? – Alex Cohn Oct 27 '12 at 03:49
  • 2
    Are there similar methods (getVertical/HorizontalViewingAngle() in the camera2 API? I didn't see them, and was wondering if I missed them, or if they are not present. Probably in CameraCharacteristics? – Innova Aug 20 '15 at 15:20
  • 1
    @Innova, I am wondering the same thing--it may be that those values were overly simplistic, but I've searched through all of the camera2 documentation, and I haven't found any sort of straightforward way to do this. – TonyTheJet Oct 10 '16 at 19:15
  • do you know why I get vertical angle bigger than horizontal? p.getVerticalViewAngle() == 61.8, p.getHorizontalViewAngle() == 56.6 (horizontal is smaller, how is it possible?) – user25 Jun 29 '17 at 12:58
  • all those angles are initially wrong (56.6, 61.8), I calculated for 16/9 aspect (1920x1080 resolution) manually (https://stackoverflow.com/a/3261794/4548520) and I got 39 degrees for vertical view and 65 for horizontal. So initially there is no right angle for 4:3 ratio so your code won't work – user25 Jun 30 '17 at 12:31
  • @Dorje can please check this question? https://stackoverflow.com/questions/67375781/without-additional-calculation-camera-or-camera2-apis-return-fov-angle-values-fo – user924 May 03 '21 at 21:25
  • "While the typical camera is a 4:3 aspect ratio, the preview may also be available in 5:3 and 16:9 ratios and this seems to be accomplished by actually extending the horizontal field of view." - This isn't true for devices with 4:3 aspect ratio sensors - 16:9 outputs are always smaller FOV than 4:3 outputs on such sensors, in vertical FOV (horizontal FOVs would be identical). If you have a 16:9 sensor, then a 4:3 will have a narrower horizontal FOV than a 16:9 output. But few devices have those. The FOV values for the old camera API are for the largest picture size and its aspect ratio. – Eddy Talvala May 06 '21 at 18:22
15

Unless there's some API call for that (I'm not an Android programmer, I wouldn't know), I would just snap a picture of a ruler from a known distance away, see how much of the ruler is shown in the picture, then use trigonometry to find the angle like this:

enter image description here

now you have the two distances l and d from the figure. With some simple goniometry, one can obtain:

tan(α/2) = (l/2)/d,

hence

α = 2*atan(l/2d)

So with this formula you can calculate the horizontal field-of-view of your camera. Of course measuring the vertical f.o.v. goes exactly the same way except that you then need to view the object in its vertical position.

Then you can hard-code it as a constant in your program. (A named constant, of course, so it'd be easy to change :-p)

AbdelHady
  • 7,590
  • 8
  • 48
  • 80
David Z
  • 116,302
  • 26
  • 230
  • 268
  • 1
    Better yet, make it user-configurable so people who don't have a Droid Incredible can adjust/calibrate it. – EboMike Jul 16 '10 at 03:57
  • @emddudley: true, I never thought about the phone having a zoom level. Although given the view angle at any one zoom level, it should be possible to calculate it at any other zoom level. – David Z Jul 16 '10 at 04:01
  • That is, `alpha = 2 * atan(l / (2 * d))`. – Yeti Sep 11 '17 at 11:45
9

I have a Droid Incredible as well. Android 2.2 introduced the functions you are looking for. In my code, I have:

public double getHVA() {
    return camera.getParameters().getHorizontalViewAngle();
}

public double getVVA() {
    return camera.getParameters().getVerticalViewAngle();
}

However, these require that you have the camera open. I'd be interested to know if there is a "best practices" way to not have to open the camera each time to get those values.

@David Zaslavsky - how? What is the mathematical relationship between the zoom levels? I can't find it anywhere (I asked in this question: What do the Android camera zoom numbers mathematically represent?)

Community
  • 1
  • 1
Max
  • 1,218
  • 1
  • 12
  • 20