如何使用Android CameraX API缩放相机?

9

我尝试使用以下代码和CameraX缩放相机。

首先,我从CameraX获取了一个Preview,并尝试通过以下方式使用Preview进行缩放。

var config = CameraX.getDefaultUseCaseConfig(PreviewConfig::class.java, lensFacing)
var preview = Preview(config)
preview.zoom(zoom)

在使用 preview.zoom()后,我只是重新绑定了 CameraX,但出现了一些错误,并且它无法工作。

CameraX.bindToLifecycle(this, preview, imageCapture, videoCapture)

当上述代码无法工作时,我先尝试使用CameraX.unbindAll(),然后再调用CameraX.bindToLifecycle()。但仍然遇到错误,并且在缩放方面没有成功。
请告诉我如何使用CameraX API进行相机缩放。
以下是仅使用CameraX.bindToLifecycle()的错误日志更新:
java.lang.IllegalArgumentException: Exceeded max simultaneously bound image capture use cases.
   at androidx.camera.camera2.impl.UseCaseSurfaceOccupancyManager.checkUseCaseLimitNotExceeded(UseCaseSurfaceOccupancyManager.java:61)
   at androidx.camera.camera2.impl.Camera2DeviceSurfaceManager.getSuggestedResolutions(Camera2DeviceSurfaceManager.java:146)
   at androidx.camera.core.CameraX.calculateSuggestedResolutions(CameraX.java:449)
   at androidx.camera.core.CameraX.bindToLifecycle(CameraX.java:144)
   at com.android.example.cameraxbasic.fragments.CameraFragment$updateCameraUi$2.onTouch(CameraFragment.kt:408)
   at android.view.View.dispatchTouchEvent(View.java:12512)
   at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3032)
   at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2719)
   at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3032)
   at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2719)
   at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3032)
   at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2719)
   at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3032)
   at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2719)
   at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3032)
   at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2719)
   at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3032)
   at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2719)
   at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3032)
   at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2719)
   at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3032)
   at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2719)
   at com.android.internal.policy.DecorView.superDispatchTouchEvent(DecorView.java:475)
   at com.android.internal.policy.PhoneWindow.superDispatchTouchEvent(PhoneWindow.java:1836)
   at android.app.Activity.dispatchTouchEvent(Activity.java:3404)
   at androidx.appcompat.view.WindowCallbackWrapper.dispatchTouchEvent(WindowCallbackWrapper.java:69)
   at com.android.internal.policy.DecorView.dispatchTouchEvent(DecorView.java:433)
   at android.view.View.dispatchPointerEvent(View.java:12755)
   at android.view.ViewRootImpl$ViewPostImeInputStage.processPointerEvent(ViewRootImpl.java:5150)
   at android.view.ViewRootImpl$ViewPostImeInputStage.onProcess(ViewRootImpl.java:4953)
   at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:4470)
   at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:4523)
   at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:4489)
   at android.view.ViewRootImpl$AsyncInputStage.forward(ViewRootImpl.java:4629)
   at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:4497)
   at android.view.ViewRootImpl$AsyncInputStage.apply(ViewRootImpl.java:4686)
   at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:4470)
   at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:4523)
   at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:4489)
   at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:4497)
   at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:4470)
   at android.view.ViewRootImpl.deliverInputEvent(ViewRootImpl.java:7192)
   at android.view.ViewRootImpl.doProcessInputEvents(ViewRootImpl.java:7126)
   at android.view.ViewRootImpl.enqueueInputEvent(ViewRootImpl.java:7087)
   at android.view.ViewRootImpl$WindowInputEventReceiver.onInputEvent(ViewRootImpl.java:7295)
   at android.view.InputEventReceiver.dispatchInputEvent(InputEventReceiver.java:193)
   at android.view.InputEventReceiver.nativeConsumeBatchedInputEvents(Native Method)
   at android.view.InputEventReceiver.consumeBatchedInputEvents(InputEventReceiver.java:184)
   at android.view.ViewRootImpl.doConsumeBatchedInput(ViewRootImpl.java:7266)
   at android.view.ViewRootImpl$ConsumeBatchedInputRunnable.run(ViewRootImpl.java:7318)
   at android.view.Choreographer$CallbackRecord.run(Choreographer.java:949)
   at android.view.Choreographer.doCallbacks(Choreographer.java:761)
   at android.view.Choreographer.doFrame(Choreographer.java:690)
   at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:935)
   at android.os.Handler.handleCallback(Handler.java:873)
   at android.os.Handler.dispatchMessage(Handler.java:99)
   at android.os.Looper.loop(Looper.java:193)
   at android.app.ActivityThread.main(ActivityThread.java:6912)
   at java.lang.reflect.Method.invoke(Native Method)
   at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
   at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:860)

