1 /*
2  * Copyright 2022 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package androidx.camera.camera2.internal;
18 
19 import android.graphics.Rect;
20 import android.hardware.camera2.CameraCharacteristics;
21 import android.util.Size;
22 import android.util.SizeF;
23 
24 import androidx.annotation.IntRange;
25 import androidx.camera.camera2.internal.compat.CameraAccessExceptionCompat;
26 import androidx.camera.camera2.internal.compat.CameraCharacteristicsCompat;
27 import androidx.camera.camera2.internal.compat.CameraManagerCompat;
28 import androidx.camera.core.CameraSelector;
29 import androidx.camera.core.impl.utils.TransformUtils;
30 import androidx.core.util.Preconditions;
31 
32 import org.jspecify.annotations.NonNull;
33 
34 /**
35  * Contains utility methods related to view angle transformation.
36  */
37 public class FovUtil {
38 
39     private static final String TAG = "FovUtil";
40 
41     // Do not allow instantiation.
FovUtil()42     private FovUtil() {
43     }
44 
45     /**
46      * Calculates view angle by focal length and sensor length.
47      *
48      * <p>The returned view angle is inexact and might not be hundred percent accurate comparing
49      * to the output image.
50      *
51      * <p>The returned view angle should between 0 and 360.
52      */
53     @IntRange(from = 0, to = 360)
focalLengthToViewAngleDegrees(float focalLength, float sensorLength)54     public static int focalLengthToViewAngleDegrees(float focalLength, float sensorLength) {
55         Preconditions.checkArgument(focalLength > 0, "Focal length should be positive.");
56         Preconditions.checkArgument(sensorLength > 0, "Sensor length should be positive.");
57 
58         int viewAngleDegrees = (int) Math.toDegrees(
59                 2 * Math.atan(sensorLength / (2 * focalLength)));
60         Preconditions.checkArgumentInRange(viewAngleDegrees, 0, 360, "The provided focal length "
61                 + "and sensor length result in an invalid view angle degrees.");
62 
63         return viewAngleDegrees;
64     }
65 
66     /**
67      * Gets the angle of view of the default camera on the device.
68      *
69      * <p>The default cameras is the camera selected by
70      * {@link CameraSelector#DEFAULT_FRONT_CAMERA} or {@link CameraSelector#DEFAULT_BACK_CAMERA}
71      * depending on the specified lens facing.
72      */
getDeviceDefaultViewAngleDegrees(@onNull CameraManagerCompat cameraManager, @CameraSelector.LensFacing int lensFacing)73     public static int getDeviceDefaultViewAngleDegrees(@NonNull CameraManagerCompat cameraManager,
74             @CameraSelector.LensFacing int lensFacing) {
75         try {
76             String[] cameraIds = cameraManager.getCameraIdList();
77             for (String cameraId : cameraIds) {
78                 CameraCharacteristicsCompat cameraCharacteristics =
79                         cameraManager.getCameraCharacteristicsCompat(cameraId);
80                 Integer cameraCharacteristicsLensFacing =
81                         cameraCharacteristics.get(CameraCharacteristics.LENS_FACING);
82                 Preconditions.checkNotNull(cameraCharacteristicsLensFacing,
83                         "Lens facing can not be null");
84                 if (cameraCharacteristicsLensFacing == LensFacingUtil.getLensFacingInt(
85                         lensFacing)) {
86                     return focalLengthToViewAngleDegrees(
87                             getDefaultFocalLength(cameraCharacteristics),
88                             getSensorHorizontalLength(cameraCharacteristics));
89                 }
90             }
91         } catch (CameraAccessExceptionCompat e) {
92             throw new IllegalArgumentException("Unable to get the default focal length.");
93         }
94 
95         throw new IllegalArgumentException("Unable to get the default focal length with the "
96                 + "specified lens facing.");
97     }
98 
99     /**
100      * Gets the length of the horizontal side of the sensor.
101      *
102      * <p>The horizontal side is the width of the sensor size after rotated by the sensor
103      * orientation.
104      */
getSensorHorizontalLength( @onNull CameraCharacteristicsCompat cameraCharacteristics)105     public static float getSensorHorizontalLength(
106             @NonNull CameraCharacteristicsCompat cameraCharacteristics) {
107         SizeF sensorSize =
108                 cameraCharacteristics.get(
109                         CameraCharacteristics.SENSOR_INFO_PHYSICAL_SIZE);
110         final Rect activeArrayRect =
111                 cameraCharacteristics.get(CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE);
112         Size pixelArraySize = cameraCharacteristics.get(
113                 CameraCharacteristics.SENSOR_INFO_PIXEL_ARRAY_SIZE);
114         final Integer sensorOrientation =
115                 cameraCharacteristics.get(CameraCharacteristics.SENSOR_ORIENTATION);
116         Preconditions.checkNotNull(sensorSize, "The sensor size can't be null.");
117         Preconditions.checkNotNull(sensorOrientation, "The sensor orientation can't be "
118                 + "null.");
119         Preconditions.checkNotNull(activeArrayRect, "The active array size can't be null.");
120         Preconditions.checkNotNull(pixelArraySize, "The pixel array size can't be null.");
121 
122         Size activeArraySize = TransformUtils.rectToSize(activeArrayRect);
123         if (TransformUtils.is90or270(sensorOrientation)) {
124             sensorSize = TransformUtils.reverseSizeF(sensorSize);
125             activeArraySize = TransformUtils.reverseSize(activeArraySize);
126             pixelArraySize = TransformUtils.reverseSize(pixelArraySize);
127         }
128 
129         return sensorSize.getWidth() * activeArraySize.getWidth() / pixelArraySize.getWidth();
130     }
131 
132     /**
133      * Gets the default focal length from a {@link CameraCharacteristics}.
134      *
135      * <p>If the camera is a logical camera that consists of multiple physical cameras, the
136      * default focal length is the focal length of the physical camera that produces image at
137      * zoom ratio {@code 1.0}.
138      */
getDefaultFocalLength( @onNull CameraCharacteristicsCompat cameraCharacteristics)139     public static float getDefaultFocalLength(
140             @NonNull CameraCharacteristicsCompat cameraCharacteristics) {
141         final float[] focalLengths =
142                 cameraCharacteristics.get(CameraCharacteristics.LENS_INFO_AVAILABLE_FOCAL_LENGTHS);
143         Preconditions.checkNotNull(focalLengths, "The focal lengths can not be empty.");
144 
145         // Assume the first focal length is the default focal length. This will not be true if the
146         // camera is a logical camera consist of multiple physical cameras and reports multiple
147         // focal lengths. However for this kind of cameras, it's suggested to use zoom ratio to
148         // do optical zoom.
149         return focalLengths[0];
150     }
151 }
152