Java 计算最佳相机预览尺寸的正确方法保持纵横比

Java 计算最佳相机预览尺寸的正确方法保持纵横比,java,android,android-camera,aspect-ratio,cwac-camera,Java,Android,Android Camera,Aspect Ratio,Cwac Camera,问题 如何通过编程准确地确定以设备屏幕大小显示相机预览的应用程序的最佳预览大小?(或者在任何可变尺寸的视图中) 在您尝试将此问题标记为上百万个其他纵横比相关问题中的一个问题的副本之前,请理解,我正在搜索一个不同于通常可用的解决方案。我问这个问题是因为我读了这么多的“答案”,但它们都指向了一个我认为不完整的解决方案(正如我将在这里描述的那样,可能存在缺陷)。如果这不是一个缺陷,那么请帮助我理解我做错了什么 我读过很多关于应用程序如何选择预览大小的不同实现,其中大多数都采用了一种我称之为“足够接近”

问题

如何通过编程准确地确定以设备屏幕大小显示相机预览的应用程序的最佳预览大小?(或者在任何可变尺寸的视图中)

在您尝试将此问题标记为上百万个其他纵横比相关问题中的一个问题的副本之前,请理解,我正在搜索一个不同于通常可用的解决方案。我问这个问题是因为我读了这么多的“答案”,但它们都指向了一个我认为不完整的解决方案(正如我将在这里描述的那样,可能存在缺陷)。如果这不是一个缺陷,那么请帮助我理解我做错了什么

我读过很多关于应用程序如何选择预览大小的不同实现,其中大多数都采用了一种我称之为“足够接近”的方法(在这种方法中,您可以通过从屏幕分辨率的比率中减去大小选项的比率来决定哪个选项是最好的,并找到具有最低值的选项)。这种方法似乎不能确保它选择了最好的选项,但却确保它不会选择最差的选项

例如,如果我尝试在屏幕分辨率为
720x1184
的设备上迭代每个可用的预览大小,并显示预览全屏(
720x1184
),以下是相机预览的结果(按最接近
abs格式屏幕分辨率的选项排序)(尺寸比选项-屏幕分辨率)
)。所有尺寸均来自)。(“结果”来自使用静态圆的测试用例的视觉观察,该静态圆出现在相机的取景器中,并且不是编程的)