使用CameraX.unbindAll()CameraX.bindToLifecycle()时的错误日志:

   E/Legacy-CameraDevice-JNI: getNativeWindow: Surface had no valid native window.
   E/Legacy-CameraDevice-JNI: LegacyCameraDevice_nativeDetectSurfaceDimens: Could not retrieve native window from surface.

--------- beginning of crash
   2019-05-09 16:49:29.155 31123-31144/com.android.example.cameraxbasic E/AndroidRuntime: FATAL EXCEPTION: CameraX-
Process: com.android.example.cameraxbasic, PID: 31123
java.lang.IllegalArgumentException: Surface was abandoned
    at android.hardware.camera2.utils.SurfaceUtils.getSurfaceSize(SurfaceUtils.java:84)
    at android.hardware.camera2.params.OutputConfiguration.<init>(OutputConfiguration.java:260)
    at android.hardware.camera2.params.OutputConfiguration.<init>(OutputConfiguration.java:145)
    at android.hardware.camera2.impl.CameraDeviceImpl.createCaptureSession(CameraDeviceImpl.java:518)
    at androidx.camera.camera2.impl.CaptureSession.open(CaptureSession.java:196)
    at androidx.camera.camera2.impl.Camera.openCaptureSession(Camera.java:535)
    at androidx.camera.camera2.impl.Camera$StateCallback.onOpened(Camera.java:743)
    at androidx.camera.core.CameraDeviceStateCallbacks$ComboDeviceStateCallback.onOpened(CameraDeviceStateCallbacks.java:99)
    at android.hardware.camera2.impl.CameraDeviceImpl$1.run(CameraDeviceImpl.java:152)
    at android.os.Handler.handleCallback(Handler.java:873)
    at android.os.Handler.dispatchMessage(Handler.java:99)
    at android.os.Looper.loop(Looper.java:193)
    at android.os.HandlerThread.run(HandlerThread.java:65)
 Caused by: android.hardware.camera2.legacy.LegacyExceptionUtils$BufferQueueAbandonedException
    at android.hardware.camera2.legacy.LegacyExceptionUtils.throwOnError(LegacyExceptionUtils.java:73)
    at android.hardware.camera2.legacy.LegacyCameraDevice.getSurfaceSize(LegacyCameraDevice.java:606)
    at android.hardware.camera2.utils.SurfaceUtils.getSurfaceSize(SurfaceUtils.java:82)
    at android.hardware.camera2.params.OutputConfiguration.<init>(OutputConfiguration.java:260) 
    at android.hardware.camera2.params.OutputConfiguration.<init>(OutputConfiguration.java:145) 
    at android.hardware.camera2.impl.CameraDeviceImpl.createCaptureSession(CameraDeviceImpl.java:518) 
    at androidx.camera.camera2.impl.CaptureSession.open(CaptureSession.java:196) 
    at androidx.camera.camera2.impl.Camera.openCaptureSession(Camera.java:535) 
    at androidx.camera.camera2.impl.Camera$StateCallback.onOpened(Camera.java:743) 
    at androidx.camera.core.CameraDeviceStateCallbacks$ComboDeviceStateCallback.onOpened(CameraDeviceStateCallbacks.java:99) 
    at android.hardware.camera2.impl.CameraDeviceImpl$1.run(CameraDeviceImpl.java:152) 
    at android.os.Handler.handleCallback(Handler.java:873) 
    at android.os.Handler.dispatchMessage(Handler.java:99) 
    at android.os.Looper.loop(Looper.java:193) 
    at android.os.HandlerThread.run(HandlerThread.java:65) 


--------- beginning of system

@tynn 请检查更新后的问题,我已经添加了错误日志。 - Patel Pinkal
我认为这两种方法都是错误的。你需要调用 unbindAll(),因为你经常调用 bindToLifecycle()。但是这会清理使用情况。重复使用可能效果不佳(尚未)。你尝试过在不重新绑定任何内容的情况下调用 preview.zoom() 吗? - tynn
@tynn,我已经尝试过preview.zoom()但是没有重新绑定,什么也没发生。 - Patel Pinkal
6个回答

14

最后,我们在CameraX中得到了Zoom API。请查看下面两种方法,您可以使用它们来缩放相机预览。

双指捏合以进行缩放:

val scaleGestureDetector = ScaleGestureDetector(context, listener)

cameraTextureView.setOnTouchListener { _, event ->
    scaleGestureDetector.onTouchEvent(event)
    return@setOnTouchListener true
}

val listener = object : ScaleGestureDetector.SimpleOnScaleGestureListener() {
    override fun onScale(detector: ScaleGestureDetector): Boolean {
        val scale = cameraInfo.zoomRatio.value * detector.scaleFactor
        cameraControl.setZoomRatio(scale)
        return true
    }
}

使用滑动条进行缩放:

