1 /* 2 * Copyright (C) 2019 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.core; 18 19 import static androidx.camera.core.impl.ImageAnalysisConfig.OPTION_BACKPRESSURE_STRATEGY; 20 import static androidx.camera.core.impl.ImageAnalysisConfig.OPTION_IMAGE_QUEUE_DEPTH; 21 import static androidx.camera.core.impl.ImageAnalysisConfig.OPTION_IMAGE_READER_PROXY_PROVIDER; 22 import static androidx.camera.core.impl.ImageAnalysisConfig.OPTION_ONE_PIXEL_SHIFT_ENABLED; 23 import static androidx.camera.core.impl.ImageAnalysisConfig.OPTION_OUTPUT_IMAGE_FORMAT; 24 import static androidx.camera.core.impl.ImageAnalysisConfig.OPTION_OUTPUT_IMAGE_ROTATION_ENABLED; 25 import static androidx.camera.core.impl.ImageInputConfig.OPTION_INPUT_DYNAMIC_RANGE; 26 import static androidx.camera.core.impl.ImageOutputConfig.OPTION_CUSTOM_ORDERED_RESOLUTIONS; 27 import static androidx.camera.core.impl.ImageOutputConfig.OPTION_DEFAULT_RESOLUTION; 28 import static androidx.camera.core.impl.ImageOutputConfig.OPTION_MAX_RESOLUTION; 29 import static androidx.camera.core.impl.ImageOutputConfig.OPTION_RESOLUTION_SELECTOR; 30 import static androidx.camera.core.impl.ImageOutputConfig.OPTION_SUPPORTED_RESOLUTIONS; 31 import static androidx.camera.core.impl.ImageOutputConfig.OPTION_TARGET_ASPECT_RATIO; 32 import static androidx.camera.core.impl.ImageOutputConfig.OPTION_TARGET_RESOLUTION; 33 import static androidx.camera.core.impl.ImageOutputConfig.OPTION_TARGET_ROTATION; 34 import static androidx.camera.core.impl.UseCaseConfig.OPTION_CAPTURE_CONFIG_UNPACKER; 35 import static androidx.camera.core.impl.UseCaseConfig.OPTION_CAPTURE_TYPE; 36 import static androidx.camera.core.impl.UseCaseConfig.OPTION_DEFAULT_CAPTURE_CONFIG; 37 import static androidx.camera.core.impl.UseCaseConfig.OPTION_DEFAULT_SESSION_CONFIG; 38 import static androidx.camera.core.impl.UseCaseConfig.OPTION_HIGH_RESOLUTION_DISABLED; 39 import static androidx.camera.core.impl.UseCaseConfig.OPTION_SESSION_CONFIG_UNPACKER; 40 import static androidx.camera.core.impl.UseCaseConfig.OPTION_SURFACE_OCCUPANCY_PRIORITY; 41 import static androidx.camera.core.impl.UseCaseConfig.OPTION_TARGET_CLASS; 42 import static androidx.camera.core.impl.UseCaseConfig.OPTION_TARGET_NAME; 43 import static androidx.camera.core.impl.UseCaseConfig.OPTION_ZSL_DISABLED; 44 import static androidx.camera.core.internal.ThreadConfig.OPTION_BACKGROUND_EXECUTOR; 45 46 import android.graphics.ImageFormat; 47 import android.graphics.Matrix; 48 import android.graphics.PixelFormat; 49 import android.graphics.Rect; 50 import android.media.CamcorderProfile; 51 import android.media.ImageReader; 52 import android.util.Pair; 53 import android.util.Size; 54 import android.view.Display; 55 import android.view.Surface; 56 import android.view.View; 57 58 import androidx.annotation.GuardedBy; 59 import androidx.annotation.IntDef; 60 import androidx.annotation.RequiresApi; 61 import androidx.annotation.RestrictTo; 62 import androidx.annotation.RestrictTo.Scope; 63 import androidx.camera.core.impl.CameraInfoInternal; 64 import androidx.camera.core.impl.CameraInternal; 65 import androidx.camera.core.impl.CaptureConfig; 66 import androidx.camera.core.impl.Config; 67 import androidx.camera.core.impl.ConfigProvider; 68 import androidx.camera.core.impl.DeferrableSurface; 69 import androidx.camera.core.impl.ImageAnalysisConfig; 70 import androidx.camera.core.impl.ImageInputConfig; 71 import androidx.camera.core.impl.ImageOutputConfig; 72 import androidx.camera.core.impl.ImageOutputConfig.RotationValue; 73 import androidx.camera.core.impl.ImmediateSurface; 74 import androidx.camera.core.impl.MutableConfig; 75 import androidx.camera.core.impl.MutableOptionsBundle; 76 import androidx.camera.core.impl.OptionsBundle; 77 import androidx.camera.core.impl.SessionConfig; 78 import androidx.camera.core.impl.StreamSpec; 79 import androidx.camera.core.impl.UseCaseConfig; 80 import androidx.camera.core.impl.UseCaseConfigFactory; 81 import androidx.camera.core.impl.utils.Threads; 82 import androidx.camera.core.impl.utils.executor.CameraXExecutors; 83 import androidx.camera.core.internal.TargetConfig; 84 import androidx.camera.core.internal.ThreadConfig; 85 import androidx.camera.core.internal.compat.quirk.OnePixelShiftQuirk; 86 import androidx.camera.core.internal.utils.SizeUtil; 87 import androidx.camera.core.resolutionselector.AspectRatioStrategy; 88 import androidx.camera.core.resolutionselector.ResolutionSelector; 89 import androidx.camera.core.resolutionselector.ResolutionStrategy; 90 import androidx.core.util.Preconditions; 91 import androidx.lifecycle.LifecycleOwner; 92 93 import org.jspecify.annotations.NonNull; 94 import org.jspecify.annotations.Nullable; 95 96 import java.lang.annotation.Retention; 97 import java.lang.annotation.RetentionPolicy; 98 import java.util.ArrayList; 99 import java.util.List; 100 import java.util.Objects; 101 import java.util.UUID; 102 import java.util.concurrent.Executor; 103 104 /** 105 * A use case providing CPU accessible images for an app to perform image analysis on. 106 * 107 * <p>ImageAnalysis acquires images from the camera via an {@link ImageReader}. Each image 108 * is provided to an {@link ImageAnalysis.Analyzer} function which can be implemented by application 109 * code, where it can access image data for application analysis via an {@link ImageProxy}. 110 * 111 * <p>The application is responsible for calling {@link ImageProxy#close()} to close the image. 112 * Failing to close the image will cause future images to be stalled or dropped depending on the 113 * backpressure strategy. 114 */ 115 public final class ImageAnalysis extends UseCase { 116 117 //////////////////////////////////////////////////////////////////////////////////////////// 118 // [UseCase lifetime constant] - Stays constant for the lifetime of the UseCase. Which means 119 // they could be created in the constructor. 120 //////////////////////////////////////////////////////////////////////////////////////////// 121 122 /** 123 * Only deliver the latest image to the analyzer, dropping images as they arrive. 124 * 125 * <p>This strategy ignores the value set by {@link Builder#setImageQueueDepth(int)}. 126 * Only one image will be delivered for analysis at a time. If more images are produced 127 * while that image is being analyzed, they will be dropped and not queued for delivery. 128 * Once the image being analyzed is closed by calling {@link ImageProxy#close()}, the 129 * next latest image will be delivered. 130 * 131 * <p>Internally this strategy may make use of an internal {@link Executor} to receive 132 * and drop images from the producer. A performance-tuned executor will be created 133 * internally unless one is explicitly provided by 134 * {@link Builder#setBackgroundExecutor(Executor)}. In order to 135 * ensure smooth operation of this backpressure strategy, any user supplied 136 * {@link Executor} must be able to quickly respond to tasks posted to it, so setting 137 * the executor manually should only be considered in advanced use cases. 138 * 139 * @see Builder#setBackgroundExecutor(Executor) 140 */ 141 public static final int STRATEGY_KEEP_ONLY_LATEST = 0; 142 /** 143 * Block the producer from generating new images. 144 * 145 * <p>Once the producer has produced the number of images equal to the image queue depth, 146 * and none have been closed, the producer will stop producing images. Note that images 147 * may be queued internally and not be delivered to the analyzer until the last delivered 148 * image has been closed with {@link ImageProxy#close()}. These internally queued images 149 * will count towards the total number of images that the producer can provide at any one 150 * time. 151 * 152 * <p>When the producer stops producing images, it may also stop producing images for 153 * other use cases, such as {@link Preview}, so it is important for the analyzer to keep 154 * up with frame rate, <i>on average</i>. Failure to keep up with frame rate may lead to 155 * jank in the frame stream and a diminished user experience. If more time is needed for 156 * analysis on <i>some</i> frames, consider increasing the image queue depth with 157 * {@link Builder#setImageQueueDepth(int)}. 158 * 159 * @see Builder#setImageQueueDepth(int) 160 */ 161 public static final int STRATEGY_BLOCK_PRODUCER = 1; 162 163 /** 164 * Images sent to the analyzer will have YUV format. 165 * 166 * <p>All {@link ImageProxy} sent to {@link Analyzer#analyze(ImageProxy)} will have 167 * format {@link android.graphics.ImageFormat#YUV_420_888} 168 * 169 * @see Builder#setOutputImageFormat(int) 170 */ 171 public static final int OUTPUT_IMAGE_FORMAT_YUV_420_888 = 1; 172 173 /** 174 * Images sent to the analyzer will have RGBA format. 175 * 176 * <p>All {@link ImageProxy} sent to {@link Analyzer#analyze(ImageProxy)} will have 177 * format {@link android.graphics.PixelFormat#RGBA_8888} 178 * 179 * <p>The output order is a single-plane with the order of R, G, B, A in increasing byte index 180 * in the {@link java.nio.ByteBuffer}. The {@link java.nio.ByteBuffer} is retrieved from 181 * {@link ImageProxy.PlaneProxy#getBuffer()}. 182 * 183 * @see Builder#setOutputImageFormat(int) 184 */ 185 public static final int OUTPUT_IMAGE_FORMAT_RGBA_8888 = 2; 186 187 /** 188 * Images sent to the analyzer will be formatted in NV21. 189 * 190 * <p>All {@link ImageProxy} sent to {@link Analyzer#analyze(ImageProxy)} will be in 191 * {@link ImageFormat#YUV_420_888} format with their image data formatted in NV21. 192 * 193 * <p>The output {@link ImageProxy} has three planes with the order of Y, U, V. The pixel 194 * stride of U or V planes are 2. The byte buffer pointer position of V plane will be ahead 195 * of the position of the U plane. Applications can directly read the <code>plane[2]</code> 196 * to get all the VU interleaved data. 197 * 198 * <p>Due to limitations on some Android devices in producing images in NV21 format, the 199 * {@link android.media.Image} object obtained from {@link ImageProxy#getImage()} will be the 200 * original image produced by the camera capture pipeline. This may result in discrepancies 201 * between the {@link android.media.Image} and the {@link ImageProxy}, such as: 202 * 203 * <ul> 204 * <li>Plane data may differ. 205 * <li>Width and height may differ. 206 * <li>Other properties may also differ. 207 * </ul> 208 * 209 * <p>Developers should be aware of these potential differences and use the properties from the 210 * {@link ImageProxy} when necessary. 211 * 212 * @see Builder#setOutputImageFormat(int) 213 */ 214 public static final int OUTPUT_IMAGE_FORMAT_NV21 = 3; 215 216 /** 217 * Provides a static configuration with implementation-agnostic options. 218 */ 219 @RestrictTo(Scope.LIBRARY_GROUP) 220 public static final Defaults DEFAULT_CONFIG = new Defaults(); 221 private static final String TAG = "ImageAnalysis"; 222 // ImageReader depth for KEEP_ONLY_LATEST mode. 223 private static final int NON_BLOCKING_IMAGE_DEPTH = 4; 224 @BackpressureStrategy 225 private static final int DEFAULT_BACKPRESSURE_STRATEGY = STRATEGY_KEEP_ONLY_LATEST; 226 private static final int DEFAULT_IMAGE_QUEUE_DEPTH = 6; 227 // Default to YUV_420_888 format for output. 228 private static final int DEFAULT_OUTPUT_IMAGE_FORMAT = OUTPUT_IMAGE_FORMAT_YUV_420_888; 229 // One pixel shift for YUV. 230 private static final Boolean DEFAULT_ONE_PIXEL_SHIFT_ENABLED = null; 231 // Default to disabled for rotation. 232 private static final boolean DEFAULT_OUTPUT_IMAGE_ROTATION_ENABLED = false; 233 private final Object mAnalysisLock = new Object(); 234 235 @GuardedBy("mAnalysisLock") 236 @SuppressWarnings("WeakerAccess") /* synthetic access */ 237 ImageAnalysisAbstractAnalyzer mImageAnalysisAbstractAnalyzer; 238 239 //////////////////////////////////////////////////////////////////////////////////////////// 240 // [UseCase lifetime dynamic] - Dynamic variables which could change during anytime during 241 // the UseCase lifetime. 242 //////////////////////////////////////////////////////////////////////////////////////////// 243 244 @GuardedBy("mAnalysisLock") 245 private Executor mSubscribedAnalyzerExecutor; 246 @GuardedBy("mAnalysisLock") 247 private ImageAnalysis.Analyzer mSubscribedAnalyzer; 248 @GuardedBy("mAnalysisLock") 249 private Rect mViewPortCropRect; 250 @GuardedBy("mAnalysisLock") 251 private Matrix mSensorToBufferTransformMatrix; 252 253 //////////////////////////////////////////////////////////////////////////////////////////// 254 // [UseCase attached dynamic] - Can change but is only available when the UseCase is attached. 255 //////////////////////////////////////////////////////////////////////////////////////////// 256 257 @SuppressWarnings("WeakerAccess") /* synthetic accessor */ 258 SessionConfig.Builder mSessionConfigBuilder; 259 260 private @Nullable DeferrableSurface mDeferrableSurface; 261 private SessionConfig.@Nullable CloseableErrorListener mCloseableErrorListener; 262 263 /** 264 * Creates a new image analysis use case from the given configuration. 265 * 266 * @param config for this use case instance 267 */ 268 @SuppressWarnings("WeakerAccess") ImageAnalysis(@onNull ImageAnalysisConfig config)269 ImageAnalysis(@NonNull ImageAnalysisConfig config) { 270 super(config); 271 } 272 273 /** 274 * {@inheritDoc} 275 */ 276 @RestrictTo(Scope.LIBRARY_GROUP) 277 @Override onMergeConfig(@onNull CameraInfoInternal cameraInfo, UseCaseConfig.@NonNull Builder<?, ?, ?> builder)278 protected @NonNull UseCaseConfig<?> onMergeConfig(@NonNull CameraInfoInternal cameraInfo, 279 UseCaseConfig.@NonNull Builder<?, ?, ?> builder) { 280 // Override the target resolution with the value provided by the analyzer. 281 Size analyzerResolution; 282 synchronized (mAnalysisLock) { 283 analyzerResolution = mSubscribedAnalyzer != null 284 ? mSubscribedAnalyzer.getDefaultTargetResolution() : null; 285 } 286 287 if (analyzerResolution == null) { 288 return builder.getUseCaseConfig(); 289 } 290 291 int targetRotation = builder.getMutableConfig().retrieveOption( 292 OPTION_TARGET_ROTATION, Surface.ROTATION_0); 293 // analyzerResolution is a size in the sensor coordinate system, but the legacy 294 // target resolution setting is in the view coordinate system. Flips the 295 // analyzerResolution according to the sensor rotation degrees. 296 if (cameraInfo.getSensorRotationDegrees(targetRotation) % 180 == 90) { 297 analyzerResolution = new Size(/* width= */ analyzerResolution.getHeight(), 298 /* height= */ analyzerResolution.getWidth()); 299 } 300 301 // Merges the analyzerResolution as legacy target resolution setting so that it can take 302 // effect when running the legacy resolution selection logic flow. 303 if (!builder.getUseCaseConfig().containsOption(OPTION_TARGET_RESOLUTION)) { 304 builder.getMutableConfig().insertOption(OPTION_TARGET_RESOLUTION, 305 analyzerResolution); 306 } 307 308 // Merges the analyzerResolution to ResolutionSelector. 309 // Note: the input builder contains the configs that are merging result of default config 310 // and app config (in UseCase#mergeConfigs()). Merging the analyzer default target 311 // resolution depends on the ResolutionSelector set by the app, therefore, need to check 312 // the ResolutionSelector retrieved from UseCase#getAppConfig() to determine how to merge 313 // it. 314 if (builder.getUseCaseConfig().containsOption(OPTION_RESOLUTION_SELECTOR)) { 315 ResolutionSelector appResolutionSelector = 316 getAppConfig().retrieveOption(OPTION_RESOLUTION_SELECTOR, null); 317 // Creates a builder according to whether app has resolution selector setting or not. 318 ResolutionSelector.Builder resolutionSelectorBuilder = 319 appResolutionSelector == null ? new ResolutionSelector.Builder() 320 : ResolutionSelector.Builder.fromResolutionSelector( 321 appResolutionSelector); 322 // Sets a ResolutionStrategy matching to the analyzer default resolution when app 323 // doesn't have resolution strategy setting. 324 if (appResolutionSelector == null 325 || appResolutionSelector.getResolutionStrategy() == null) { 326 resolutionSelectorBuilder.setResolutionStrategy( 327 new ResolutionStrategy(analyzerResolution, 328 ResolutionStrategy.FALLBACK_RULE_CLOSEST_HIGHER_THEN_LOWER)); 329 } 330 // Sets a ResolutionFilter to select the analyzer default resolution in priority only 331 // when the app doesn't have its own resolution selector setting. This can't be set when 332 // app has any ResolutionSelector setting. Otherwise, app might obtain an unexpected 333 // resolution for ImageAnalysis. 334 if (appResolutionSelector == null) { 335 final Size analyzerResolutionFinal = analyzerResolution; 336 resolutionSelectorBuilder.setResolutionFilter( 337 (supportedSizes, rotationDegrees) -> { 338 List<Size> resultList = new ArrayList<>(supportedSizes); 339 if (resultList.contains(analyzerResolutionFinal)) { 340 resultList.remove(analyzerResolutionFinal); 341 resultList.add(0, analyzerResolutionFinal); 342 } 343 return resultList; 344 } 345 ); 346 } 347 builder.getMutableConfig().insertOption(OPTION_RESOLUTION_SELECTOR, 348 resolutionSelectorBuilder.build()); 349 } 350 351 return builder.getUseCaseConfig(); 352 } 353 354 @SuppressWarnings("WeakerAccess") /* synthetic accessor */ createPipeline(@onNull String cameraId, @NonNull ImageAnalysisConfig config, @NonNull StreamSpec streamSpec)355 SessionConfig.Builder createPipeline(@NonNull String cameraId, 356 @NonNull ImageAnalysisConfig config, @NonNull StreamSpec streamSpec) { 357 Threads.checkMainThread(); 358 Size resolution = streamSpec.getResolution(); 359 360 Executor backgroundExecutor = Preconditions.checkNotNull(config.getBackgroundExecutor( 361 CameraXExecutors.highPriorityExecutor())); 362 363 int imageQueueDepth = 364 getBackpressureStrategy() == STRATEGY_BLOCK_PRODUCER ? getImageQueueDepth() 365 : NON_BLOCKING_IMAGE_DEPTH; 366 SafeCloseImageReaderProxy imageReaderProxy; 367 if (config.getImageReaderProxyProvider() != null) { 368 imageReaderProxy = new SafeCloseImageReaderProxy( 369 config.getImageReaderProxyProvider().newInstance( 370 resolution.getWidth(), resolution.getHeight(), getImageFormat(), 371 imageQueueDepth, 0)); 372 } else { 373 imageReaderProxy = 374 new SafeCloseImageReaderProxy(ImageReaderProxys.createIsolatedReader( 375 resolution.getWidth(), 376 resolution.getHeight(), 377 getImageFormat(), 378 imageQueueDepth)); 379 } 380 381 ImageAnalysisAbstractAnalyzer imageAnalysisAbstractAnalyzer; 382 synchronized (mAnalysisLock) { 383 recreateImageAnalysisAbstractAnalyzer(); 384 imageAnalysisAbstractAnalyzer = mImageAnalysisAbstractAnalyzer; 385 } 386 387 boolean flipWH = getCamera() != null ? isFlipWH(getCamera()) : false; 388 int width = flipWH ? resolution.getHeight() : resolution.getWidth(); 389 int height = flipWH ? resolution.getWidth() : resolution.getHeight(); 390 int format = getOutputImageFormat() == OUTPUT_IMAGE_FORMAT_RGBA_8888 391 ? PixelFormat.RGBA_8888 : ImageFormat.YUV_420_888; 392 393 boolean isYuv2Rgb = getImageFormat() == ImageFormat.YUV_420_888 394 && getOutputImageFormat() == OUTPUT_IMAGE_FORMAT_RGBA_8888; 395 boolean isYuv2Nv21 = getImageFormat() == ImageFormat.YUV_420_888 396 && getOutputImageFormat() == OUTPUT_IMAGE_FORMAT_NV21; 397 boolean isYuvRotationOrPixelShift = getImageFormat() == ImageFormat.YUV_420_888 398 && ((getCamera() != null && getRelativeRotation(getCamera()) != 0) 399 || Boolean.TRUE.equals(getOnePixelShiftEnabled())); 400 401 // TODO(b/195021586): to support RGB format input for image analysis for devices already 402 // supporting RGB natively. The logic here will check if the specific configured size is 403 // available in RGB and if not, fall back to YUV-RGB conversion. 404 final SafeCloseImageReaderProxy processedImageReaderProxy = 405 (isYuv2Rgb || (isYuvRotationOrPixelShift && !isYuv2Nv21)) 406 ? new SafeCloseImageReaderProxy( 407 ImageReaderProxys.createIsolatedReader( 408 width, 409 height, 410 format, 411 imageReaderProxy.getMaxImages())) : null; 412 if (processedImageReaderProxy != null) { 413 imageAnalysisAbstractAnalyzer.setProcessedImageReaderProxy(processedImageReaderProxy); 414 } 415 416 tryUpdateRelativeRotation(); 417 418 imageReaderProxy.setOnImageAvailableListener(imageAnalysisAbstractAnalyzer, 419 backgroundExecutor); 420 421 SessionConfig.Builder sessionConfigBuilder = SessionConfig.Builder.createFrom(config, 422 streamSpec.getResolution()); 423 if (streamSpec.getImplementationOptions() != null) { 424 sessionConfigBuilder.addImplementationOptions(streamSpec.getImplementationOptions()); 425 } 426 427 if (mDeferrableSurface != null) { 428 mDeferrableSurface.close(); 429 } 430 mDeferrableSurface = new ImmediateSurface(imageReaderProxy.getSurface(), resolution, 431 getImageFormat()); 432 mDeferrableSurface.getTerminationFuture().addListener( 433 () -> { 434 imageReaderProxy.safeClose(); 435 if (processedImageReaderProxy != null) { 436 processedImageReaderProxy.safeClose(); 437 } 438 }, 439 CameraXExecutors.mainThreadExecutor()); 440 441 sessionConfigBuilder.setSessionType(streamSpec.getSessionType()); 442 // Applies the AE fps range to the session config builder according to the stream spec and 443 // quirk values. 444 applyExpectedFrameRateRange(sessionConfigBuilder, streamSpec); 445 446 sessionConfigBuilder.addSurface(mDeferrableSurface, 447 streamSpec.getDynamicRange(), 448 null, 449 MirrorMode.MIRROR_MODE_UNSPECIFIED); 450 451 if (mCloseableErrorListener != null) { 452 mCloseableErrorListener.close(); 453 } 454 mCloseableErrorListener = new SessionConfig.CloseableErrorListener( 455 (sessionConfig, error) -> { 456 // Do nothing when the use case has been unbound. 457 if (getCamera() == null) { 458 return; 459 } 460 461 clearPipeline(); 462 // Clear cache so app won't get a outdated image. 463 imageAnalysisAbstractAnalyzer.clearCache(); 464 // Only reset the pipeline when the bound camera is the same. 465 mSessionConfigBuilder = createPipeline(getCameraId(), 466 (ImageAnalysisConfig) getCurrentConfig(), 467 Preconditions.checkNotNull(getAttachedStreamSpec())); 468 updateSessionConfig(List.of(mSessionConfigBuilder.build())); 469 notifyReset(); 470 }); 471 472 sessionConfigBuilder.setErrorListener(mCloseableErrorListener); 473 474 return sessionConfigBuilder; 475 } 476 recreateImageAnalysisAbstractAnalyzer()477 private void recreateImageAnalysisAbstractAnalyzer() { 478 synchronized (mAnalysisLock) { 479 ImageAnalysisConfig config = (ImageAnalysisConfig) getCurrentConfig(); 480 481 if (config.getBackpressureStrategy(DEFAULT_BACKPRESSURE_STRATEGY) 482 == STRATEGY_BLOCK_PRODUCER) { 483 mImageAnalysisAbstractAnalyzer = new ImageAnalysisBlockingAnalyzer(); 484 } else { 485 mImageAnalysisAbstractAnalyzer = new ImageAnalysisNonBlockingAnalyzer( 486 config.getBackgroundExecutor(CameraXExecutors.highPriorityExecutor())); 487 } 488 mImageAnalysisAbstractAnalyzer.setOutputImageFormat(getOutputImageFormat()); 489 mImageAnalysisAbstractAnalyzer.setOutputImageRotationEnabled( 490 isOutputImageRotationEnabled()); 491 492 CameraInternal cameraInternal = getCamera(); 493 494 // Flag to enable or disable one pixel shift. It will override the flag set by device 495 // info. 496 // If enabled, the workaround will be applied for all devices. 497 // If disabled, the workaround will be disabled for all devices. 498 // If not configured, the workaround will be applied to the problem devices only. 499 Boolean isOnePixelShiftEnabled = getOnePixelShiftEnabled(); 500 boolean isOnePixelShiftIssueDevice = false; 501 if (cameraInternal != null) { 502 isOnePixelShiftIssueDevice = 503 cameraInternal.getCameraInfoInternal().getCameraQuirks().contains( 504 OnePixelShiftQuirk.class); 505 } 506 mImageAnalysisAbstractAnalyzer.setOnePixelShiftEnabled( 507 isOnePixelShiftEnabled == null ? isOnePixelShiftIssueDevice 508 : isOnePixelShiftEnabled); 509 510 // Sets relative rotation 511 if (cameraInternal != null) { 512 mImageAnalysisAbstractAnalyzer.setRelativeRotation( 513 getRelativeRotation(cameraInternal)); 514 } 515 516 // Sets view port crop rect 517 if (mViewPortCropRect != null) { 518 mImageAnalysisAbstractAnalyzer.setViewPortCropRect(mViewPortCropRect); 519 } 520 521 // Sets sensor to buffer transform matrix 522 if (mSensorToBufferTransformMatrix != null) { 523 mImageAnalysisAbstractAnalyzer.setSensorToBufferTransformMatrix( 524 mSensorToBufferTransformMatrix); 525 } 526 527 if (mSubscribedAnalyzerExecutor != null && mSubscribedAnalyzer != null) { 528 mImageAnalysisAbstractAnalyzer.setAnalyzer(mSubscribedAnalyzerExecutor, 529 mSubscribedAnalyzer); 530 } 531 } 532 } 533 534 /** 535 * Clear the internal pipeline so that the pipeline can be set up again. 536 */ 537 @SuppressWarnings("WeakerAccess") /* synthetic accessor */ clearPipeline()538 void clearPipeline() { 539 Threads.checkMainThread(); 540 541 // Closes the old error listener 542 if (mCloseableErrorListener != null) { 543 mCloseableErrorListener.close(); 544 mCloseableErrorListener = null; 545 } 546 547 if (mDeferrableSurface != null) { 548 mDeferrableSurface.close(); 549 mDeferrableSurface = null; 550 } 551 } 552 553 /** 554 * Removes a previously set analyzer. 555 * 556 * <p>This will stop data from streaming to the {@link ImageAnalysis}. 557 */ clearAnalyzer()558 public void clearAnalyzer() { 559 synchronized (mAnalysisLock) { 560 if (mImageAnalysisAbstractAnalyzer != null) { 561 mImageAnalysisAbstractAnalyzer.setAnalyzer(null, null); 562 } 563 if (mSubscribedAnalyzer != null) { 564 notifyInactive(); 565 } 566 mSubscribedAnalyzerExecutor = null; 567 mSubscribedAnalyzer = null; 568 } 569 } 570 571 /** 572 * Returns the rotation of the intended target for images. 573 * 574 * <p> 575 * The rotation can be set when constructing an {@link ImageAnalysis} instance using 576 * {@link ImageAnalysis.Builder#setTargetRotation(int)}, or dynamically by calling 577 * {@link ImageAnalysis#setTargetRotation(int)}. If not set, the target rotation 578 * defaults to the value of {@link Display#getRotation()} of the default display at the time 579 * the use case is created. The use case is fully created once it has been attached to a camera. 580 * </p> 581 * 582 * @return The rotation of the intended target for images. 583 * @see ImageAnalysis#setTargetRotation(int) 584 */ 585 @RotationValue getTargetRotation()586 public int getTargetRotation() { 587 return getTargetRotationInternal(); 588 } 589 590 /** 591 * Sets the target rotation. 592 * 593 * <p>This adjust the {@link ImageInfo#getRotationDegrees()} of the {@link ImageProxy} passed 594 * to {@link Analyzer#analyze(ImageProxy)}. The rotation value of ImageInfo will be the 595 * rotation, which if applied to the output image, will make the image match target rotation 596 * specified here. 597 * 598 * <p>While rotation can also be set via {@link Builder#setTargetRotation(int)}, using 599 * {@link ImageAnalysis#setTargetRotation(int)} allows the target rotation to be set 600 * dynamically. 601 * 602 * <p>In general, it is best to use an {@link android.view.OrientationEventListener} to 603 * set the target rotation. This way, the rotation output to the Analyzer will indicate 604 * which way is down for a given image. This is important since display orientation may be 605 * locked by device default, user setting, or app configuration, and some devices may not 606 * transition to a reverse-portrait display orientation. In these cases, set target rotation 607 * dynamically according to the {@link android.view.OrientationEventListener}, without 608 * re-creating the use case. {@link UseCase#snapToSurfaceRotation(int)} is a helper function to 609 * convert the orientation of the {@link android.view.OrientationEventListener} to a rotation 610 * value. See {@link UseCase#snapToSurfaceRotation(int)} for more information and sample code. 611 * 612 * <p>When this function is called, value set by 613 * {@link ImageAnalysis.Builder#setTargetResolution(Size)} will be updated automatically to 614 * make sure the suitable resolution can be selected when the use case is bound. 615 * 616 * <p>If not set here or by configuration, the target rotation will default to the value of 617 * {@link Display#getRotation()} of the default display at the time the use case is bound. To 618 * return to the default value, set the value to 619 * <pre>{@code 620 * context.getSystemService(WindowManager.class).getDefaultDisplay().getRotation(); 621 * }</pre> 622 * 623 * @param rotation Target rotation of the output image, expressed as one of 624 * {@link Surface#ROTATION_0}, {@link Surface#ROTATION_90}, 625 * {@link Surface#ROTATION_180}, or {@link Surface#ROTATION_270}. 626 */ setTargetRotation(@otationValue int rotation)627 public void setTargetRotation(@RotationValue int rotation) { 628 if (setTargetRotationInternal(rotation)) { 629 tryUpdateRelativeRotation(); 630 } 631 } 632 633 /** 634 * Sets an analyzer to receive and analyze images. 635 * 636 * <p>Setting an analyzer will signal to the camera that it should begin sending data. The 637 * stream of data can be stopped by calling {@link #clearAnalyzer()}. 638 * 639 * <p>Applications can process or copy the image by implementing the {@link Analyzer}. If 640 * frames should be skipped (no analysis), the analyzer function should return, instead of 641 * disconnecting the analyzer function completely. 642 * 643 * <p>Setting an analyzer function replaces any previous analyzer. Only one analyzer can be 644 * set at any time. 645 * 646 * @param executor The executor in which the 647 * {@link ImageAnalysis.Analyzer#analyze(ImageProxy)} will be run. 648 * @param analyzer of the images. 649 */ setAnalyzer(@onNull Executor executor, @NonNull Analyzer analyzer)650 public void setAnalyzer(@NonNull Executor executor, @NonNull Analyzer analyzer) { 651 synchronized (mAnalysisLock) { 652 if (mImageAnalysisAbstractAnalyzer != null) { 653 mImageAnalysisAbstractAnalyzer.setAnalyzer(executor, 654 image -> analyzer.analyze(image)); 655 } 656 if (mSubscribedAnalyzer == null) { 657 notifyActive(); 658 } 659 mSubscribedAnalyzerExecutor = executor; 660 mSubscribedAnalyzer = analyzer; 661 } 662 } 663 664 /** 665 * {@inheritDoc} 666 */ 667 @RestrictTo(Scope.LIBRARY_GROUP) 668 @Override setViewPortCropRect(@onNull Rect viewPortCropRect)669 public void setViewPortCropRect(@NonNull Rect viewPortCropRect) { 670 super.setViewPortCropRect(viewPortCropRect); 671 synchronized (mAnalysisLock) { 672 if (mImageAnalysisAbstractAnalyzer != null) { 673 mImageAnalysisAbstractAnalyzer.setViewPortCropRect(viewPortCropRect); 674 } 675 mViewPortCropRect = viewPortCropRect; 676 } 677 } 678 679 /** 680 * {@inheritDoc} 681 */ 682 @RestrictTo(Scope.LIBRARY_GROUP) 683 @Override setSensorToBufferTransformMatrix(@onNull Matrix matrix)684 public void setSensorToBufferTransformMatrix(@NonNull Matrix matrix) { 685 super.setSensorToBufferTransformMatrix(matrix); 686 synchronized (mAnalysisLock) { 687 if (mImageAnalysisAbstractAnalyzer != null) { 688 mImageAnalysisAbstractAnalyzer.setSensorToBufferTransformMatrix(matrix); 689 } 690 mSensorToBufferTransformMatrix = matrix; 691 } 692 } 693 isFlipWH(@onNull CameraInternal cameraInternal)694 private boolean isFlipWH(@NonNull CameraInternal cameraInternal) { 695 return isOutputImageRotationEnabled() 696 ? ((getRelativeRotation(cameraInternal) % 180) != 0) : false; 697 } 698 699 /** 700 * Returns the mode with which images are acquired from the {@linkplain ImageReader image 701 * producer}. 702 * 703 * <p> 704 * The backpressure strategy is set when constructing an {@link ImageAnalysis} instance using 705 * {@link ImageAnalysis.Builder#setBackpressureStrategy(int)}. If not set, it defaults to 706 * {@link ImageAnalysis#STRATEGY_KEEP_ONLY_LATEST}. 707 * </p> 708 * 709 * @return The backpressure strategy applied to the image producer. 710 * @see ImageAnalysis.Builder#setBackpressureStrategy(int) 711 */ 712 @BackpressureStrategy getBackpressureStrategy()713 public int getBackpressureStrategy() { 714 return ((ImageAnalysisConfig) getCurrentConfig()).getBackpressureStrategy( 715 DEFAULT_BACKPRESSURE_STRATEGY); 716 } 717 718 /** 719 * Returns the executor that will be used for background tasks. 720 * 721 * @return The {@link Executor} provided to 722 * {@link ImageAnalysis.Builder#setBackgroundExecutor(Executor)}. 723 * If no Executor has been provided, then returns {@code null} 724 */ 725 @ExperimentalUseCaseApi getBackgroundExecutor()726 public @Nullable Executor getBackgroundExecutor() { 727 return ((ImageAnalysisConfig) getCurrentConfig()) 728 .getBackgroundExecutor(null); 729 } 730 731 /** 732 * Returns the number of images available to the camera pipeline, including the image being 733 * analyzed, for the {@link #STRATEGY_BLOCK_PRODUCER} backpressure mode. 734 * 735 * <p> 736 * The image queue depth is set when constructing an {@link ImageAnalysis} instance using 737 * {@link ImageAnalysis.Builder#setImageQueueDepth(int)}. If not set, and this option is used 738 * by the backpressure strategy, the default will be a queue depth of 6 images. 739 * </p> 740 * 741 * @return The image queue depth for the {@link #STRATEGY_BLOCK_PRODUCER} backpressure mode. 742 * @see ImageAnalysis.Builder#setImageQueueDepth(int) 743 * @see ImageAnalysis.Builder#setBackpressureStrategy(int) 744 */ getImageQueueDepth()745 public int getImageQueueDepth() { 746 return ((ImageAnalysisConfig) getCurrentConfig()).getImageQueueDepth( 747 DEFAULT_IMAGE_QUEUE_DEPTH); 748 } 749 750 /** 751 * Gets output image format. 752 * 753 * <p>The returned image format will be 754 * {@link ImageAnalysis#OUTPUT_IMAGE_FORMAT_YUV_420_888}, 755 * {@link ImageAnalysis#OUTPUT_IMAGE_FORMAT_RGBA_8888} or 756 * {@link ImageAnalysis#OUTPUT_IMAGE_FORMAT_NV21}. 757 * 758 * @return output image format. 759 * @see ImageAnalysis.Builder#setOutputImageFormat(int) 760 */ 761 @ImageAnalysis.OutputImageFormat getOutputImageFormat()762 public int getOutputImageFormat() { 763 return ((ImageAnalysisConfig) getCurrentConfig()).getOutputImageFormat( 764 DEFAULT_OUTPUT_IMAGE_FORMAT); 765 } 766 767 /** 768 * Checks if output image rotation is enabled. It returns false by default. 769 * 770 * @return true if enabled, false otherwise. 771 * @see ImageAnalysis.Builder#setOutputImageRotationEnabled(boolean) 772 */ isOutputImageRotationEnabled()773 public boolean isOutputImageRotationEnabled() { 774 return ((ImageAnalysisConfig) getCurrentConfig()).isOutputImageRotationEnabled( 775 DEFAULT_OUTPUT_IMAGE_ROTATION_ENABLED); 776 } 777 778 /** 779 * 780 */ 781 @RestrictTo(Scope.LIBRARY_GROUP) getOnePixelShiftEnabled()782 public @Nullable Boolean getOnePixelShiftEnabled() { 783 return ((ImageAnalysisConfig) getCurrentConfig()).getOnePixelShiftEnabled( 784 DEFAULT_ONE_PIXEL_SHIFT_ENABLED); 785 } 786 787 /** 788 * Gets resolution related information of the {@link ImageAnalysis}. 789 * 790 * <p>The returned {@link ResolutionInfo} will be expressed in the coordinates of the camera 791 * sensor. It will be the same as the resolution of the {@link ImageProxy} received from 792 * {@link ImageAnalysis.Analyzer#analyze}. 793 * 794 * <p>The resolution information might change if the use case is unbound and then rebound or 795 * {@link #setTargetRotation(int)} is called to change the target rotation setting. The 796 * application needs to call {@link #getResolutionInfo()} again to get the latest 797 * {@link ResolutionInfo} for the changes. 798 * 799 * @return the resolution information if the use case has been bound by the 800 * {@link androidx.camera.lifecycle.ProcessCameraProvider#bindToLifecycle(LifecycleOwner, 801 * CameraSelector, UseCase...)} API, or null if the use case is not bound yet. 802 */ getResolutionInfo()803 public @Nullable ResolutionInfo getResolutionInfo() { 804 return getResolutionInfoInternal(); 805 } 806 807 /** 808 * Returns the resolution selector setting. 809 * 810 * <p>This setting is set when constructing an ImageAnalysis using 811 * {@link Builder#setResolutionSelector(ResolutionSelector)}. 812 */ getResolutionSelector()813 public @Nullable ResolutionSelector getResolutionSelector() { 814 return ((ImageOutputConfig) getCurrentConfig()).getResolutionSelector(null); 815 } 816 817 @Override toString()818 public @NonNull String toString() { 819 return TAG + ":" + getName(); 820 } 821 822 /** 823 * {@inheritDoc} 824 */ 825 @RestrictTo(Scope.LIBRARY_GROUP) 826 @Override onUnbind()827 public void onUnbind() { 828 clearPipeline(); 829 synchronized (mAnalysisLock) { 830 mImageAnalysisAbstractAnalyzer.detach(); 831 mImageAnalysisAbstractAnalyzer = null; 832 } 833 } 834 835 /** 836 * {@inheritDoc} 837 */ 838 @RestrictTo(Scope.LIBRARY_GROUP) 839 @Override getDefaultConfig(boolean applyDefaultConfig, @NonNull UseCaseConfigFactory factory)840 public @Nullable UseCaseConfig<?> getDefaultConfig(boolean applyDefaultConfig, 841 @NonNull UseCaseConfigFactory factory) { 842 Config captureConfig = factory.getConfig( 843 DEFAULT_CONFIG.getConfig().getCaptureType(), 844 ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY); 845 846 if (applyDefaultConfig) { 847 captureConfig = Config.mergeConfigs(captureConfig, DEFAULT_CONFIG.getConfig()); 848 } 849 850 return captureConfig == null ? null : 851 getUseCaseConfigBuilder(captureConfig).getUseCaseConfig(); 852 } 853 854 /** 855 * {@inheritDoc} 856 */ 857 @RestrictTo(Scope.LIBRARY_GROUP) 858 @Override getUseCaseConfigBuilder(@onNull Config config)859 public UseCaseConfig.@NonNull Builder<?, ?, ?> getUseCaseConfigBuilder(@NonNull Config config) { 860 return ImageAnalysis.Builder.fromConfig(config); 861 } 862 863 /** 864 * {@inheritDoc} 865 */ 866 @Override 867 @RestrictTo(Scope.LIBRARY_GROUP) onSuggestedStreamSpecUpdated( @onNull StreamSpec primaryStreamSpec, @Nullable StreamSpec secondaryStreamSpec)868 protected @NonNull StreamSpec onSuggestedStreamSpecUpdated( 869 @NonNull StreamSpec primaryStreamSpec, 870 @Nullable StreamSpec secondaryStreamSpec) { 871 final ImageAnalysisConfig config = (ImageAnalysisConfig) getCurrentConfig(); 872 873 mSessionConfigBuilder = createPipeline(getCameraId(), config, 874 primaryStreamSpec); 875 updateSessionConfig(List.of(mSessionConfigBuilder.build())); 876 877 return primaryStreamSpec; 878 } 879 880 /** 881 * {@inheritDoc} 882 */ 883 @Override 884 @RestrictTo(Scope.LIBRARY_GROUP) onSuggestedStreamSpecImplementationOptionsUpdated( @onNull Config config)885 protected @NonNull StreamSpec onSuggestedStreamSpecImplementationOptionsUpdated( 886 @NonNull Config config) { 887 mSessionConfigBuilder.addImplementationOptions(config); 888 updateSessionConfig(List.of(mSessionConfigBuilder.build())); 889 return getAttachedStreamSpec().toBuilder().setImplementationOptions(config).build(); 890 } 891 892 /** 893 * Updates relative rotation if attached to a camera. No-op otherwise. 894 */ tryUpdateRelativeRotation()895 private void tryUpdateRelativeRotation() { 896 synchronized (mAnalysisLock) { 897 CameraInternal cameraInternal = getCamera(); 898 if (cameraInternal != null) { 899 mImageAnalysisAbstractAnalyzer.setRelativeRotation( 900 getRelativeRotation(cameraInternal)); 901 } 902 } 903 } 904 905 /** 906 * How to apply backpressure to the source producing images for analysis. 907 * 908 * <p>Sometimes, images may be produced faster than they can be analyzed. Since images 909 * generally reserve a large portion of the device's memory, they cannot be buffered 910 * unbounded and indefinitely. The backpressure strategy defines how to deal with this scenario. 911 * 912 * <p>The receiver of the {@link ImageProxy} is responsible for explicitly closing the image 913 * by calling {@link ImageProxy#close()}. However, the image will only be valid when the 914 * ImageAnalysis instance is bound to a camera. 915 * 916 * @see Builder#setBackpressureStrategy(int) 917 */ 918 @IntDef({STRATEGY_KEEP_ONLY_LATEST, STRATEGY_BLOCK_PRODUCER}) 919 @Retention(RetentionPolicy.SOURCE) 920 @RestrictTo(Scope.LIBRARY_GROUP) 921 public @interface BackpressureStrategy { 922 } 923 924 /** 925 * Supported output image format for image analysis. 926 * 927 * <p>The supported output image format 928 * is {@link ImageAnalysis#OUTPUT_IMAGE_FORMAT_YUV_420_888}, 929 * {@link ImageAnalysis#OUTPUT_IMAGE_FORMAT_RGBA_8888} and 930 * {@link ImageAnalysis#OUTPUT_IMAGE_FORMAT_NV21}. 931 * 932 * <p>By default, {@link ImageAnalysis#OUTPUT_IMAGE_FORMAT_YUV_420_888} will be used. 933 * 934 * @see Builder#setOutputImageFormat(int) 935 */ 936 @IntDef({OUTPUT_IMAGE_FORMAT_YUV_420_888, OUTPUT_IMAGE_FORMAT_RGBA_8888, 937 OUTPUT_IMAGE_FORMAT_NV21}) 938 @Retention(RetentionPolicy.SOURCE) 939 @RestrictTo(Scope.LIBRARY_GROUP) 940 public @interface OutputImageFormat { 941 } 942 943 /** 944 * Interface for analyzing images. 945 * 946 * <p>Implement Analyzer and pass it to {@link ImageAnalysis#setAnalyzer(Executor, Analyzer)} 947 * to receive images and perform custom processing by implementing the 948 * {@link ImageAnalysis.Analyzer#analyze(ImageProxy)} function. 949 */ 950 public interface Analyzer { 951 /** 952 * Analyzes an image to produce a result. 953 * 954 * <p>This method is called once for each image from the camera, and called at the 955 * frame rate of the camera. Each analyze call is executed sequentially. 956 * 957 * <p>It is the responsibility of the application to close the image once done with it. 958 * If the images are not closed then it may block further images from being produced 959 * (causing the preview to stall) or drop images as determined by the configured 960 * backpressure strategy. The exact behavior is configurable via 961 * {@link ImageAnalysis.Builder#setBackpressureStrategy(int)}. 962 * 963 * <p>Images produced here will no longer be valid after the {@link ImageAnalysis} 964 * instance that produced it has been unbound from the camera. 965 * 966 * <p>The image provided has format {@link android.graphics.ImageFormat#YUV_420_888}. 967 * 968 * <p>The provided image is typically in the orientation of the sensor, meaning CameraX 969 * does not perform an internal rotation of the data. The rotationDegrees parameter allows 970 * the analysis to understand the image orientation when processing or to apply a rotation. 971 * For example, if the 972 * {@linkplain ImageAnalysis#setTargetRotation(int) target rotation}) is natural 973 * orientation, rotationDegrees would be the rotation which would align the buffer 974 * data ordering to natural orientation. 975 * 976 * <p>Timestamps are in nanoseconds and monotonic and can be compared to timestamps from 977 * images produced from UseCases bound to the same camera instance. More detail is 978 * available depending on the implementation. For example with CameraX using a 979 * {@link androidx.camera.camera2} implementation additional detail can be found in 980 * {@link android.hardware.camera2.CameraDevice} documentation. 981 * 982 * @param image The image to analyze 983 * @see android.media.Image#getTimestamp() 984 * @see android.hardware.camera2.CaptureResult#SENSOR_TIMESTAMP 985 */ analyze(@onNull ImageProxy image)986 void analyze(@NonNull ImageProxy image); 987 988 /** 989 * Implement this method to set a default target resolution for the {@link ImageAnalysis}. 990 * 991 * <p> Implement this method if the {@link Analyzer} requires a specific resolution to 992 * work. The return value will be used as the default target resolution for the 993 * {@link ImageAnalysis}. Return {@code null} if no falling back is needed. By default, 994 * this method returns {@code null}. 995 * 996 * <p> If the app does not set a target resolution for {@link ImageAnalysis}, then this 997 * value will be used as the target resolution. If the {@link ImageAnalysis} has set a 998 * target resolution, e.g. if {@link ImageAnalysis.Builder#setTargetResolution(Size)} is 999 * called, then the {@link ImageAnalysis} will use the app value over this value. 1000 * 1001 * <p> Note that this method is invoked by CameraX at the time of binding to lifecycle. In 1002 * order for this value to be effective, the {@link Analyzer} has to be set before 1003 * {@link ImageAnalysis} is bound to a lifecycle. Otherwise, the value will be ignored. 1004 * 1005 * @return the default resolution of {@link ImageAnalysis}, or {@code null} if no specific 1006 * resolution is needed. 1007 */ getDefaultTargetResolution()1008 default @Nullable Size getDefaultTargetResolution() { 1009 return null; 1010 } 1011 1012 /** 1013 * Implement this method to return the target coordinate system. 1014 * 1015 * <p>The coordinates detected by analyzing camera frame usually needs to be transformed. 1016 * For example, in order to highlight a detected face, the app needs to transform the 1017 * bounding box from the {@link ImageAnalysis}'s coordinate system to the View's coordinate 1018 * system. This method allows the implementer to set a target coordinate system. 1019 * 1020 * <p>The value will be used by CameraX to calculate the transformation {@link Matrix} and 1021 * forward it to the {@link Analyzer} via {@link #updateTransform}. By default, this 1022 * method returns {@link ImageAnalysis#COORDINATE_SYSTEM_ORIGINAL}. 1023 * 1024 * <p>For now, camera-core only supports {@link ImageAnalysis#COORDINATE_SYSTEM_ORIGINAL}, 1025 * please see libraries derived from camera-core, for example, camera-view. 1026 * 1027 * @see #updateTransform(Matrix) 1028 */ getTargetCoordinateSystem()1029 default int getTargetCoordinateSystem() { 1030 return COORDINATE_SYSTEM_ORIGINAL; 1031 } 1032 1033 /** 1034 * Implement this method to receive the {@link Matrix} for coordinate transformation. 1035 * 1036 * <p>The value represents the transformation from the camera sensor to the target 1037 * coordinate system defined in {@link #getTargetCoordinateSystem()}. It should be used 1038 * by the implementation to transform the coordinates detected in the camera frame. For 1039 * example, the coordinates of the detected face. 1040 * 1041 * <p>If the value is {@code null}, it means that no valid transformation is available. 1042 * It could have different causes depending on the value of 1043 * {@link #getTargetCoordinateSystem()}: 1044 * <ul> 1045 * <li> If the target coordinate system is {@link #COORDINATE_SYSTEM_ORIGINAL}, it is 1046 * always invalid because in that case, the coordinate system depends on how the 1047 * analysis algorithm processes the {@link ImageProxy}. 1048 * <li> It is also invalid if the target coordinate system is not available, for example 1049 * if the analyzer targets the viewfinder and the view finder is not visible in UI. 1050 * </ul> 1051 * 1052 * <p>This method is invoked whenever a new transformation is ready. For example, when 1053 * the view finder is first a launched as well as when it's resized. 1054 * 1055 * @see #getTargetCoordinateSystem() 1056 */ updateTransform(@ullable Matrix matrix)1057 default void updateTransform(@Nullable Matrix matrix) { 1058 // no-op 1059 } 1060 } 1061 1062 /** 1063 * {@link ImageAnalysis.Analyzer} option for returning the original coordinates. 1064 * 1065 * <p>Use this option if no additional transformation is needed by the {@link Analyzer} 1066 * implementation. The coordinates returned by the {@link Analyzer} should be within (0, 0) - 1067 * (width, height) where width and height are the dimensions of the {@link ImageProxy}. 1068 * 1069 * <p>By using this option, CameraX will pass {@code null} to 1070 * {@link Analyzer#updateTransform(Matrix)}. 1071 */ 1072 public static final int COORDINATE_SYSTEM_ORIGINAL = 0; 1073 1074 /** 1075 * {@link ImageAnalysis.Analyzer} option for returning UI coordinates. 1076 * 1077 * <p>When the {@link ImageAnalysis.Analyzer} is configured with this option, it will receive a 1078 * {@link Matrix} that will receive a value that represents the transformation from camera 1079 * sensor to the {@link View}, which can be used for highlighting detected result in UI. For 1080 * example, laying over a bounding box on top of the detected face. 1081 * 1082 * <p>Note this option will only work with an artifact that displays the camera feed in UI. 1083 * Generally, this is used by higher-level libraries such as the CameraController API that 1084 * incorporates a viewfinder UI. It will not be effective when used with camera-core directly. 1085 * 1086 * @see ImageAnalysis.Analyzer 1087 */ 1088 public static final int COORDINATE_SYSTEM_VIEW_REFERENCED = 1; 1089 1090 /** 1091 * {@link ImageAnalysis.Analyzer} option for returning the sensor coordinates. 1092 * 1093 * <p>Use this option if the app wishes to get the detected objects in camera sensor 1094 * coordinates. The coordinates returned by the {@link Analyzer} should be within (left, 1095 * right) - (width, height), where the left, right, width and height are bounds of the camera 1096 * sensor's active array. 1097 * 1098 * <p>By using this option, CameraX will pass 1099 * {@link ImageInfo#getSensorToBufferTransformMatrix()}'s inverse to 1100 * {@link Analyzer#updateTransform}. 1101 */ 1102 public static final int COORDINATE_SYSTEM_SENSOR = 2; 1103 1104 /** 1105 * Provides a base static default configuration for the ImageAnalysis. 1106 * 1107 * <p>These values may be overridden by the implementation. They only provide a minimum set of 1108 * defaults that are implementation independent. 1109 */ 1110 @RestrictTo(Scope.LIBRARY_GROUP) 1111 public static final class Defaults implements ConfigProvider<ImageAnalysisConfig> { 1112 private static final Size DEFAULT_TARGET_RESOLUTION = new Size(640, 480); 1113 private static final int DEFAULT_SURFACE_OCCUPANCY_PRIORITY = 1; 1114 private static final int DEFAULT_ASPECT_RATIO = AspectRatio.RATIO_4_3; 1115 1116 /** 1117 * Explicitly setting the default dynamic range to SDR (rather than UNSPECIFIED) means 1118 * ImageAnalysis won't inherit dynamic ranges from other use cases. 1119 */ 1120 // TODO(b/258099919): ImageAnalysis currently can't support HDR, so we don't expose the 1121 // dynamic range setter and require SDR. We may want to get rid of this default once we 1122 // can support tone-mapping from HDR -> SDR 1123 private static final DynamicRange DEFAULT_DYNAMIC_RANGE = DynamicRange.SDR; 1124 1125 private static final ResolutionSelector DEFAULT_RESOLUTION_SELECTOR = 1126 new ResolutionSelector.Builder().setAspectRatioStrategy( 1127 AspectRatioStrategy.RATIO_4_3_FALLBACK_AUTO_STRATEGY) 1128 .setResolutionStrategy(new ResolutionStrategy(SizeUtil.RESOLUTION_VGA, 1129 ResolutionStrategy.FALLBACK_RULE_CLOSEST_HIGHER_THEN_LOWER)) 1130 .build(); 1131 1132 private static final ImageAnalysisConfig DEFAULT_CONFIG; 1133 1134 static { 1135 Builder builder = new Builder() 1136 .setDefaultResolution(DEFAULT_TARGET_RESOLUTION) 1137 .setSurfaceOccupancyPriority(DEFAULT_SURFACE_OCCUPANCY_PRIORITY) 1138 .setTargetAspectRatio(DEFAULT_ASPECT_RATIO) 1139 .setResolutionSelector(DEFAULT_RESOLUTION_SELECTOR) 1140 .setDynamicRange(DEFAULT_DYNAMIC_RANGE); 1141 1142 DEFAULT_CONFIG = builder.getUseCaseConfig(); 1143 } 1144 1145 @Override getConfig()1146 public @NonNull ImageAnalysisConfig getConfig() { 1147 return DEFAULT_CONFIG; 1148 } 1149 } 1150 1151 /** Builder for a {@link ImageAnalysis}. */ 1152 @SuppressWarnings({"ObjectToString", "HiddenSuperclass"}) 1153 public static final class Builder 1154 implements ImageOutputConfig.Builder<Builder>, 1155 ThreadConfig.Builder<Builder>, 1156 UseCaseConfig.Builder<ImageAnalysis, ImageAnalysisConfig, Builder>, 1157 ImageInputConfig.Builder<Builder> { 1158 1159 private final MutableOptionsBundle mMutableConfig; 1160 1161 /** Creates a new Builder object. */ Builder()1162 public Builder() { 1163 this(MutableOptionsBundle.create()); 1164 } 1165 Builder(MutableOptionsBundle mutableConfig)1166 private Builder(MutableOptionsBundle mutableConfig) { 1167 mMutableConfig = mutableConfig; 1168 1169 Class<?> oldConfigClass = 1170 mutableConfig.retrieveOption(TargetConfig.OPTION_TARGET_CLASS, null); 1171 if (oldConfigClass != null && !oldConfigClass.equals(ImageAnalysis.class)) { 1172 throw new IllegalArgumentException( 1173 "Invalid target class configuration for " 1174 + Builder.this 1175 + ": " 1176 + oldConfigClass); 1177 } 1178 1179 setCaptureType(UseCaseConfigFactory.CaptureType.IMAGE_ANALYSIS); 1180 setTargetClass(ImageAnalysis.class); 1181 } 1182 1183 /** 1184 * Generates a Builder from another Config object. 1185 * 1186 * @param configuration An immutable configuration to pre-populate this builder. 1187 * @return The new Builder. 1188 */ 1189 @RestrictTo(Scope.LIBRARY_GROUP) fromConfig(@onNull Config configuration)1190 static @NonNull Builder fromConfig(@NonNull Config configuration) { 1191 return new Builder(MutableOptionsBundle.from(configuration)); 1192 } 1193 1194 /** 1195 * Generates a Builder from another Config object. 1196 * 1197 * @param configuration An immutable configuration to pre-populate this builder. 1198 * @return The new Builder. 1199 */ 1200 @RestrictTo(Scope.LIBRARY_GROUP) fromConfig(@onNull ImageAnalysisConfig configuration)1201 public static @NonNull Builder fromConfig(@NonNull ImageAnalysisConfig configuration) { 1202 return new Builder(MutableOptionsBundle.from(configuration)); 1203 } 1204 1205 /** 1206 * Sets the backpressure strategy to apply to the image producer to deal with scenarios 1207 * where images may be produced faster than they can be analyzed. 1208 * 1209 * <p>The available values are {@link #STRATEGY_BLOCK_PRODUCER} and 1210 * {@link #STRATEGY_KEEP_ONLY_LATEST}. 1211 * 1212 * <p>If not set, the backpressure strategy will default to 1213 * {@link #STRATEGY_KEEP_ONLY_LATEST}. 1214 * 1215 * @param strategy The strategy to use. 1216 * @return The current Builder. 1217 */ setBackpressureStrategy(@ackpressureStrategy int strategy)1218 public @NonNull Builder setBackpressureStrategy(@BackpressureStrategy int strategy) { 1219 getMutableConfig().insertOption(OPTION_BACKPRESSURE_STRATEGY, strategy); 1220 return this; 1221 } 1222 1223 /** 1224 * Sets the number of images available to the camera pipeline for 1225 * {@link #STRATEGY_BLOCK_PRODUCER} mode. 1226 * 1227 * <p>The image queue depth is the number of images available to the camera to fill with 1228 * data. This includes the image currently being analyzed by {@link 1229 * ImageAnalysis.Analyzer#analyze(ImageProxy)}. Increasing the image queue depth 1230 * may make camera operation smoother, depending on the backpressure strategy, at 1231 * the cost of increased memory usage. 1232 * 1233 * <p>When the backpressure strategy is set to {@link #STRATEGY_BLOCK_PRODUCER}, 1234 * increasing the image queue depth may make the camera pipeline run smoother on systems 1235 * under high load. However, the time spent analyzing an image should still be kept under 1236 * a single frame period for the current frame rate, <i>on average</i>, to avoid stalling 1237 * the camera pipeline. 1238 * 1239 * <p>The value only applies to {@link #STRATEGY_BLOCK_PRODUCER} mode. 1240 * For {@link #STRATEGY_KEEP_ONLY_LATEST} the value is ignored. 1241 * 1242 * <p>If not set, and this option is used by the selected backpressure strategy, 1243 * the default will be a queue depth of 6 images. 1244 * 1245 * @param depth The total number of images available to the camera. 1246 * @return The current Builder. 1247 */ setImageQueueDepth(int depth)1248 public @NonNull Builder setImageQueueDepth(int depth) { 1249 getMutableConfig().insertOption(OPTION_IMAGE_QUEUE_DEPTH, depth); 1250 return this; 1251 } 1252 1253 /** 1254 * Sets output image format. 1255 * 1256 * <p>The supported output image format 1257 * is {@link OutputImageFormat#OUTPUT_IMAGE_FORMAT_YUV_420_888}, 1258 * {@link OutputImageFormat#OUTPUT_IMAGE_FORMAT_RGBA_8888} and 1259 * {@link OutputImageFormat#OUTPUT_IMAGE_FORMAT_NV21}. 1260 * 1261 * <p>If not set, {@link OutputImageFormat#OUTPUT_IMAGE_FORMAT_YUV_420_888} will be used. 1262 * 1263 * Requesting {@link OutputImageFormat#OUTPUT_IMAGE_FORMAT_RGBA_8888} or 1264 * {@link OutputImageFormat#OUTPUT_IMAGE_FORMAT_NV21} will have extra overhead because 1265 * format conversion takes time. 1266 * 1267 * @param outputImageFormat The output image format. 1268 * @return The current Builder. 1269 */ setOutputImageFormat(@utputImageFormat int outputImageFormat)1270 public @NonNull Builder setOutputImageFormat(@OutputImageFormat int outputImageFormat) { 1271 getMutableConfig().insertOption(OPTION_OUTPUT_IMAGE_FORMAT, outputImageFormat); 1272 return this; 1273 } 1274 1275 /** 1276 * Enable or disable output image rotation. 1277 * 1278 * <p>On API 22 and below, this API has no effect. User needs to handle the image rotation 1279 * based on the {@link ImageInfo#getRotationDegrees()}. 1280 * 1281 * <p>{@link ImageAnalysis#setTargetRotation(int)} is to adjust the rotation 1282 * degree information returned by {@link ImageInfo#getRotationDegrees()} based on 1283 * sensor rotation and user still needs to rotate the output image to achieve the target 1284 * rotation. Once this is enabled, user doesn't need to handle the rotation, the output 1285 * image will be a rotated {@link ImageProxy} and {@link ImageInfo#getRotationDegrees()} 1286 * will return 0. 1287 * 1288 * <p>Turning this on will add more processing overhead to every image analysis 1289 * frame. The average processing time is about 10-15ms for 640x480 image on a mid-range 1290 * device. 1291 * 1292 * By default, the rotation is disabled. 1293 * 1294 * @param outputImageRotationEnabled flag to enable or disable. 1295 * @return The current Builder. 1296 * @see 1297 * <a href="https://developer.android.com/training/camerax/orientation-rotation#imageanalysis">ImageAnalysis</a> 1298 */ 1299 @RequiresApi(23) setOutputImageRotationEnabled(boolean outputImageRotationEnabled)1300 public @NonNull Builder setOutputImageRotationEnabled(boolean outputImageRotationEnabled) { 1301 getMutableConfig().insertOption(OPTION_OUTPUT_IMAGE_ROTATION_ENABLED, 1302 outputImageRotationEnabled); 1303 return this; 1304 } 1305 1306 @RestrictTo(Scope.LIBRARY_GROUP) setOnePixelShiftEnabled(boolean onePixelShiftEnabled)1307 public @NonNull Builder setOnePixelShiftEnabled(boolean onePixelShiftEnabled) { 1308 getMutableConfig().insertOption(OPTION_ONE_PIXEL_SHIFT_ENABLED, 1309 Boolean.valueOf(onePixelShiftEnabled)); 1310 return this; 1311 } 1312 1313 /** 1314 * {@inheritDoc} 1315 */ 1316 @RestrictTo(Scope.LIBRARY_GROUP) 1317 @Override getMutableConfig()1318 public @NonNull MutableConfig getMutableConfig() { 1319 return mMutableConfig; 1320 } 1321 1322 /** 1323 * {@inheritDoc} 1324 */ 1325 @RestrictTo(Scope.LIBRARY_GROUP) 1326 @Override getUseCaseConfig()1327 public @NonNull ImageAnalysisConfig getUseCaseConfig() { 1328 return new ImageAnalysisConfig(OptionsBundle.from(mMutableConfig)); 1329 } 1330 1331 /** 1332 * Builds an {@link ImageAnalysis} from the current state. 1333 * 1334 * @return A {@link ImageAnalysis} populated with the current state. 1335 * @throws IllegalArgumentException if attempting to set both target aspect ratio and 1336 * target resolution. 1337 */ 1338 @Override build()1339 public @NonNull ImageAnalysis build() { 1340 ImageAnalysisConfig imageAnalysisConfig = getUseCaseConfig(); 1341 ImageOutputConfig.validateConfig(imageAnalysisConfig); 1342 return new ImageAnalysis(imageAnalysisConfig); 1343 } 1344 1345 // Implementations of TargetConfig.Builder default methods 1346 1347 @RestrictTo(Scope.LIBRARY_GROUP) 1348 @Override setTargetClass(@onNull Class<ImageAnalysis> targetClass)1349 public @NonNull Builder setTargetClass(@NonNull Class<ImageAnalysis> targetClass) { 1350 getMutableConfig().insertOption(OPTION_TARGET_CLASS, targetClass); 1351 1352 // If no name is set yet, then generate a unique name 1353 if (null == getMutableConfig().retrieveOption(OPTION_TARGET_NAME, null)) { 1354 String targetName = targetClass.getCanonicalName() + "-" + UUID.randomUUID(); 1355 setTargetName(targetName); 1356 } 1357 1358 return this; 1359 } 1360 1361 /** 1362 * Sets the name of the target object being configured, used only for debug logging. 1363 * 1364 * <p>The name should be a value that can uniquely identify an instance of the object being 1365 * configured. 1366 * 1367 * <p>If not set, the target name will default to a unique name automatically generated 1368 * with the class canonical name and random UUID. 1369 * 1370 * @param targetName A unique string identifier for the instance of the class being 1371 * configured. 1372 * @return the current Builder. 1373 */ 1374 @Override setTargetName(@onNull String targetName)1375 public @NonNull Builder setTargetName(@NonNull String targetName) { 1376 getMutableConfig().insertOption(OPTION_TARGET_NAME, targetName); 1377 return this; 1378 } 1379 1380 /** 1381 * Sets the aspect ratio of the intended target for images from this configuration. 1382 * 1383 * <p>The aspect ratio is the ratio of width to height in the sensor orientation. 1384 * 1385 * <p>It is not allowed to set both target aspect ratio and target resolution on the same 1386 * use case. Attempting so will throw an IllegalArgumentException when building the Config. 1387 * 1388 * <p>The target aspect ratio is used as a hint when determining the resulting output aspect 1389 * ratio which may differ from the request, possibly due to device constraints. 1390 * Application code should check the resulting output's resolution and the resulting 1391 * aspect ratio may not be exactly as requested. 1392 * 1393 * <p>If not set, or {@link AspectRatio#RATIO_DEFAULT} is supplied, resolutions with 1394 * aspect ratio 4:3 will be considered in higher priority. 1395 * 1396 * @param aspectRatio The desired ImageAnalysis {@link AspectRatio} 1397 * @return The current Builder. 1398 * @deprecated use {@link ResolutionSelector} with {@link AspectRatioStrategy} to specify 1399 * the preferred aspect ratio settings instead. 1400 */ 1401 @Override 1402 @Deprecated setTargetAspectRatio(@spectRatio.Ratio int aspectRatio)1403 public @NonNull Builder setTargetAspectRatio(@AspectRatio.Ratio int aspectRatio) { 1404 if (aspectRatio == AspectRatio.RATIO_DEFAULT) { 1405 aspectRatio = Defaults.DEFAULT_ASPECT_RATIO; 1406 } 1407 getMutableConfig().insertOption(OPTION_TARGET_ASPECT_RATIO, aspectRatio); 1408 return this; 1409 } 1410 1411 /** 1412 * Sets the rotation of the intended target for images from this configuration. 1413 * 1414 * <p>This adjust the {@link ImageInfo#getRotationDegrees()} of the {@link ImageProxy} 1415 * passed to {@link Analyzer#analyze(ImageProxy)}. The rotation value of ImageInfo will 1416 * be the rotation, which if applied to the output image, will make the image match 1417 * target rotation specified here. 1418 * 1419 * <p>This is one of four valid values: {@link Surface#ROTATION_0}, {@link 1420 * Surface#ROTATION_90}, {@link Surface#ROTATION_180}, {@link Surface#ROTATION_270}. 1421 * Rotation values are relative to the "natural" rotation, {@link Surface#ROTATION_0}. 1422 * 1423 * <p>In general, it is best to additionally set the target rotation dynamically on the use 1424 * case. See {@link androidx.camera.core.ImageAnalysis#setTargetRotation(int)} for 1425 * additional documentation. 1426 * 1427 * <p>If not set, the target rotation will default to the value of 1428 * {@link android.view.Display#getRotation()} of the default display at the time the 1429 * use case is created. The use case is fully created once it has been attached to a camera. 1430 * 1431 * @param rotation The rotation of the intended target. 1432 * @return The current Builder. 1433 * @see androidx.camera.core.ImageAnalysis#setTargetRotation(int) 1434 * @see android.view.OrientationEventListener 1435 */ 1436 @Override setTargetRotation(@otationValue int rotation)1437 public @NonNull Builder setTargetRotation(@RotationValue int rotation) { 1438 getMutableConfig().insertOption(OPTION_TARGET_ROTATION, rotation); 1439 return this; 1440 } 1441 1442 /** 1443 * setMirrorMode is not supported on ImageAnalysis. 1444 */ 1445 @RestrictTo(Scope.LIBRARY_GROUP) 1446 @Override setMirrorMode(@irrorMode.Mirror int mirrorMode)1447 public @NonNull Builder setMirrorMode(@MirrorMode.Mirror int mirrorMode) { 1448 throw new UnsupportedOperationException("setMirrorMode is not supported."); 1449 } 1450 1451 /** 1452 * Sets the resolution of the intended target from this configuration. 1453 * 1454 * <p>The target resolution attempts to establish a minimum bound for the image resolution. 1455 * The actual image resolution will be the closest available resolution in size that is not 1456 * smaller than the target resolution, as determined by the Camera implementation. However, 1457 * if no resolution exists that is equal to or larger than the target resolution, the 1458 * nearest available resolution smaller than the target resolution will be chosen. 1459 * Resolutions with the same aspect ratio of the provided {@link Size} will be considered in 1460 * higher priority before resolutions of different aspect ratios. 1461 * 1462 * <p>It is not allowed to set both target aspect ratio and target resolution on the same 1463 * use case. Attempting so will throw an IllegalArgumentException when building the Config. 1464 * 1465 * <p>The resolution {@link Size} should be expressed in the coordinate frame after 1466 * rotating the supported sizes by the target rotation. For example, a device with 1467 * portrait natural orientation in natural target rotation requesting a portrait image 1468 * may specify 480x640, and the same device, rotated 90 degrees and targeting landscape 1469 * orientation may specify 640x480. 1470 * 1471 * <p>If not set, resolution of 640x480 will be selected to use in priority. 1472 * 1473 * <p>When using the <code>camera-camera2</code> CameraX implementation, which resolution 1474 * will be finally selected will depend on the camera device's hardware level and the 1475 * bound use cases combination. The device hardware level information can be retrieved by 1476 * {@link android.hardware.camera2.CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL} 1477 * from the interop class 1478 * {@link androidx.camera.camera2.interop.Camera2CameraInfo#getCameraCharacteristic(CameraCharacteristics.Key)}. 1479 * A <code>LIMITED-level</code> above device can support a <code>RECORD</code> size 1480 * resolution for {@link ImageAnalysis} when it is bound together with {@link Preview} 1481 * and {@link ImageCapture}. The trade-off is the selected resolution for the 1482 * {@link ImageCapture} will also be restricted by the <code>RECORD</code> size. To 1483 * successfully select a <code>RECORD</code> size resolution for {@link ImageAnalysis}, a 1484 * <code>RECORD</code> size target resolution should be set on both {@link ImageCapture} 1485 * and {@link ImageAnalysis}. This indicates that the application clearly understand the 1486 * trade-off and prefer the {@link ImageAnalysis} to have a larger resolution rather than 1487 * the {@link ImageCapture} to have a <code>MAXIMUM</code> size resolution. For the 1488 * definitions of <code>RECORD</code>, <code>MAXIMUM</code> sizes and more details see the 1489 * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraDevice#regular-capture">Regular capture</a> 1490 * section in {@link android.hardware.camera2.CameraDevice}'s. The <code>RECORD</code> 1491 * size refers to the camera device's maximum supported recording resolution, as 1492 * determined by {@link CamcorderProfile}. The <code>MAXIMUM</code> size refers to the 1493 * camera device's maximum output resolution for that format or target from 1494 * {@link android.hardware.camera2.params.StreamConfigurationMap#getOutputSizes}. 1495 * 1496 * @param resolution The target resolution to choose from supported output sizes list. 1497 * @return The current Builder. 1498 * @deprecated use {@link ResolutionSelector} with {@link ResolutionStrategy} to specify 1499 * the preferred resolution settings instead. 1500 */ 1501 @Override 1502 @Deprecated setTargetResolution(@onNull Size resolution)1503 public @NonNull Builder setTargetResolution(@NonNull Size resolution) { 1504 getMutableConfig() 1505 .insertOption(ImageOutputConfig.OPTION_TARGET_RESOLUTION, resolution); 1506 return this; 1507 } 1508 1509 /** 1510 * Sets the default resolution of the intended target from this configuration. 1511 * 1512 * @param resolution The default resolution to choose from supported output sizes list. 1513 * @return The current Builder. 1514 */ 1515 @RestrictTo(Scope.LIBRARY_GROUP) 1516 @Override setDefaultResolution(@onNull Size resolution)1517 public @NonNull Builder setDefaultResolution(@NonNull Size resolution) { 1518 getMutableConfig().insertOption(OPTION_DEFAULT_RESOLUTION, 1519 resolution); 1520 return this; 1521 } 1522 1523 @RestrictTo(Scope.LIBRARY_GROUP) 1524 @Override setMaxResolution(@onNull Size resolution)1525 public @NonNull Builder setMaxResolution(@NonNull Size resolution) { 1526 getMutableConfig().insertOption(OPTION_MAX_RESOLUTION, resolution); 1527 return this; 1528 } 1529 1530 @RestrictTo(Scope.LIBRARY_GROUP) 1531 @Override setSupportedResolutions( @onNull List<Pair<Integer, Size[]>> resolutions)1532 public @NonNull Builder setSupportedResolutions( 1533 @NonNull List<Pair<Integer, Size[]>> resolutions) { 1534 getMutableConfig().insertOption(OPTION_SUPPORTED_RESOLUTIONS, resolutions); 1535 return this; 1536 } 1537 1538 @RestrictTo(Scope.LIBRARY_GROUP) 1539 @Override setCustomOrderedResolutions(@onNull List<Size> resolutions)1540 public @NonNull Builder setCustomOrderedResolutions(@NonNull List<Size> resolutions) { 1541 getMutableConfig().insertOption(OPTION_CUSTOM_ORDERED_RESOLUTIONS, resolutions); 1542 return this; 1543 } 1544 1545 /** 1546 * Sets the resolution selector to select the preferred supported resolution. 1547 * 1548 * <p>ImageAnalysis has a default {@link ResolutionStrategy} with bound size as 640x480 1549 * and fallback rule of {@link ResolutionStrategy#FALLBACK_RULE_CLOSEST_HIGHER_THEN_LOWER}. 1550 * Applications can override this default strategy with a different resolution strategy. 1551 * 1552 * <p>When using the {@code camera-camera2} CameraX implementation, which resolution is 1553 * finally selected depends on the camera device's hardware level, capabilities and the 1554 * bound use cases combination. The device hardware level and capabilities information 1555 * can be retrieved via the interop class 1556 * {@link androidx.camera.camera2.interop.Camera2CameraInfo#getCameraCharacteristic(android.hardware.camera2.CameraCharacteristics.Key)} 1557 * with 1558 * {@link android.hardware.camera2.CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL} and 1559 * {@link android.hardware.camera2.CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES}. 1560 * 1561 * <p>A {@code LIMITED-level} above device can support a {@code RECORD} size resolution 1562 * for {@link ImageAnalysis} when it is bound together with {@link Preview} and 1563 * {@link ImageCapture}. The trade-off is the selected resolution for the 1564 * {@link ImageCapture} is also restricted by the {@code RECORD} size. To successfully 1565 * select a {@code RECORD} size resolution for {@link ImageAnalysis}, a 1566 * {@link ResolutionStrategy} of selecting {@code RECORD} size resolution should be set 1567 * on both {@link ImageCapture} and {@link ImageAnalysis}. This indicates that the 1568 * application clearly understand the trade-off and prefer the {@link ImageAnalysis} to 1569 * have a larger resolution rather than the {@link ImageCapture} to have a {@code MAXIMUM 1570 * } size resolution. For the definitions of {@code RECORD}, {@code MAXIMUM} sizes and 1571 * more details see the 1572 * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraDevice#regular-capture">Regular capture</a> 1573 * section in {@link android.hardware.camera2.CameraDevice}'s. The {@code RECORD} size 1574 * refers to the camera device's maximum supported recording resolution, as determined by 1575 * {@link CamcorderProfile}. The {@code MAXIMUM} size refers to the camera device's 1576 * maximum output resolution for that format or target from 1577 * {@link android.hardware.camera2.params.StreamConfigurationMap#getOutputSizes}. 1578 * 1579 * <p>The existing {@link #setTargetResolution(Size)} and 1580 * {@link #setTargetAspectRatio(int)} APIs are deprecated and are not compatible with 1581 * {@link #setResolutionSelector(ResolutionSelector)}. Calling either of these APIs 1582 * together with {@link #setResolutionSelector(ResolutionSelector)} will result in an 1583 * {@link IllegalArgumentException} being thrown when you attempt to build the 1584 * {@link ImageAnalysis} instance. 1585 * 1586 * @return The current Builder. 1587 */ 1588 @Override setResolutionSelector( @onNull ResolutionSelector resolutionSelector)1589 public @NonNull Builder setResolutionSelector( 1590 @NonNull ResolutionSelector resolutionSelector) { 1591 getMutableConfig().insertOption(OPTION_RESOLUTION_SELECTOR, resolutionSelector); 1592 return this; 1593 } 1594 1595 // Implementations of ThreadConfig.Builder default methods 1596 1597 /** 1598 * Sets the default executor that will be used for background tasks. 1599 * 1600 * <p>If not set, the background executor will default to an automatically generated 1601 * {@link Executor}. 1602 * 1603 * @param executor The executor which will be used for background tasks. 1604 * @return the current Builder. 1605 */ 1606 @Override setBackgroundExecutor(@onNull Executor executor)1607 public @NonNull Builder setBackgroundExecutor(@NonNull Executor executor) { 1608 getMutableConfig().insertOption(OPTION_BACKGROUND_EXECUTOR, executor); 1609 return this; 1610 } 1611 1612 // Implementations of UseCaseConfig.Builder default methods 1613 1614 @RestrictTo(Scope.LIBRARY_GROUP) 1615 @Override setDefaultSessionConfig(@onNull SessionConfig sessionConfig)1616 public @NonNull Builder setDefaultSessionConfig(@NonNull SessionConfig sessionConfig) { 1617 getMutableConfig().insertOption(OPTION_DEFAULT_SESSION_CONFIG, sessionConfig); 1618 return this; 1619 } 1620 1621 @RestrictTo(Scope.LIBRARY_GROUP) 1622 @Override setDefaultCaptureConfig(@onNull CaptureConfig captureConfig)1623 public @NonNull Builder setDefaultCaptureConfig(@NonNull CaptureConfig captureConfig) { 1624 getMutableConfig().insertOption(OPTION_DEFAULT_CAPTURE_CONFIG, captureConfig); 1625 return this; 1626 } 1627 1628 @RestrictTo(Scope.LIBRARY_GROUP) 1629 @Override setSessionOptionUnpacker( SessionConfig.@onNull OptionUnpacker optionUnpacker)1630 public @NonNull Builder setSessionOptionUnpacker( 1631 SessionConfig.@NonNull OptionUnpacker optionUnpacker) { 1632 getMutableConfig().insertOption(OPTION_SESSION_CONFIG_UNPACKER, optionUnpacker); 1633 return this; 1634 } 1635 1636 @RestrictTo(Scope.LIBRARY_GROUP) 1637 @Override setCaptureOptionUnpacker( CaptureConfig.@onNull OptionUnpacker optionUnpacker)1638 public @NonNull Builder setCaptureOptionUnpacker( 1639 CaptureConfig.@NonNull OptionUnpacker optionUnpacker) { 1640 getMutableConfig().insertOption(OPTION_CAPTURE_CONFIG_UNPACKER, optionUnpacker); 1641 return this; 1642 } 1643 1644 @RestrictTo(Scope.LIBRARY_GROUP) 1645 @Override setSurfaceOccupancyPriority(int priority)1646 public @NonNull Builder setSurfaceOccupancyPriority(int priority) { 1647 getMutableConfig().insertOption(OPTION_SURFACE_OCCUPANCY_PRIORITY, priority); 1648 return this; 1649 } 1650 1651 @RestrictTo(Scope.LIBRARY_GROUP) setImageReaderProxyProvider( @onNull ImageReaderProxyProvider imageReaderProxyProvider)1652 public @NonNull Builder setImageReaderProxyProvider( 1653 @NonNull ImageReaderProxyProvider imageReaderProxyProvider) { 1654 getMutableConfig().insertOption(OPTION_IMAGE_READER_PROXY_PROVIDER, 1655 imageReaderProxyProvider); 1656 return this; 1657 } 1658 1659 @RestrictTo(Scope.LIBRARY_GROUP) 1660 @Override setZslDisabled(boolean disabled)1661 public @NonNull Builder setZslDisabled(boolean disabled) { 1662 getMutableConfig().insertOption(OPTION_ZSL_DISABLED, disabled); 1663 return this; 1664 } 1665 1666 @RestrictTo(Scope.LIBRARY_GROUP) 1667 @Override setHighResolutionDisabled(boolean disabled)1668 public @NonNull Builder setHighResolutionDisabled(boolean disabled) { 1669 getMutableConfig().insertOption(OPTION_HIGH_RESOLUTION_DISABLED, disabled); 1670 return this; 1671 } 1672 1673 @RestrictTo(Scope.LIBRARY_GROUP) 1674 @Override setCaptureType( UseCaseConfigFactory.@onNull CaptureType captureType)1675 public @NonNull Builder setCaptureType( 1676 UseCaseConfigFactory.@NonNull CaptureType captureType) { 1677 getMutableConfig().insertOption(OPTION_CAPTURE_TYPE, captureType); 1678 return this; 1679 } 1680 1681 // Implementations of ImageInputConfig.Builder default methods 1682 1683 /** 1684 * Sets the {@link DynamicRange}. 1685 * 1686 * <p>This is currently only exposed to internally set the dynamic range to SDR. 1687 * 1688 * @return The current Builder. 1689 * @see DynamicRange 1690 */ 1691 @RestrictTo(Scope.LIBRARY) 1692 @Override setDynamicRange(@onNull DynamicRange dynamicRange)1693 public @NonNull Builder setDynamicRange(@NonNull DynamicRange dynamicRange) { 1694 // TODO(b/258099919): ImageAnalysis currently can't support HDR, so we require SDR. 1695 // It's possible to support other DynamicRanges through tone-mapping or by exposing 1696 // other ImageReader formats, such as YCBCR_P010. 1697 if (!Objects.equals(DynamicRange.SDR, dynamicRange)) { 1698 throw new UnsupportedOperationException( 1699 "ImageAnalysis currently only supports SDR"); 1700 } 1701 getMutableConfig().insertOption(OPTION_INPUT_DYNAMIC_RANGE, dynamicRange); 1702 return this; 1703 } 1704 } 1705 } 1706