在我的应用程序中,我在 SurfaceView 上有一个相机预览,它几乎完全按照我想要的方式工作,但有一个令人讨厌的问题:
当我在我的 Sony Xperia 或我正在测试的 Samsung S7 上进入相机 View 时(模拟器看起来不错)它会正确显示预览。当我旋转它时,预览会正确旋转,当我将它旋转回来时,预览会失去它的比例并且 View 会扭曲。第一次旋转后,每次旋转回来时,我都会有同样的失真。
目前有效的方法是我使用 SurfaceView 的缩放:
FrameLayout.LayoutParams newScale = new FrameLayout.LayoutParams(width, height, Gravity.CENTER );
mTextureView.setLayoutParams(newScale);
mTextureView.setScaleX( xScale);
mTextureView.setScaleY( yScale);
然后我创建 captureSession:
mCaptureSession = cameraCaptureSession;
try {
mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE,
CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
mPreviewRequestBuilder.set(CaptureRequest.SCALER_CROP_REGION, cropRectangle);
mPreviewRequest = mPreviewRequestBuilder.build();
mCaptureSession.setRepeatingRequest(mPreviewRequest, getCaptureCallback(), mBackgroundHandler);
} catch (CameraAccessException e) {
// and so on.
}
显然,此时我已经非常沮丧,所以我一直在记录我的轮换,这是我变得非常困惑的地方。
这看起来不错:
========================
View Dimensions (720, 1184)
Sensor Dimensions: (3984, 5512)
Final size: 720, 1280
========================
View ratio: 1.7777778 previewRatio: 1.6444445 scale: 1.0
Image preview size is (720, 1184) scale is: (1.0) image size is (720, 1280)
Image scale is (1.3835341, 0.925) Max Image is (3984, 5512) cropRectangle is (441, 0 -> 3543, 5512)
然后我旋转它:
========================
View Dimensions (1184, 720)
Sensor Dimensions: (5512, 3984)
Final size: 1280, 720
========================
View ratio: 0.5625 previewRatio: 0.6081081 scale: 1.0
Image preview size is (1184, 720) scale is: (1.0) view size is (1280, 720)
Image scale is (0.925, 1.3835342) Max Image is (5512, 3984) cropRectangle is (0, 441 -> 5512, 3543)
现在我将它旋转回来:
========================
View Dimensions (720, 1184)
Sensor Dimensions: (3984, 5512)
Final size: 720, 1280
========================
View ratio: 1.7777778 previewRatio: 1.6444445 scale: 1.0
Image preview size is (720, 1184) scale is: (1.0) view size is (720, 1280)
Image scale is (1.3835341, 0.925) Max Image is (3984, 5512) cropRectangle is (441, 0 -> 3543, 5512)
那些比例和那个裁剪矩形看起来与我完全相同,但由于某种原因,最后一张图像扭曲得更宽,好像 x 比例更接近 1.8 或类似。我可以向任一方向旋转回横向,看起来不错,然后转回纵向,一切又变胖了。
我想知道这是否是相机试图自动校正的一个特征,但我对如何进一步解决这个问题缺乏想法。我如何说服我的手机旋转两次后它应该显示与第一次显示相同的比例?
更新:如果我在第二次运行时使用 setXScale 并将其设置为 1.0 而不是第一次运行时的值,它看起来是正确的。但是当我比较 SurfaceView 的变换矩阵时图像比例不好和好的时候,它们是相同的,所以这看起来像是对预览中缩放比例不佳的补偿。我可以更改缩放的行为方式,因此初始预览太窄并且旋转后比例正确,但这同样没有真正帮助,因为我需要能够在纵向和横向之间切换而图像不会被它扭曲,这似乎应该是默认行为。在 Landscape 中打开 View 的行为方式大致相同 - 首次打开时扭曲,然后正常。
似乎每次都正确地重新创建了 CaptureSession。
第二次更新:调用 dumpsys SurfaceFlinger
显示可能的原因:
-------------------------------------------------------------------------------
Layer name
Z | Comp Type | Disp Frame (LTRB) | Source Crop (LTRB)
-------------------------------------------------------------------------------
SurfaceView - com.myapp.CameraActivity#0 4294967294 | Device | 0 0 720 1184 | 0.0 0.0 960.0 720.0
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
所以这里看起来问题是源裁剪对于显示帧的尺寸来说太小了,因此图像被拉伸(stretch)了。我的设备认为它正在获取 1280x720 的图像,但出于某种原因,Source Crop 显示的尺寸较小。这可能与传感器的比例有关吗?我是否需要只从与传感器比率匹配的尺寸中挑选?
最佳答案
与 FrameLayout 相比使用自定义纹理 View ,然后通过 Surface 访问它会解决您的问题。
private void createCameraPreviewSession() {
try {
SurfaceTexture texture = mTextureView.getSurfaceTexture();
assert texture != null;
texture.setDefaultBufferSize(mPreviewSize.getWidth(), mPreviewSize.getHeight());
Surface surface = new Surface(texture);
mPreviewRequestBuilder
= mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
mPreviewRequestBuilder.addTarget(surface);
mCameraDevice.createCaptureSession(Arrays.asList(surface, mImageReader.getSurface()),
new CameraCaptureSession.StateCallback() {
@Override
public void onConfigured(@NonNull CameraCaptureSession cameraCaptureSession) {
// The camera is already closed
if (null == mCameraDevice) {
return;
}
mCaptureSession = cameraCaptureSession;
try {
mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE,
CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
setAutoFlash(mPreviewRequestBuilder);
mPreviewRequest = mPreviewRequestBuilder.build();
mCaptureSession.setRepeatingRequest(mPreviewRequest,
mCaptureCallback, mBackgroundHandler);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
@Override
public void onConfigureFailed(
@NonNull CameraCaptureSession cameraCaptureSession) {
showToast("Failed");
}
}, null
);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
并根据相机输出从特定设备检索旋转
/**
* Retrieves the JPEG orientation from the specified screen rotation.
*
* @param rotation The screen rotation.
* @return The JPEG orientation (one of 0, 90, 270, and 360)
*/
private int getOrientation(int rotation) {
return (ORIENTATIONS.get(rotation) + mSensorOrientation + 270) % 360;
}
关于Android Camera2 为什么我的预览比例在旋转后又旋转回来?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53891999/