zoomSeekBar.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener {        
    override fun onProgressChanged(seekBar: SeekBar?, progress: Int, fromUser: Boolean) {
        cameraControl.setLinearZoom(progress / 100.toFloat())
    }

    override fun onStartTrackingTouch(seekBar: SeekBar?) {}

    override fun onStopTrackingTouch(seekBar: SeekBar?) {}
})

请使用以下内容来获取 cameraControl

val cameraControl = CameraX.getCameraControl(lensFacing)

代码参考链接: https://github.com/Pinkal7600/camera-samples/tree/master/CameraXBasic


@SuyogDorlikar,请检查我编辑过的谷歌代码示例,它可以用于缩放。我在答案底部添加了代码示例链接。 - Patel Pinkal
谢谢!但只需 -" progress / 10.toFloat()"。 - nicolas asinovich

5
这是我在最新的 CameraX 1.0.0-beta01 示例项目中所做的。添加到 onViewCreated 底部。
val listener = object : ScaleGestureDetector.SimpleOnScaleGestureListener() {
override fun onScale(detector: ScaleGestureDetector): Boolean {
    val scale = camera!!.cameraInfo.zoomState.value!!.zoomRatio * detector.scaleFactor
        camera!!.cameraControl.setZoomRatio(scale)
        return true
    }
}

val scaleGestureDetector = ScaleGestureDetector(context, listener)

viewFinder.setOnTouchListener { _, event ->
    scaleGestureDetector.onTouchEvent(event)
    return@setOnTouchListener true
}

3
为了使缩放正常工作,您还需要在bindToLifecycle()之后调用zoom()。另请注意,zoom()接受一个以传感器坐标为单位的矩形。详情请参见此处。 您可能还需要使用camera2 API获取传感器活动区域并获取相机ID(请使用具有正确LENS_FACING的第一个相机ID)。 我们知道这些是很多工作,因此我们正在开发一种新的更高级别的缩放API,它更容易、更简单。

感谢您的回答。我尝试了在bindToLifecycle()之后使用zoom()方法,并传递了Rect参数,但没有得到任何输出。但是,我会像您提到的那样尝试使用Camera2 API。希望我能够在相机缩放方面得到一些输出。 - Patel Pinkal
正如您所说,您正在开发新的级别缩放API,这将是简单易用的。那么,它会与我们目前使用的相机和相机2 API有所不同吗? - Patel Pinkal
是的,新的缩放 API 将与 camera1 和 camera2 API 不同。 - Scott Nien
好的,谢谢。我们在等待着。 - Patel Pinkal

0

看起来错误与预览缩放无关: java.lang.IllegalArgumentException: Exceeded max simultaneously bound image capture use cases.

这表明您在解绑之前多次调用了CameraX.bindToLifecycle(imageCapture),导致了崩溃。我建议您使用配置构建器而不是getDefaultUseCaseConfig,因为它们更明确易读,而getDefaultUseCaseConfig是一个隐藏的API。

请参考官方示例以获取示例实现,以及文档以获取更多详细信息。


谢谢您的回答,但是它并没有帮助我,因为我已经根据GitHub上的官方示例代码自己完成了这个缩放代码,并且没有任何关于使用CameraX进行相机缩放的文档。如果您有任何想法,请告诉我如何缩放? - Patel Pinkal

0
  ScaleGestureDetector.OnScaleGestureListener listener = new ScaleGestureDetector.OnScaleGestureListener() {
            @Override
            public boolean onScale(ScaleGestureDetector scaleGestureDetector) {
                ZoomState f = cam1.getCameraInfo().getZoomState().getValue();
                Log.d("Zoom", String.valueOf(f.getZoomRatio()));

                float scale = scaleGestureDetector.getScaleFactor();
                cam1.getCameraControl().setZoomRatio(scale * f.getZoomRatio());
                return true;
            }

            @Override
            public boolean onScaleBegin(ScaleGestureDetector scaleGestureDetector) {

                return true;
            }

            @Override
            public void onScaleEnd(ScaleGestureDetector scaleGestureDetector) {

            }
        };
        ScaleGestureDetector scaleGestureDetector = new ScaleGestureDetector(getApplicationContext(), listener);

        previewView.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View view, MotionEvent motionEvent) {
                return scaleGestureDetector.onTouchEvent(motionEvent);

            }
        });


1
目前你的回答不够清晰,请编辑并添加更多细节,以帮助其他人理解它如何回答问题。你可以在帮助中心找到有关如何编写好答案的更多信息。 - Community
1-创建“ScaleGestureDetector.OnScaleGestureListener”对象,它将监听屏幕触摸/捏合。2-创建一个ScaleGestureDetector对象。3-然后在预览上设置触摸监听器“scaleGestureDetector.onTouchEvent(motionEvent);”。 - risabvishwakarma

-1

我可以使用这段代码进行缩放

val my = Rect(left, top, right, bottom)
preview.zoom(my)

请在 GitHub 上查看完整示例,只需检查我的 答案

对我来说它完美地工作。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接