You configure each CameraX use case to control different aspects of the use case's operations.
For example, with the image capture use case, you can set a target aspect ratio and a flash mode. The following code shows one example:
Kotlin
val imageCapture = ImageCapture.Builder() .setFlashMode(...) .setTargetAspectRatio(...) .build()
Java
ImageCapture imageCapture = new ImageCapture.Builder() .setFlashMode(...) .setTargetAspectRatio(...) .build();
In addition to configuration options, some use cases expose APIs to dynamically alter settings after the use case has been created. For information on configuration that is specific to the individual use cases, see Implement a preview, Analyze images, and Image capture.
Automatic selection
CameraX automatically provides functionality that is specific to the device that your app is running on. For example, CameraX will automatically determine the best resolution to use if you don't specify a resolution, or if the resolution you specify is unsupported. All of this is handled by the library, eliminating the need for you to write device-specific code.
CameraX's goal is to successfully initialize a camera session. This means CameraX compromises on resolution and aspect ratios based on device capability. The compromise may happen because:
- The device doesn't support the requested resolution.
- The device has compatibility issues, such as legacy devices that require certain resolutions to operate correctly.
- On some devices, certain formats are only available at certain aspect ratios.
- The device has a preference for a "nearest mod16" for JPEG or video encoding.
See
SCALER_STREAM_CONFIGURATION_MAP
for more information.
Although CameraX creates and manages the session, you should always check the returned image sizes on the use case output in your code and adjust accordingly.
Rotation
By default, the camera rotation is set to match the default display's rotation during the creation of the use case. In this default case, CameraX produces outputs to allow the app to easily match what you would expect to see in the preview. You can change the rotation to a custom value to support multi-display devices by passing in the current display orientation when configuring use case objects or dynamically after they have been created.
Your app can set the target rotation using configuration settings. It can then
update rotation settings by using the methods from the use case APIs (such as
ImageAnalysis.setTargetRotation()
),
even while the lifecycle is in a running state. You might use this when the app
is locked to portrait mode—and so no reconfiguration occurs on
rotation—but the photo or analysis use case needs to be aware of the
current rotation of the device. For example, rotation awareness may be needed so
faces are oriented correctly for face detection, or photos are set to landscape
or portrait.
Data for captured images might be stored without rotation information. Exif data contains rotation information so that gallery applications can show the image in the correct orientation after saving.
To display preview data with the correct orientation, you can use the
metadata output from
Preview.PreviewOutput()
to create transforms.
The following code sample shows how to set the rotation on an orientation event:
Kotlin
override fun onCreate() { val imageCapture = ImageCapture.Builder().build() val orientationEventListener = object : OrientationEventListener(this as Context) { override fun onOrientationChanged(orientation : Int) { // Monitors orientation values to determine the target rotation value val rotation : Int = when (orientation) { in 45..134 -> Surface.ROTATION_270 in 135..224 -> Surface.ROTATION_180 in 225..314 -> Surface.ROTATION_90 else -> Surface.ROTATION_0 } imageCapture.targetRotation = rotation } } orientationEventListener.enable() }
Java
@Override public void onCreate() { ImageCapture imageCapture = new ImageCapture.Builder().build(); OrientationEventListener orientationEventListener = new OrientationEventListener((Context)this) { @Override public void onOrientationChanged(int orientation) { int rotation; // Monitors orientation values to determine the target rotation value if (orientation >= 45 && orientation < 135) { rotation = Surface.ROTATION_270; } else if (orientation >= 135 && orientation < 225) { rotation = Surface.ROTATION_180; } else if (orientation >= 225 && orientation < 315) { rotation = Surface.ROTATION_90; } else { rotation = Surface.ROTATION_0; } imageCapture.setTargetRotation(rotation); } }; orientationEventListener.enable(); }
Based on the set rotation, each use case will either rotate the image data directly or provide rotation metadata to the consumers of the non-rotated image data.
- Preview: Metadata output is provided so that the rotation of the target
resolution is known using
Preview.getTargetRotation()
. - ImageAnalysis: Metadata output is provided so that image buffer coordinates are known relative to display coordinates.
- ImageCapture: The image Exif metadata, buffer, or both the buffer and metadata will be altered to note the rotation setting. The value altered depends upon the HAL implementation.
Crop rect
By default, the crop rect is the full buffer rect. You can customize it with
ViewPort
and
UseCaseGroup
. By grouping use
cases and setting the viewport, CameraX guarantees that the crop rects of all
the use cases in the group point to the same area in the camera sensor.
The following code snippet shows how to use these two classes:
Kotlin
val viewPort = ViewPort.Builder(Rational(width, height), display.rotation).build() val useCaseGroup = UseCaseGroup.Builder() .addUseCase(preview) .addUseCase(imageAnalysis) .addUseCase(imageCapture) .setViewPort(viewPort) .build() cameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector, useCaseGroup)
Java
ViewPort viewPort = new ViewPort.Builder( new Rational(width, height), getDisplay().getRotation()).build(); UseCaseGroup useCaseGroup = new UseCaseGroup.Builder() .addUseCase(preview) .addUseCase(imageAnalysis) .addUseCase(imageCapture) .setViewPort(viewPort) .build(); cameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector, useCaseGroup);
ViewPort
defines the buffer rect visible to end users. Then CameraX calculates
the largest possible crop rect based on the properties of the viewport and the
attached use cases. Usually, to achieve a WYSIWYG effect, you should configure
the viewport based on the preview use case. A simple way to get the viewport is
to use PreviewView
.
The following code snippets shows how to get the ViewPort
object:
Kotlin
val viewport = findViewById<PreviewView>(R.id.preview_view).viewPort
Java
ViewPort viewPort = ((PreviewView)findViewById(R.id.preview_view)).getViewPort();
In the preceding example, what the app gets from ImageAnalysis
and
ImageCapture
matches what the end user sees in PreviewView
, assuming the
PreviewView
's scale type is set to the default, FILL_CENTER
. After applying
the crop rect and rotation to the output buffer, the image from all use cases
will be the same, though possibly with different resolutions. For more
information on how to apply the transformation info, see transform
output.
Camera resolution
You can choose to allow CameraX to set image resolution based on a combination of the device capabilities, device’s supported hardware level, use case, and provided aspect ratio. Alternatively, you can set a specific target resolution or a specific aspect ratio in use cases that support that configuration.
Automatic resolution
CameraX can automatically determine the best resolution settings based on the
use cases specified in cameraProcessProvider.bindToLifecycle()
. Whenever
possible, specify all the use cases needed to run concurrently in a single
session in a single bindToLifecycle()
call. CameraX will determine resolutions
based on the set of use cases bound by considering the device’s supported
hardware level and by accounting for device-specific variance (where a device
may exceed or not meet the stream configurations
available).
The intent is to allow the application to run on a wide variety of devices
while minimizing device-specific code paths.
The default aspect ratio for image capture and image analysis use cases is 4:3.
Use cases have a configurable aspect ratio to allow the application to specify the desired aspect ratio based on UI design. CameraX output will be produced to match the aspect ratios requested as closely as the device supports. If there is no exact-match resolution supported, the one that fulfills the most conditions is selected. Thus the application dictates how the camera should appear in the app, and CameraX determines the best camera resolution settings to satisfy that on different devices.
For example an app can do any of the following:
- Specify a target resolution of 4:3 or 16:9 for a use case
- Specify a custom resolution, which CameraX will attempt to find the closest match to
- Specify a cropping aspect ratio for
ImageCapture
CameraX will choose the internal Camera2 surface resolutions automatically. The following table shows the resolutions:
Use case | Internal surface resolution | Output data resolution |
---|---|---|
Preview | Aspect Ratio: The resolution that best fits the target to the setting. | Internal surface resolution. Metadata is provided to allow a View to crop, scale, and rotate for the target aspect ratio. |
Default resolution: Highest preview resolution, or highest device-preferred resolution that matches aspect ratio above. | ||
Max resolution: Preview size, which refers to the best size match to the device's screen resolution, or to 1080p (1920x1080), whichever is smaller. | ||
Image analysis | Aspect ratio: The resolution that best fits the target to the setting. | Internal surface resolution. |
Default resolution: The default target resolution setting is 640x480. Adjusting both target resolution and corresponding aspect ratio will result in a best-supported resolution under 1080p. | ||
Max resolution: This is limited by CameraX to 1080p. The target
resolution is set as 640x480 by default, so if you want a resolution
larger than 640x480, you must use
setTargetResolution()
and
setTargetAspectRatio()
to get the closest one from the supported resolutions.
|
||
Image capture | Aspect ratio: Aspect ratio that best fits the setting. | Internal surface resolution. |
Default resolution: Highest resolution available, or highest device-preferred resolution that matches aspect ratio above. | ||
Max resolution: The camera device's maximum output resolution for
JPEG format from
StreamConfigurationMap.getOutputSizes()
|
Specify a resolution
You can set specific resolutions when building use cases using the
setTargetResolution(Size resolution)
method, as shown in the following code
sample:
Kotlin
val imageAnalysis = ImageAnalysis.Builder() .setTargetResolution(Size(1280, 720)) .build()
Java
ImageAnalysis imageAnalysis = new ImageAnalysis.Builder() .setTargetResolution(new Size(1280, 720)) .build();
You can't set both target aspect ratio and target resolution on the same use
case. Doing so will throw an IllegalArgumentException
when building the
config object.
Express the resolution Size
in the coordinate
frame after rotating the supported sizes by the target rotation. For example, a
device with portrait natural orientation in natural target rotation requesting a
portrait image may specify 480x640, and the same device, rotated 90 degrees and
targeting landscape orientation may specify 640x480.
The maximum available resolution that you can select for an
ImageAnalysis
is 1080p. The
limitation of 1080p for ImageAnalysis
considers both performance and quality
factors so that users can obtain reasonable quality and a smooth output stream.
The target resolution attempts to establish a minimum bound for the image
resolution. The actual image resolution will be the closest available
resolution in size that is not smaller than the target resolution, as determined
by the Camera implementation. However, if no resolution exists that is equal to
or larger than the target resolution, the nearest available resolution smaller
than the target resolution will be chosen. Resolutions with the same aspect
ratio of the provided Size
are given higher priority than resolutions of
different aspect ratios.
CameraX will apply the best suitable resolution based on the requests. If the
primary need is to satisfy aspect ratio, specify only
setTargetAspectRatio
, and CameraX will determine a specific resolution
suitable based on the device. If the primary need of the app is to specify a
resolution in order to make image processing more efficient (for example a
small or mid-sized image based on device processing capability), use
setTargetResolution(Size resolution)
.
If your app requires an exact resolution, see the table within
createCaptureSession()
to determine what maximum resolutions are supported by each hardware level. To
check for the specific resolutions supported by the current device, see
StreamConfigurationMap.getOutputSizes(int)
.
If your app is running on Android 10 or higher, you can use
isSessionConfigurationSupported()
to verify a specific SessionConfiguration
.
Control focus
The CameraControl
API offers Tap-to-Focus capabilities. Start by getting a
CameraControl
object, as shown in the following code:
Kotlin
val camera = processCameraProvider.bindToLifecycle(...) val cameraControl = camera.getCameraControl()
Java
Camera camera = processCameraProvider.bindToLifecycle(...); CameraControl cameraControl = camera.getCameraControl();
Use
MeteringPointFactory
,
MeteringPoint
,
MeteringMode
,
and
FocusMeteringAction
to run Tap-to-Focus:
Kotlin
val factory = SurfaceOrientedMeteringPointFactory(width, height) val point = factory.createPoint(x, y) val action = FocusMeteringAction.Builder(point, FocusMeteringAction.FLAG_AF) .addPoint(point2, FocusMeteringAction.FLAG_AE) // could have many // auto calling cancelFocusAndMetering in 5 seconds .setAutoCancelDuration(5, TimeUnit.SECONDS) .build() val future = cameraControl.startFocusAndMetering(action) future.addListener( Runnable { val result = future.get() // process the result } , executor)
Java
MeteringPointFactory factory = new SurfaceOrientedMeteringPointFactory(width, height); MeteringPoint point = factory.createPoint(x, y); FocusMeteringAction action = new FocusMeteringAction.Builder(point, FocusMeteringAction.FLAG_AF) .addPoint(point2, FocusMeteringAction.FLAG_AE) // could have many // auto calling cancelFocusAndMetering in 5 seconds .setAutoCancelDuration(5, TimeUnit.SECONDS) .build(); ListenableFuturefuture = cameraControl.startFocusAndMetering(action) future.addListener( () -> { try { FocusMeteringResult result = future.get(); // process the result } catch (Exception e) { } } , executor);
Additional resources
To learn more about CameraX, consult the following additional resources.
Codelab
Code sample