安卓相机,如何获取最佳尺寸、预览尺寸、图片尺寸、视图尺寸,避免图像失真。

20

当使用takepicture方法将OpenGL视图和Android相机混合以获得两者的图像时,图像会变形。我检查后发现,相机图片的大小设置为640X480,而OpenGL视图和相机预览都设置为1280x720。

因此,我将相机图片大小设置为1280x720,结果完美无缺。但是无法在代码中设置大小,因为每个Android设备都不同,并且每个设备的预览大小和图片大小设置都必须从支持的大小列表中选择。

考虑到这里有三个变量,即活动布局的屏幕大小、相机预览大小和图片大小,最佳方法是什么?

是最好使用match_parent或fitXY作为FrameLayout大小,并只使用两个变量,即预览大小和图片大小吗?

请注意,预览大小和图片大小中的多个宽度和高度组合是相同的。例如,1280 x 720存在于预览和图片中,是否总是存在匹配的大小?

 List<Size> previewSizes = mCamera.getParameters().getSupportedPreviewSizes();

 List<Size> imageSizes = mCamera.getParameters().getSupportedPictureSizes();

 // layout in the activity that the cameraView will placed in
 int layoutWidth = frameLayout.getWidth();
 int layoutHeight = frameLayout.getHeight();

例如,在一款安卓平板电脑上,针对使用的三个变量进行测量后,这些是结果:

活动中布局视图组的大小作为测量结果。

 1280 x 736

支持的图片尺寸

 320 x 240
 640 x 480
 1024 x 768
 1280 x 720 << best size in my example to use
 1280 x 768
 1280 x 920
 1600 x 1200
 2048 x 1536
 2560 x 1440
 2560 x 1536
 2560 x 1920  << native resolution of hardware camera

支持的预览尺寸

 176 x 144
 320 x 240
 352 x 288
 480 x 320
 480 x 368
 640 x 480
 800 x 480
 800 x 600
 864 x 480
 864 x 576
 960 x 540
 1280 x 720 << best size in my example to use
 1280 x 768
 1280 x 960

这会生成“未绑定有效相机”错误吗? - Keith
mCamera 是如何构建的? - Keith
4个回答

19

通常情况下,图片比例和预览比例是相对应的,但并不总是如此。您可以确信其中至少有一些是经典的 4:3 比例(例如 640x480)。16:9 的支持也很普遍。

屏幕可能具有不同的宽高比。为了正确填充其以摄像头图像,请添加黑边(与 YouTube 中使用的方法相同)。或者,您可以裁剪摄像头图像以填满整个屏幕。

请注意,设备技术规格中报告的屏幕尺寸并不总是实际可用于图像显示的大小。例如,系统菜单、标题栏等可能占用屏幕资源。在某些设备上提供了 沉浸式模式,其行为取决于系统版本。您可以期待 Android 的未来发展(例如第二屏幕支持)将使游戏更加 有趣

因此,针对您的特定问题的答案:

最好使用 match_parent 或 fitXY 作为 FrameLayout 大小并仅使用两个变量,即预览大小和图片大小吗? - 不是

是否总是存在两种尺寸的匹配? - 是的,但也许这种尺寸不是最优的。

即使您的相机支持不同的“宽”图片尺寸,1280x720 的预览和 2560x1440 的图片可能是最佳匹配,都恰好为 16:9。通常,在小尺寸下图片的质量并不比同样大小的预览质量显著更好,因此,如果您真正需要的是 1280x720,则可以选择保存 预览帧

如果您可以放弃 takePicture(),您的应用程序将更加响应快速。


4
我认为获取预览大小和图片大小的最佳方式是将相机设置为支持所有Android设备。这意味着您决定设置的预览大小和图片大小适合所有设备支持的流行格式,例如1280x720等。
以下代码是我使用的示例。
// start preview with new settings
    try {
        // set preview size and make any resize, rotate or
        // reformatting changes here
        Camera.Parameters parameters = mCamera.getParameters();

        for (Camera.Size size : parameters.getSupportedPictureSizes()) {
            // 640 480
            // 960 720
            // 1024 768
            // 1280 720
            // 1600 1200
            // 2560 1920
            // 3264 2448
            // 2048 1536
            // 3264 1836
            // 2048 1152
            // 3264 2176
            if (1600 <= size.width & size.width <= 1920) {
                parameters.setPreviewSize(size.width, size.height);
                parameters.setPictureSize(size.width, size.height);
                break;
            }
        }
        // Set parameters for camera
        CustomCamera.mCamera.setParameters(parameters);

        Camera.Size size = CustomCamera.mCamera.getParameters().getPictureSize();

        mCamera.setPreviewDisplay(mHolder);
        mCamera.startPreview();
    } catch (Exception e) {
        e.printStackTrace();
    }

