问题
如何编程地并准确地确定在设备屏幕大小(或任何可变尺寸的视图内)显示相机预览的最佳预览大小?
在您试图将其标记为SO上数百万其他宽高比相关问题之一之前,请理解我正在寻找与通常可用解决方案不同的解决方案。 我提出这个问题是因为我已经阅读了很多“答案”,但它们都指向了一个我认为不完整的解决方案(并且可能存在缺陷,正如我将在这里描述的那样)。 如果这不是一个缺陷,请帮助我理解我做错了什么。
我已经阅读了很多关于应用程序如何选择预览大小的不同实现,并且其中大多数采用了我称之为“趋近”的方法(通过从尺寸选项的比率中减去屏幕分辨率的比率,并找到具有最低值的选项来确定最佳选项)。 这种方法似乎不能确保选择了最佳选项,但确保不会选择最差选项。
例如,如果我尝试在屏幕分辨率为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
。虽然这个值不是最差的选项,但也稍微有些压缩。我将在我的测试应用程序中运行他的代码,并进行测试用例以确定它是否也会轻微地压缩图像,因为我已经知道它会返回一个不太理想的尺寸。