如果不在另一台设备上运行,这将不是一个合适的Android代码测试。以下是在屏幕分辨率为800x1216的设备上显示相机预览并以相同分辨率显示预览的结果(
800x1216

“足够接近”方法(假设比率中的任何增量等于或小于
1.4d
是可接受的)将在两台设备上返回
1920x1080
,如果通过从最低到最高的值进行迭代。如果迭代从最高到最低的值,它将为DeviceA选择
176x144
,为DeviceB选择
176x144
。这两个选项(尽管“足够接近”)都不是最佳选项

问题

在研究上述结果时,我如何通过编程推导出“看起来不错”的值?我无法使用“足够接近”的方法获得这些值,因此我误解了屏幕大小、显示预览的视图和预览大小之间的关系。我错过了什么

           Screen dimensions      = 720x1184
             View dimensions      = 720x1184
Screen and View Aspect Ratio      = 0.6081081081081081
Best Preview Size ratio (720x480) = 1.5  
为什么最佳选项不是具有最低比率增量的值? 结果是令人惊讶的,因为其他人似乎认为最好的选择是通过计算比率的最小差异,但我所看到的是最好的选择似乎是在所有选项的中间。并且它们的宽度更接近将显示预览的视图的宽度

基于上述观察结果(最佳选项不是比率增量最低的值),我开发了这个算法,迭代所有可能的预览大小,检查是否满足我的“足够接近”标准,存储满足此条件的大小,并最终找到至少大于或等于提供的宽度的值

public static Size getBestAspectPreviewSize(int displayOrientation,
                                            int width,
                                            int height,
                                            Camera.Parameters parameters,
                                            double closeEnough) {


    double targetRatio=(double)width / height;
    Size bestSize = null;

    if (displayOrientation == 90 || displayOrientation == 270) {
        targetRatio=(double)height / width;
    }

    List<Size> sizes=parameters.getSupportedPreviewSizes();
    TreeMap<Double, List> diffs = new TreeMap<Double, List>();


    for (Size size : sizes) {

        double ratio=(double)size.width / size.height;

        double diff = Math.abs(ratio - targetRatio);
        if (diff < closeEnough){
            if (diffs.keySet().contains(diff)){
                //add the value to the list
                diffs.get(diff).add(size);
            } else {
                List newList = new ArrayList<Camera.Size>();
                newList.add(size);
                diffs.put(diff, newList);
            }

            Logging.format("usable: %sx%s %s", size.width, size.height, diff);
        }
    }

    //diffs now contains all of the usable sizes
    //now let's see which one has the least amount of
    for (Map.Entry entry: diffs.entrySet()){
        List<Size> entries = (List)entry.getValue();
        for (Camera.Size s: entries) {

            if (s.width >= width && s.height >= width) {
                bestSize = s;
            }
            Logging.format("results: %s %sx%s", entry.getKey(), s.width, s.height);
        }
    }

    //if we don't have bestSize then just use whatever the default was to begin with
    if (bestSize==null){
        if (parameters.getPreviewSize()!=null){
            bestSize = parameters.getPreviewSize();
            return bestSize;
        }

        //pick the smallest difference in ratio?  or pick the largest resolution?
        //right now we are just picking the lowest ratio difference
        for (Map.Entry entry: diffs.entrySet()){
            List<Size> entries = (List)entry.getValue();
            for (Camera.Size s: entries) {
                if (bestSize == null){
                    bestSize = s;
                }
            }
        }
    }

    return bestSize;
}
public静态大小getBestAspectPreviewSize(int-displayOrientation,
整数宽度,
整数高度,
摄像机,参数,
双倍(足够近){
双目标率=(双)宽度/高度;
大小bestSize=null;
如果(显示方向==90 | |显示方向==270){
targetRatio=(双)高/宽;
}
列表大小=参数。getSupportedPreviewSizes();
TreeMap diff=新的TreeMap();
用于(尺寸:尺寸){
双倍比率=(双倍)size.width/size.height;
双差=数学绝对值(比率-目标比率);
如果(差<足够近){
if(diff.keySet()包含(diff)){
//将值添加到列表中
差异获取(差异)、添加(大小);
}否则{
List newList=newarraylist();
添加(大小);
diff.put(diff,newList);
}
Logging.format(“可用:%sx%s%s”,size.width,size.height,diff);
}
}
//Diff现在包含所有可用大小
//现在让我们看看哪一个有最少的
对于(Map.Entry:diff.entrySet()){
List entries=(List)entry.getValue();
用于(Camera.Size s:条目){
如果(s.width>=宽度和s.height>=宽度){
最佳尺寸=s;
}
格式(“结果:%s%sx%s”,entry.getKey(),s.width,s.height);
}
}
//如果我们没有bestSize,那么只需使用默认值即可
if(bestSize==null){
if(parameters.getPreviewSize()!=null){
bestSize=parameters.getPreviewSize();
返回最佳大小;
}
//选择最小的比率差异?还是选择最大的分辨率?
//现在我们只是选择最低的比率差
对于(Map.Entry:diff.entrySet()){
List entries=(List)entry.getValue();
用于(Camera.Size s:条目){
if(bestSize==null){
最佳尺寸=s;
}
}
}
}
返回最佳大小;
}
显然,这个算法不知道如何选择最好的选项,只是知道而已
           Screen dimensions      = 720x1184
             View dimensions      = 720x1184
Screen and View Aspect Ratio      = 0.6081081081081081
Best Preview Size ratio (720x480) = 1.5  
public static Size getBestAspectPreviewSize(int displayOrientation,
                                            int width,
                                            int height,
                                            Camera.Parameters parameters,
                                            double closeEnough) {


    double targetRatio=(double)width / height;
    Size bestSize = null;

    if (displayOrientation == 90 || displayOrientation == 270) {
        targetRatio=(double)height / width;
    }

    List<Size> sizes=parameters.getSupportedPreviewSizes();
    TreeMap<Double, List> diffs = new TreeMap<Double, List>();


    for (Size size : sizes) {

        double ratio=(double)size.width / size.height;

        double diff = Math.abs(ratio - targetRatio);
        if (diff < closeEnough){
            if (diffs.keySet().contains(diff)){
                //add the value to the list
                diffs.get(diff).add(size);
            } else {
                List newList = new ArrayList<Camera.Size>();
                newList.add(size);
                diffs.put(diff, newList);
            }

            Logging.format("usable: %sx%s %s", size.width, size.height, diff);
        }
    }

    //diffs now contains all of the usable sizes
    //now let's see which one has the least amount of
    for (Map.Entry entry: diffs.entrySet()){
        List<Size> entries = (List)entry.getValue();
        for (Camera.Size s: entries) {

            if (s.width >= width && s.height >= width) {
                bestSize = s;
            }
            Logging.format("results: %s %sx%s", entry.getKey(), s.width, s.height);
        }
    }

    //if we don't have bestSize then just use whatever the default was to begin with
    if (bestSize==null){
        if (parameters.getPreviewSize()!=null){
            bestSize = parameters.getPreviewSize();
            return bestSize;
        }

        //pick the smallest difference in ratio?  or pick the largest resolution?
        //right now we are just picking the lowest ratio difference
        for (Map.Entry entry: diffs.entrySet()){
            List<Size> entries = (List)entry.getValue();
            for (Camera.Size s: entries) {
                if (bestSize == null){
                    bestSize = s;
                }
            }
        }
    }

    return bestSize;
}
1184.0/720.0 = 1.6444444

Res         Ratio         Delta in ratios  Result       
----------------------------------------------------   
176/144   = 1.222 ( 0.422) (vertically stretched)
352/288   = 1.222 ( 0.422) (vertically stretched)
320/240   = 1.333 ( 0.311) (vertically stretched)
640/480   = 1.333 ( 0.311) (vertically stretched)
720/480   = 1.500 ( 0.144) (vertically stretched)
800/480   = 1.666 (-0.016) (looks good)
640/360   = 1.777 (-0.126) (horizontally squashed)
1280/720  = 1.777 (-0.126) (slight horizontal squash)
1920/1080 = 1.777 (-0.126) (slight horizontal squash)