纵向模式下的相机横向预览
Camera landscape preview in portrait mode
我有一个对话框设计,相机预览在纵向模式下应该是横向的(见图片)。我通过 getOptimalPreviewSize() 正确选择了预览大小,当我不使用 setDisplayOrientation(90) 时,我得到这个:
当我使用 setDisplayOrientation(90) 时,我得到这个:
有人知道如何解决吗? android能做那种事吗?
回答(感谢 Eddy Talvala):
由于 xml 中的摄像头视图是水平的,因此您无法纵向处理整个摄像头图片,因此您应该裁剪。我决定水平放置并裁剪底部的所有内容:
| |
| _________ | _____
|| || | |
|| || | ^-^ |
|| ______ || | \_/ |
|| | ^-^ ||| | | |
|| |_\_/_ ||| |__|__|
|| image || sensor
|| ||
|| ||
||_________||
|| < O [] ||
|___________|
device
您应该使用 TextureView 指定适合和裁剪的矩阵。类似的东西 (kotlin):
fun camInit() {
val camParam = camera.parameters
val optimalSize = getOptimalPortraitPreviewSize(camParam.supportedPreviewSizes, width, height)
camParam.setPreviewSize(optimalSize.width, optimalSize.height)
camera.parameters = camParam
camera.setPreviewTexture(surface)
val scaleDelta = optimalSize.height - preview.width // for portrait
val scaleY: Float = (optimalSize.width.toFloat() - scaleDelta) / preview.height // for portrait
val matrix = Matrix()
matrix.setScale(1f, scaleY) // 1f cause we fit horizontally
preview.setTransform(matrix)
camera.startPreview()
}
请注意,对于纵向模式,您应该使用特定的 getOptimalProtraitPreviewSize(),因为每个人都使用标准函数来获得相机预览的最佳尺寸(使用 ASPECT_TOLERANCE 和其他东西)可以 return 你一个尺寸分辨率小。尝试这样的事情:
fun getOptimalPortraitPreviewSize(sizes: List<Camera.Size>, previewWidth: Int) {
var selectedSize: Camera.Size = sizes[0]
for (size in sizes) {
// use size's height cause of portrait
val currentSizeWidthDelta = abs(size.height - previewWidth)
val selectedSizeWidthDelta = abs(selectedSize.height - previewWidth)
if (currentSizeWidthDelta < selectedSizeWidthDelta) selectedSize = size
}
return selectedSize
}
图像传感器的长边与设备的长边对齐。
(bad ascii art):
____________
| |
| _________ | _____
|| || | |
|| || | |
|| || | |
|| || | |
|| || |_____|
|| || sensor
|| || physical
|| || orientation
||_________||
|| < O [] ||
|___________|
device
鉴于此,您无法像这样绘制图像:
_____________
| |
| _________ | _____
|| || | |
|| || | |
|| ______ || | |
|| | ||| | |
|| |______||| |_____|
|| image || sensor
|| ||
|| ||
||_________||
|| < O [] ||
|___________|
device
除非您旋转图像(在这种情况下向上不是向上):
_____________
| |
| _________ | _____
|| || | |
|| || | ^-^ |
|| ______ || | \_/ |
|| | <-\__||| | | |
|| |_<-/__||| |__|__|
|| image || sensor
|| ||
|| ||
||_________||
|| < O [] ||
|___________|
device
或者你水平拉伸图像,看起来很糟糕:
_____________
| |
| _________ | _____
|| || | |
|| || | ^-^ |
|| ______ || | \_/ |
|| | ^___^||| | | |
|| |___|__||| |__|__|
|| image || sensor
|| ||
|| ||
||_________||
|| < O [] ||
|___________|
device
或者您裁剪图像的一部分,这会大大降低 FOV:
_____________
| |
| _________ | _____
|| || | |
|| || | ^-^ |
|| ______ || | \_/ |
|| | ^-^ ||| | | |
|| |_\_/__||| |__|__|
|| image || sensor
|| ||
|| ||
||_________||
|| < O [] ||
|___________|
device
因为图像传感器是横向的,而 phone 是横向的,所以如果不发生这三种情况之一,就无法在纵向中放置横向预览 UI。您要么需要在人像 UI 中进行人像预览,要么需要裁剪图像。这不是 Android 的限制,它只是几何形状的限制。
如果你想裁剪,你可能想要将相机数据发送到 SurfaceTexture,然后在 OpenGL 中裁剪,如果你想要实时预览。
如果您使用的是 Camera2 API。当通过 ChooseOptimalSize 获取 mPreviewSize 时,您可以交换宽度和高度。 (宽度和高度来自您的视图)。
然后
textureView.setAspectRatio(mPreviewSize.Height, mPreviewSize.Width);
创建预览会话时
texture.SetDefaultBufferSize(mPreviewSize.Width, mPreviewSize.Height);
然后你将预览正常图像
感谢@Eddy Talvala 和@blinker!我使用我的功能来正确更新任何方向的尺寸:
public void invalidateSize(TextureView textureView) {
Point cameraResolution = getCameraResolution();
int previewRotation = getPreviewRotation();
// Camera and preview on screen in the opposite orientation
if (previewRotation == 90 || previewRotation == 270) {
//noinspection SuspiciousNameCombination
cameraResolution = new Point(cameraResolution.y, cameraResolution.x);
}
float cameraAspectRatio = cameraResolution.x / (float) cameraResolution.y;
float textureAspectRatio = textureView.getWidth() / (float) textureView.getHeight();
float scaleX = cameraAspectRatio / textureAspectRatio;
float scaleY = textureAspectRatio / cameraAspectRatio;
if (scaleX > scaleY) {
scaleY = 1;
} else {
scaleX = 1;
}
Matrix matrix = new Matrix();
matrix.setScale(scaleX, scaleY,
textureView.getWidth() / 2f, textureView.getHeight() / 2f);
textureView.setTransform(matrix);
}
我有一个对话框设计,相机预览在纵向模式下应该是横向的(见图片)。我通过 getOptimalPreviewSize() 正确选择了预览大小,当我不使用 setDisplayOrientation(90) 时,我得到这个:
当我使用 setDisplayOrientation(90) 时,我得到这个:
有人知道如何解决吗? android能做那种事吗?
回答(感谢 Eddy Talvala):
由于 xml 中的摄像头视图是水平的,因此您无法纵向处理整个摄像头图片,因此您应该裁剪。我决定水平放置并裁剪底部的所有内容:
| |
| _________ | _____
|| || | |
|| || | ^-^ |
|| ______ || | \_/ |
|| | ^-^ ||| | | |
|| |_\_/_ ||| |__|__|
|| image || sensor
|| ||
|| ||
||_________||
|| < O [] ||
|___________|
device
您应该使用 TextureView 指定适合和裁剪的矩阵。类似的东西 (kotlin):
fun camInit() {
val camParam = camera.parameters
val optimalSize = getOptimalPortraitPreviewSize(camParam.supportedPreviewSizes, width, height)
camParam.setPreviewSize(optimalSize.width, optimalSize.height)
camera.parameters = camParam
camera.setPreviewTexture(surface)
val scaleDelta = optimalSize.height - preview.width // for portrait
val scaleY: Float = (optimalSize.width.toFloat() - scaleDelta) / preview.height // for portrait
val matrix = Matrix()
matrix.setScale(1f, scaleY) // 1f cause we fit horizontally
preview.setTransform(matrix)
camera.startPreview()
}
请注意,对于纵向模式,您应该使用特定的 getOptimalProtraitPreviewSize(),因为每个人都使用标准函数来获得相机预览的最佳尺寸(使用 ASPECT_TOLERANCE 和其他东西)可以 return 你一个尺寸分辨率小。尝试这样的事情:
fun getOptimalPortraitPreviewSize(sizes: List<Camera.Size>, previewWidth: Int) {
var selectedSize: Camera.Size = sizes[0]
for (size in sizes) {
// use size's height cause of portrait
val currentSizeWidthDelta = abs(size.height - previewWidth)
val selectedSizeWidthDelta = abs(selectedSize.height - previewWidth)
if (currentSizeWidthDelta < selectedSizeWidthDelta) selectedSize = size
}
return selectedSize
}
图像传感器的长边与设备的长边对齐。
(bad ascii art):
____________
| |
| _________ | _____
|| || | |
|| || | |
|| || | |
|| || | |
|| || |_____|
|| || sensor
|| || physical
|| || orientation
||_________||
|| < O [] ||
|___________|
device
鉴于此,您无法像这样绘制图像:
_____________
| |
| _________ | _____
|| || | |
|| || | |
|| ______ || | |
|| | ||| | |
|| |______||| |_____|
|| image || sensor
|| ||
|| ||
||_________||
|| < O [] ||
|___________|
device
除非您旋转图像(在这种情况下向上不是向上):
_____________
| |
| _________ | _____
|| || | |
|| || | ^-^ |
|| ______ || | \_/ |
|| | <-\__||| | | |
|| |_<-/__||| |__|__|
|| image || sensor
|| ||
|| ||
||_________||
|| < O [] ||
|___________|
device
或者你水平拉伸图像,看起来很糟糕:
_____________
| |
| _________ | _____
|| || | |
|| || | ^-^ |
|| ______ || | \_/ |
|| | ^___^||| | | |
|| |___|__||| |__|__|
|| image || sensor
|| ||
|| ||
||_________||
|| < O [] ||
|___________|
device
或者您裁剪图像的一部分,这会大大降低 FOV:
_____________
| |
| _________ | _____
|| || | |
|| || | ^-^ |
|| ______ || | \_/ |
|| | ^-^ ||| | | |
|| |_\_/__||| |__|__|
|| image || sensor
|| ||
|| ||
||_________||
|| < O [] ||
|___________|
device
因为图像传感器是横向的,而 phone 是横向的,所以如果不发生这三种情况之一,就无法在纵向中放置横向预览 UI。您要么需要在人像 UI 中进行人像预览,要么需要裁剪图像。这不是 Android 的限制,它只是几何形状的限制。
如果你想裁剪,你可能想要将相机数据发送到 SurfaceTexture,然后在 OpenGL 中裁剪,如果你想要实时预览。
如果您使用的是 Camera2 API。当通过 ChooseOptimalSize 获取 mPreviewSize 时,您可以交换宽度和高度。 (宽度和高度来自您的视图)。 然后
textureView.setAspectRatio(mPreviewSize.Height, mPreviewSize.Width);
创建预览会话时
texture.SetDefaultBufferSize(mPreviewSize.Width, mPreviewSize.Height);
然后你将预览正常图像
感谢@Eddy Talvala 和@blinker!我使用我的功能来正确更新任何方向的尺寸:
public void invalidateSize(TextureView textureView) {
Point cameraResolution = getCameraResolution();
int previewRotation = getPreviewRotation();
// Camera and preview on screen in the opposite orientation
if (previewRotation == 90 || previewRotation == 270) {
//noinspection SuspiciousNameCombination
cameraResolution = new Point(cameraResolution.y, cameraResolution.x);
}
float cameraAspectRatio = cameraResolution.x / (float) cameraResolution.y;
float textureAspectRatio = textureView.getWidth() / (float) textureView.getHeight();
float scaleX = cameraAspectRatio / textureAspectRatio;
float scaleY = textureAspectRatio / cameraAspectRatio;
if (scaleX > scaleY) {
scaleY = 1;
} else {
scaleX = 1;
}
Matrix matrix = new Matrix();
matrix.setScale(scaleX, scaleY,
textureView.getWidth() / 2f, textureView.getHeight() / 2f);
textureView.setTransform(matrix);
}