FovUtil.java
/*
* Copyright 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package androidx.camera.camera2.internal;
import android.graphics.Rect;
import android.hardware.camera2.CameraCharacteristics;
import android.util.Size;
import android.util.SizeF;
import androidx.annotation.IntRange;
import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi;
import androidx.camera.camera2.internal.compat.CameraAccessExceptionCompat;
import androidx.camera.camera2.internal.compat.CameraCharacteristicsCompat;
import androidx.camera.camera2.internal.compat.CameraManagerCompat;
import androidx.camera.core.CameraSelector;
import androidx.camera.core.impl.utils.TransformUtils;
import androidx.core.util.Preconditions;
/**
* Contains utility methods related to view angle transformation.
*/
@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java
public class FovUtil {
private static final String TAG = "FovUtil";
// Do not allow instantiation.
private FovUtil() {
}
/**
* Calculates view angle by focal length and sensor length.
*
* <p>The returned view angle is inexact and might not be hundred percent accurate comparing
* to the output image.
*
* <p>The returned view angle should between 0 and 360.
*/
@IntRange(from = 0, to = 360)
public static int focalLengthToViewAngleDegrees(float focalLength, float sensorLength) {
Preconditions.checkArgument(focalLength > 0, "Focal length should be positive.");
Preconditions.checkArgument(sensorLength > 0, "Sensor length should be positive.");
int viewAngleDegrees = (int) Math.toDegrees(
2 * Math.atan(sensorLength / (2 * focalLength)));
Preconditions.checkArgumentInRange(viewAngleDegrees, 0, 360, "The provided focal length "
+ "and sensor length result in an invalid view angle degrees.");
return viewAngleDegrees;
}
/**
* Gets the angle of view of the default camera on the device.
*
* <p>The default cameras is the camera selected by
* {@link CameraSelector#DEFAULT_FRONT_CAMERA} or {@link CameraSelector#DEFAULT_BACK_CAMERA}
* depending on the specified lens facing.
*/
public static int getDeviceDefaultViewAngleDegrees(@NonNull CameraManagerCompat cameraManager,
@CameraSelector.LensFacing int lensFacing) {
try {
String[] cameraIds = cameraManager.getCameraIdList();
for (String cameraId : cameraIds) {
CameraCharacteristicsCompat cameraCharacteristics =
cameraManager.getCameraCharacteristicsCompat(cameraId);
Integer cameraCharacteristicsLensFacing =
cameraCharacteristics.get(CameraCharacteristics.LENS_FACING);
Preconditions.checkNotNull(cameraCharacteristicsLensFacing,
"Lens facing can not be null");
if (cameraCharacteristicsLensFacing == LensFacingUtil.getLensFacingInt(
lensFacing)) {
return focalLengthToViewAngleDegrees(
getDefaultFocalLength(cameraCharacteristics),
getSensorHorizontalLength(cameraCharacteristics));
}
}
} catch (CameraAccessExceptionCompat e) {
throw new IllegalArgumentException("Unable to get the default focal length.");
}
throw new IllegalArgumentException("Unable to get the default focal length with the "
+ "specified lens facing.");
}
/**
* Gets the length of the horizontal side of the sensor.
*
* <p>The horizontal side is the width of the sensor size after rotated by the sensor
* orientation.
*/
public static float getSensorHorizontalLength(
@NonNull CameraCharacteristicsCompat cameraCharacteristics) {
SizeF sensorSize =
cameraCharacteristics.get(
CameraCharacteristics.SENSOR_INFO_PHYSICAL_SIZE);
final Rect activeArrayRect =
cameraCharacteristics.get(CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE);
Size pixelArraySize = cameraCharacteristics.get(
CameraCharacteristics.SENSOR_INFO_PIXEL_ARRAY_SIZE);
final Integer sensorOrientation =
cameraCharacteristics.get(CameraCharacteristics.SENSOR_ORIENTATION);
Preconditions.checkNotNull(sensorSize, "The sensor size can't be null.");
Preconditions.checkNotNull(sensorOrientation, "The sensor orientation can't be "
+ "null.");
Preconditions.checkNotNull(activeArrayRect, "The active array size can't be null.");
Preconditions.checkNotNull(pixelArraySize, "The pixel array size can't be null.");
Size activeArraySize = TransformUtils.rectToSize(activeArrayRect);
if (TransformUtils.is90or270(sensorOrientation)) {
sensorSize = TransformUtils.reverseSizeF(sensorSize);
activeArraySize = TransformUtils.reverseSize(activeArraySize);
pixelArraySize = TransformUtils.reverseSize(pixelArraySize);
}
return sensorSize.getWidth() * activeArraySize.getWidth() / pixelArraySize.getWidth();
}
/**
* Gets the default focal length from a {@link CameraCharacteristics}.
*
* <p>If the camera is a logical camera that consists of multiple physical cameras, the
* default focal length is the focal length of the physical camera that produces image at
* zoom ratio {@code 1.0}.
*/
public static float getDefaultFocalLength(
@NonNull CameraCharacteristicsCompat cameraCharacteristics) {
final float[] focalLengths =
cameraCharacteristics.get(CameraCharacteristics.LENS_INFO_AVAILABLE_FOCAL_LENGTHS);
Preconditions.checkNotNull(focalLengths, "The focal lengths can not be empty.");
// Assume the first focal length is the default focal length. This will not be true if the
// camera is a logical camera consist of multiple physical cameras and reports multiple
// focal lengths. However for this kind of cameras, it's suggested to use zoom ratio to
// do optical zoom.
return focalLengths[0];
}
}