10

最近几个月,Android 发布了一个新的 API camerax。我试图了解如何让相机自动对焦。

https://groups.google.com/a/android.com/forum/#!searchin/camerax-developers/auto $20focus|sort:date/camerax-developers/IQ3KZd8iOIY/LIbrRIqEBgAJ

这是关于该主题的讨论,但几乎没有具体的文档。

https://github.com/android/camera-samples/tree/master/CameraXBasic/app/src/main/java/com/android/example/cameraxbasic

这也是基本的camerax应用程序,但我找不到任何处理自动对焦的文件。

任何有关文档的提示或要点都是有帮助的。此外,我对 android 还很陌生,所以很可能我遗漏了一些使上述链接更有用的东西。

4

7 回答 7

32

使用当前的 CameraX 1.0.0,您可以通过以下两种方式进行操作:

  1. 每 X 秒自动对焦:

     previewView.afterMeasured {
         val autoFocusPoint = SurfaceOrientedMeteringPointFactory(1f, 1f)
                 .createPoint(.5f, .5f)
         try {
             val autoFocusAction = FocusMeteringAction.Builder(
                 autoFocusPoint,
                 FocusMeteringAction.FLAG_AF
             ).apply {
                 //start auto-focusing after 2 seconds
                 setAutoCancelDuration(2, TimeUnit.SECONDS)
             }.build()
             camera.cameraControl.startFocusAndMetering(autoFocusAction)
         } catch (e: CameraInfoUnavailableException) {
             Log.d("ERROR", "cannot access camera", e)
         }
     }
    
  2. 专注于点击:

     previewView.afterMeasured {
         previewView.setOnTouchListener { _, event ->
             return@setOnTouchListener when (event.action) {
                 MotionEvent.ACTION_DOWN -> {
                     true
                 }
                 MotionEvent.ACTION_UP -> {
                     val factory: MeteringPointFactory = SurfaceOrientedMeteringPointFactory(
                         previewView.width.toFloat(), previewView.height.toFloat()
                     )
                     val autoFocusPoint = factory.createPoint(event.x, event.y)
                     try {
                         camera.cameraControl.startFocusAndMetering(
                             FocusMeteringAction.Builder(
                                 autoFocusPoint,
                                 FocusMeteringAction.FLAG_AF
                             ).apply {
                                 //focus only when the user tap the preview
                                 disableAutoCancel()
                             }.build()
                         )
                     } catch (e: CameraInfoUnavailableException) {
                         Log.d("ERROR", "cannot access camera", e)
                     }
                     true
                 }
                 else -> false // Unhandled event.
             }
         }
     }
    

afterMeasured扩展功能是一个简单的实用程序:(感谢 ch271828n改进它

inline fun View.afterMeasured(crossinline block: () -> Unit) {
    if (measuredWidth > 0 && measuredHeight > 0) {
        block()
    } else {
        viewTreeObserver.addOnGlobalLayoutListener(object : ViewTreeObserver.GlobalLayoutListener {
            override fun onGlobalLayout() {
                if (measuredWidth > 0 && measuredHeight > 0) {
                    viewTreeObserver.removeOnGlobalLayoutListener(this)
                    block()
                } 
            }
        })
    }
}

Camera可以通过以下方式获得对象

val camera = cameraProvider.bindToLifecycle(
    this@Activity, cameraSelector, previewView //this is a PreviewView
)
于 2020-02-06T13:16:34.907 回答
4

只需指出,要使“点击聚焦”与 PreviewView 一起使用,您需要使用 DisplayOrientedMeteringPointFactory。否则你会弄乱坐标。

val factory = DisplayOrientedMeteringPointFactory(activity.display, camera.cameraInfo, previewView.width.toFloat(), previewView.height.toFloat())

其余的使用 MatPag 的答案。

于 2020-11-18T18:54:50.730 回答
3

某些 Android 设备存在相机无法使用 CameraX 自动对焦的问题。CameraX 团队意识到了这一点,并正在通过内部票证对其进行跟踪,并希望很快能得到修复。

于 2019-10-04T15:23:08.050 回答
1

您可以在此处找到有关 Focus 的文档,因为它是在“1.0.0-alpha05”中添加的 https://developer.android.com/jetpack/androidx/releases/camera#camera2-core-1.0.0-alpha05

基本上你必须在你的视图上设置一个触摸监听器并抓住点击的位置

private boolean onTouchToFocus(View viewA, MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                break;
            case MotionEvent.ACTION_UP:
                    return focus(event);
                break;
            default:
                // Unhandled event.
                return false;
        }
        return true;
    }

并将这个位置翻译成点