Best size 取决于相机的像素数。因此,如果使用相机,建议使用默认设置。


嗨,老兄,你的代码起作用了,但是在if条件内部我必须使用另一种方法:if (size.width <= 800) { - stackflow
嗨,它运行良好,但当我在预览中使用此图像大小时,预览和结果是不同的。它给了比我捕获的更多的图像。 - saibaba vali
2
.getSupportedPictureSizes()和.getSupportedPreviewSizes()可以不同(通常是这样的) - mariusz_latarnik01

2
可能的算法:
1)获取输出区域宽高比(=宽度/高度)
2)遍历支持的尺寸:
选择最接近宽高比(±5%)的最大尺寸 如果没有接近的或分辨率太低 - 只需使您绘制帧的表面具有与最大支持尺寸相同的宽高比,并将其居中在父视图中
然而,存在预览尺寸和图片尺寸。如果您希望它们具有相同的宽高比 - 那么更好的选择是在1)中使用所选图片尺寸的宽高比。这样做的话,您可能需要将您的表面居中在父视图中。
希望能对您有所帮助)

你是什么意思?比率(=宽度/高度)2) - Neon Warge
1
纵横比=宽度/高度。有什么不明显的吗? - Sam
我在你的“2)”处迷失了,你只是在枚举你的观点。 ;) - Neon Warge
现在不再总是建议选择最大的一个,因为这很容易产生1500万像素的图像,对于大多数应用来说是过度的。 - anneb
@anneb 取决于“最佳大小”的定义。 - Sam
如果我从列表中选择更高的预览和图片质量,我会遇到空白屏幕。 - Arul

0

我曾遇到同样的问题,后来在这里找到了一个非常好的解决方案:http://www.java2s.com/example/android/camera/get-optimal-preview-size-by-width-and-height.html

在调用此函数之前,我只需通过交换宽度和高度来处理摄像机的横向模式:

public static Camera.Size getOptimalPreviewSize(
        List<Camera.Size> sizes, int w, int h) {
    // Use a very small tolerance because we want an exact match.
    final double ASPECT_TOLERANCE = 0.1;
    double targetRatio = (double) w / h;
    if (sizes == null)
        return null;

    Camera.Size optimalSize = null;/*  w  w  w  .ja va  2 s .c  om*/

    // Start with max value and refine as we iterate over available preview sizes. This is the
    // minimum difference between view and camera height.
    double minDiff = Double.MAX_VALUE;

    // Target view height
    int targetHeight = h;

    // Try to find a preview size that matches aspect ratio and the target view size.
    // Iterate over all available sizes and pick the largest size that can fit in the view and
    // still maintain the aspect ratio.
    for (Camera.Size size : sizes) {
        double ratio = (double) size.width / size.height;
        if (Math.abs(ratio - targetRatio) > ASPECT_TOLERANCE)
            continue;
        if (Math.abs(size.height - targetHeight) < minDiff) {
            optimalSize = size;
            minDiff = Math.abs(size.height - targetHeight);
        }
    }

    // Cannot find preview size that matches the aspect ratio, ignore the requirement
    if (optimalSize == null) {
        minDiff = Double.MAX_VALUE;
        for (Camera.Size size : sizes) {
            if (Math.abs(size.height - targetHeight) < minDiff) {
                optimalSize = size;
                minDiff = Math.abs(size.height - targetHeight);
            }
        }
    }

    return optimalSize;
}

...
// get optional sizes list
...

    if (SensorOrientation == 90 || SensorOrientation == 270) {
        // SWAP
        int tmp = mwidth;
        mwidth = mheight;
        mheight = tmp;
    }

    return getOptimalPreviewSize(sizes, mheight, mwidth);

这个是如何选择高度和宽度的? - alext

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