在 camera2 api 中预览拉伸
preview stretches in camera2 apis
以下是在 camera2 中使用纹理视图时的屏幕截图 apis.In 全屏预览拉伸,但它在使用较低分辨率(第二张图像)时有效。
如何在不拉伸的情况下全屏使用此预览。
这是某些设备上的常见问题。我主要在三星上注意到它。您可以在 TextureView
上使用设置转换的技巧,使其像 ImageView
行为
一样 centerCrop
以下答案假设您仅处于纵向模式。
你的问题是
How to use the preview in full-screen without stretching it
让我们将其分解为两件事:
- 您希望预览充满屏幕
- 预览不能变形
首先你需要知道这在逻辑上是不可能的没有裁剪,如果你的设备的视口具有不同的纵横比和相机提供的任何可用分辨率。
所以我假设您接受裁剪预览。
第 1 步:获取可用分辨率列表
StreamConfigurationMap map = mCameraCharacteristics.get(
CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
if (map == null) {
throw new IllegalStateException("Failed to get configuration map: " + mCameraId);
}
Size[] sizes = map.getOutputSizes(SurfaceTexture.class);
现在您将获得设备相机的可用分辨率(尺寸)列表。
第 2 步:找到最佳纵横比
我们的想法是循环尺寸并查看最合适的尺寸。您可能需要编写自己的 "best fits".
实现
我不打算在这里提供任何代码,因为我所拥有的与您的用例完全不同。但理想情况下,它应该是这样的:
Size findBestSize (Size[] sizes) {
//Logic goes here
}
第 3 步:告诉相机 API 您要使用此尺寸
//...
textureView.setBufferSize(bestSize.getWidth(), bestSize.getHeight());
Surface surface = textureView.getSurface();
try {
mPreviewRequestBuilder = mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
mPreviewRequestBuilder.addTarget(surface);
mCamera.createCaptureSession(Arrays.asList(surface, mImageReader.getSurface()),
mSessionCallback, null);
} catch (final Exception e) {
//...
}
第 4 步:使预览超出视口范围
这与 Camera2 无关 API。我们 "crop" 通过让 SurfaceView
/ TextureView
超出设备视口来进行预览。
首先将您的 SurfaceView
或 TextureView
放入 RelativeLayout
。
在从第 2 步获得宽高比后,使用下面的方法将其扩展到屏幕之外。
请注意,在这种情况下,您可能需要在启动相机之前就知道此纵横比。
//Suppose this value is obtained from Step 2.
//I simply test here by hardcoding a 3:4 aspect ratio, where my phone has a thinner aspect ratio.
float cameraAspectRatio = (float) 0.75;
//Preparation
DisplayMetrics metrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(metrics);
int screenWidth = metrics.widthPixels;
int screenHeight = metrics.heightPixels;
int finalWidth = screenWidth;
int finalHeight = screenHeight;
int widthDifference = 0;
int heightDifference = 0;
float screenAspectRatio = (float) screenWidth / screenHeight;
//Determines whether we crop width or crop height
if (screenAspectRatio > cameraAspectRatio) { //Keep width crop height
finalHeight = (int) (screenWidth / cameraAspectRatio);
heightDifference = finalHeight - screenHeight;
} else { //Keep height crop width
finalWidth = (int) (screenHeight * cameraAspectRatio);
widthDifference = finalWidth - screenWidth;
}
//Apply the result to the Preview
RelativeLayout.LayoutParams lp = (RelativeLayout.LayoutParams) cameraView.getLayoutParams();
lp.width = finalWidth;
lp.height = finalHeight;
//Below 2 lines are to center the preview, since cropping default occurs at the right and bottom
lp.leftMargin = - (widthDifference / 2);
lp.topMargin = - (heightDifference / 2);
cameraView.setLayoutParams(lp);
如果你不在乎第2步的结果,其实你可以忽略第1步到第3步,直接使用那里的库,只要你可以配置它的纵横比即可。 (貌似this one最好,不过我还没试过)
我已经使用 my forked library 进行了测试。在不修改我的库的任何代码的情况下,我仅通过使用第 4 步就设法使预览全屏:
在使用第 4 步之前:
使用第 4 步后:
而且刚拍完照片后的预览也不会变形,因为预览也超出了你的屏幕范围。
但输出图像将包含您在预览中看不到的区域,这很合理。
第一步到第三步的代码一般参考自Google's CameraView.
我知道你的问题是什么。您可能正在尝试这样的事情:
textureView.setSurfaceTextureListener(new TextureView.SurfaceTextureListener() {
@Override
public void onSurfaceTextureAvailable(SurfaceTexture surfaceTexture, int i, int j) {
cam.startPreview(surfaceTexture, i, j);
cam.takePicture();
}
public void onSurfaceTextureSizeChanged(SurfaceTexture surfaceTexture, int i, int i1) { }
public boolean onSurfaceTextureDestroyed(SurfaceTexture surfaceTexture) { return false; }
public void onSurfaceTextureUpdated(SurfaceTexture surfaceTexture) { }
});
我也遇到过类似的情况,但是这一行解决了我的问题
view_finder.preferredImplementationMode = PreviewView.ImplementationMode.TEXTURE_VIEW
在你的xml中:
<androidx.camera.view.PreviewView
android:id="@+id/view_finder"
android:layout_width="match_parent"
android:layout_height="match_parent" />
使用cameraX实现相机可以参考
https://github.com/android/camera-samples/tree/master/CameraXBasic
以下是在 camera2 中使用纹理视图时的屏幕截图 apis.In 全屏预览拉伸,但它在使用较低分辨率(第二张图像)时有效。 如何在不拉伸的情况下全屏使用此预览。
这是某些设备上的常见问题。我主要在三星上注意到它。您可以在 TextureView
上使用设置转换的技巧,使其像 ImageView
行为
以下答案假设您仅处于纵向模式。
你的问题是
How to use the preview in full-screen without stretching it
让我们将其分解为两件事:
- 您希望预览充满屏幕
- 预览不能变形
首先你需要知道这在逻辑上是不可能的没有裁剪,如果你的设备的视口具有不同的纵横比和相机提供的任何可用分辨率。
所以我假设您接受裁剪预览。
第 1 步:获取可用分辨率列表
StreamConfigurationMap map = mCameraCharacteristics.get(
CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
if (map == null) {
throw new IllegalStateException("Failed to get configuration map: " + mCameraId);
}
Size[] sizes = map.getOutputSizes(SurfaceTexture.class);
现在您将获得设备相机的可用分辨率(尺寸)列表。
第 2 步:找到最佳纵横比
我们的想法是循环尺寸并查看最合适的尺寸。您可能需要编写自己的 "best fits".
实现我不打算在这里提供任何代码,因为我所拥有的与您的用例完全不同。但理想情况下,它应该是这样的:
Size findBestSize (Size[] sizes) {
//Logic goes here
}
第 3 步:告诉相机 API 您要使用此尺寸
//...
textureView.setBufferSize(bestSize.getWidth(), bestSize.getHeight());
Surface surface = textureView.getSurface();
try {
mPreviewRequestBuilder = mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
mPreviewRequestBuilder.addTarget(surface);
mCamera.createCaptureSession(Arrays.asList(surface, mImageReader.getSurface()),
mSessionCallback, null);
} catch (final Exception e) {
//...
}
第 4 步:使预览超出视口范围
这与 Camera2 无关 API。我们 "crop" 通过让 SurfaceView
/ TextureView
超出设备视口来进行预览。
首先将您的 SurfaceView
或 TextureView
放入 RelativeLayout
。
在从第 2 步获得宽高比后,使用下面的方法将其扩展到屏幕之外。
请注意,在这种情况下,您可能需要在启动相机之前就知道此纵横比。
//Suppose this value is obtained from Step 2.
//I simply test here by hardcoding a 3:4 aspect ratio, where my phone has a thinner aspect ratio.
float cameraAspectRatio = (float) 0.75;
//Preparation
DisplayMetrics metrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(metrics);
int screenWidth = metrics.widthPixels;
int screenHeight = metrics.heightPixels;
int finalWidth = screenWidth;
int finalHeight = screenHeight;
int widthDifference = 0;
int heightDifference = 0;
float screenAspectRatio = (float) screenWidth / screenHeight;
//Determines whether we crop width or crop height
if (screenAspectRatio > cameraAspectRatio) { //Keep width crop height
finalHeight = (int) (screenWidth / cameraAspectRatio);
heightDifference = finalHeight - screenHeight;
} else { //Keep height crop width
finalWidth = (int) (screenHeight * cameraAspectRatio);
widthDifference = finalWidth - screenWidth;
}
//Apply the result to the Preview
RelativeLayout.LayoutParams lp = (RelativeLayout.LayoutParams) cameraView.getLayoutParams();
lp.width = finalWidth;
lp.height = finalHeight;
//Below 2 lines are to center the preview, since cropping default occurs at the right and bottom
lp.leftMargin = - (widthDifference / 2);
lp.topMargin = - (heightDifference / 2);
cameraView.setLayoutParams(lp);
如果你不在乎第2步的结果,其实你可以忽略第1步到第3步,直接使用那里的库,只要你可以配置它的纵横比即可。 (貌似this one最好,不过我还没试过)
我已经使用 my forked library 进行了测试。在不修改我的库的任何代码的情况下,我仅通过使用第 4 步就设法使预览全屏:
在使用第 4 步之前:
使用第 4 步后:
而且刚拍完照片后的预览也不会变形,因为预览也超出了你的屏幕范围。
但输出图像将包含您在预览中看不到的区域,这很合理。
第一步到第三步的代码一般参考自Google's CameraView.
我知道你的问题是什么。您可能正在尝试这样的事情:
textureView.setSurfaceTextureListener(new TextureView.SurfaceTextureListener() {
@Override
public void onSurfaceTextureAvailable(SurfaceTexture surfaceTexture, int i, int j) {
cam.startPreview(surfaceTexture, i, j);
cam.takePicture();
}
public void onSurfaceTextureSizeChanged(SurfaceTexture surfaceTexture, int i, int i1) { }
public boolean onSurfaceTextureDestroyed(SurfaceTexture surfaceTexture) { return false; }
public void onSurfaceTextureUpdated(SurfaceTexture surfaceTexture) { }
});
我也遇到过类似的情况,但是这一行解决了我的问题
view_finder.preferredImplementationMode = PreviewView.ImplementationMode.TEXTURE_VIEW
在你的xml中:
<androidx.camera.view.PreviewView
android:id="@+id/view_finder"
android:layout_width="match_parent"
android:layout_height="match_parent" />
使用cameraX实现相机可以参考 https://github.com/android/camera-samples/tree/master/CameraXBasic