private boolean focus(MotionEvent event) {
        final float x = (event != null) ? event.getX() : getView().getX() + getView().getWidth() / 2f;
        final float y = (event != null) ? event.getY() : getView().getY() + getView().getHeight() / 2f;

        TextureViewMeteringPointFactory pointFactory = new TextureViewMeteringPointFactory(textureView);
        float afPointWidth = 1.0f / 6.0f;  // 1/6 total area
        float aePointWidth = afPointWidth * 1.5f;
        MeteringPoint afPoint = pointFactory.createPoint(x, y, afPointWidth, 1.0f);
        MeteringPoint aePoint = pointFactory.createPoint(x, y, aePointWidth, 1.0f);

           try {
            CameraX.getCameraControl(lensFacing).startFocusAndMetering(
                FocusMeteringAction.Builder.from(afPoint, FocusMeteringAction.MeteringMode.AF_ONLY)
                                           .addPoint(aePoint, FocusMeteringAction.MeteringMode.AE_ONLY)
                                           .build());
        } catch (CameraInfoUnavailableException e) {
            Log.d(TAG, "cannot access camera", e);
        }

        return true;
    }

于 2019-10-03T08:45:37.010 回答
1

使用当前1.0.0-rc031.0.0-alpha22工件

此解决方案假定相机已经设置,包括bindToLifecycle. 之后,我们需要在尝试对焦相机之前检查 previewView streamState 是否为 STREAMING

 previewView.getPreviewStreamState().observe(getActivity(), value -> {
        if (value.equals(STREAMING)) {
            setUpCameraAutoFocus();
        }
    });

private void setUpCameraAutoFocus() {
    final float x =  previewView.getX() + previewView.getWidth() / 2f;
    final float y =  previewView.getY() + previewView.getHeight() / 2f;

  MeteringPointFactory pointFactory = previewView.getMeteringPointFactory();
  float afPointWidth = 1.0f / 6.0f;  // 1/6 total area
  float aePointWidth = afPointWidth * 1.5f;
  MeteringPoint afPoint = pointFactory.createPoint(x, y, afPointWidth);
  MeteringPoint aePoint = pointFactory.createPoint(x, y, aePointWidth);
  ListenableFuture<FocusMeteringResult> future = cameraControl.startFocusAndMetering(
          new FocusMeteringAction.Builder(afPoint,
                  FocusMeteringAction.FLAG_AF).addPoint(aePoint,
                  FocusMeteringAction.FLAG_AE).build());
  Futures.addCallback(future, new FutureCallback<FocusMeteringResult>() {
    @Override
    public void onSuccess(@Nullable FocusMeteringResult result) {
    }

    @Override
    public void onFailure(Throwable t) {
      // Throw the unexpected error.
      throw new RuntimeException(t);
    }
  }, CameraXExecutors.directExecutor());
}
于 2021-03-16T16:42:57.410 回答
0

我遇到了同样的问题,我设置了这个解决方案(即使它看起来很愚蠢)。

val displayMetrics = resources.displayMetrics
val factory = SurfaceOrientedMeteringPointFactory(
    displayMetrics.widthPixels.toFloat(),
    displayMetrics.heightPixels.toFloat()
)
val point = factory.createPoint(
    displayMetrics.widthPixels / 2f,
    displayMetrics.heightPixels / 2f
)
val action = FocusMeteringAction
            .Builder(point, FocusMeteringAction.FLAG_AF)
            .build()

try {
    camera = cameraProvider.bindToLifecycle(
        lifecycleOwner,
        cameraSelector,
        preview,
        imageAnalyzer
    )
    GlobalScope.launch(Dispatchers.Default) {
        while (workflowModel.isCameraLive) {
            camera?.cameraControl?.startFocusAndMetering(action)?
            delay(3000)
        }
    }
} catch (e: Exception) {
    Log.e(mTag, "Use case binding failed", e)
}

while基本上,我在循环中每 3 秒重新启动一次聚焦动作。

isCameraLive是一个布尔变量,我存储在我的 viewModel 中,我true在启动相机和false通过调用cameraProvider.unbindAll().

于 2021-01-15T19:37:50.637 回答
0

投票率最高afterMeasured函数有一个严重的错误:它的回调通常从未被调用过。

非常简单的修复:

inline fun View.afterMeasured(crossinline block: () -> Unit) {
    if (measuredWidth > 0 && measuredHeight > 0) {
        block()
    } else {
        viewTreeObserver.addOnGlobalLayoutListener(object : ViewTreeObserver.OnGlobalLayoutListener {
            override fun onGlobalLayout() {
                if (measuredWidth > 0 && measuredHeight > 0) {
                    viewTreeObserver.removeOnGlobalLayoutListener(this)
                    block()
                }
            }
        })
    }
}

说明:我观察到(在生产中的应用程序中),有时视图已经被测量并且没有 ui 更改,因此onGlobalLayout以后永远不会调用。那么afterMeasured's 的回调将永远不会被调用,因此相机不会被初始化。

于 2021-12-10T11:53:30.120 回答