【发布时间】:2015-05-05 20:00:28
【问题描述】:
问题
您如何以编程方式准确确定以设备屏幕尺寸显示相机预览的应用程序的最佳预览尺寸? (或者在任何可变维度的视图中,真的)。
在您尝试将此标记为与其他数百万个宽高比相关问题之一的重复之前,请理解我正在寻找与通常可用的解决方案不同的解决方案。我问这个问题是因为我已经阅读了很多“答案”,但它们都指向我认为不完整的解决方案(并且可能存在缺陷,正如我将在此处描述的那样)。如果这不是缺陷,请帮助我理解我做错了什么。
我已经阅读了很多关于应用程序如何选择预览大小的不同实现,其中大多数采用我称之为“足够接近”的方法(您可以根据减去大小的比率来决定哪个选项最好从屏幕分辨率的比例中选择选项并找到具有最低值的选项)。这种方法似乎不能确保它选择最佳选项,但确保它不会选择最差选项。
例如,如果我尝试在屏幕分辨率为 720x1184 的设备上迭代每个可用的预览大小并全屏显示预览 (720x1184),这里是相机预览的结果(按以下选项排序)最接近屏幕分辨率的格式为abs(ratio of size option - screen resolution ratio))。所有尺寸均来自.getSupportedPreviewSizes)。 (“结果”来自使用测试用例的视觉观察,该测试用例使用出现在相机取景器中的静态圆圈,不是程序化的)
720.0/1184.0 = 0.6081081081081081
Res Ratio Delta in ratios Result
----------------------------------------------------
176/144 = 1.22222222222 (0.614114114114) (vertically stretched)
352/288 = 1.22222222222 (0.614114114114) (vertically stretched)
320/240 = 1.33333333333 (0.725225225225) (vertically stretched)
640/480 = 1.33333333333 (0.725225225225) (vertically stretched)
720/480 = 1.5 (0.891891891892) (vertically stretched)
800/480 = 1.66666666667 (1.05855855856) (looks good)
640/360 = 1.77777777778 (1.16966966967) (horizontally squashed)
1280/720 = 1.77777777778 (1.16966966967) (slight horizontal squash)
1920/1080 = 1.77777777778 (1.16966966967) (slight horizontal squash)
如果不在另一台设备上运行它,它就不是一个正确的 Android 代码测试。以下是在屏幕分辨率为 800x1216 的设备上显示相机预览并以相同分辨率显示预览的结果 (800x1216)
800/1216.0 = 0.657894736842
Res Ratio Delta in ratios Results
------------------------------------------------
176/144 = 1.22222222222 (0.56432748538) (looks vertically stretched)
352/288 = 1.22222222222 (0.56432748538) (looks vertically stretched)
480/368 = 1.30434782609 (0.646453089245) (looks vertically stretched)
320/240 = 1.33333333333 (0.675438596491) (looks vertically stretched)
640/480 = 1.33333333333 (0.675438596491) (looks vertically stretched)
800/600 = 1.33333333333 (0.675438596491) (looks vertically stretched)
480/320 = 1.5 (0.842105263158) (looks good)
720/480 = 1.5 (0.842105263158) (looks good)
800/480 = 1.66666666667 (1.00877192982) (looks horizontally squashed)
960/540 = 1.77777777778 (1.11988304094) (looks horizontally squashed)
1280/720 = 1.77777777778 (1.11988304094) (looks horizontally squashed)
1920/1080 = 1.77777777778 (1.11988304094) (looks horizontally squashed)
864/480 = 1.8 (1.14210526316) (looks horizontally squashed)
“足够接近”的方法(假设比率中的任何增量等于或小于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;
}
很明显,这个算法不知道如何选择最好的选项,只知道如何选择一个不像我见过的所有其他实现那样最差的选项。在改进算法以实际选择最佳选项之前,我需要了解实际看起来不错的尺寸与将显示预览的视图尺寸之间的关系。
我查看了CommonWares' camera-cwac 项目如何处理这个问题的实现,它似乎也使用了“足够接近”的算法。如果我将相同的逻辑应用于我的项目,那么我会得到不错的值,但不是“完美”的大小。两种设备我都收到了1920x1080。尽管该值不是最糟糕的选择,但它也被略微挤压了。我将在带有测试用例的测试应用程序中运行他的代码,以确定它是否也会稍微挤压图像,因为我已经知道它会返回一个不是最佳的尺寸。
【问题讨论】:
-
我认为您需要重新计算显示比例,然后再计算增量,因为显示器处于纵向模式。
标签: java android android-camera aspect-ratio cwac-camera