我的应用程序需要以纵向模式捕获一些给定尺寸(比如说宽x高)的图片。
一般情况下,相机不支持我想要的尺寸(宽x高),因此我需要裁剪拍摄的图片以符合我的规格。
这个看似简单的程序让我对预览和图片尺寸和格式之间“良好”对应的问题感到疯狂。
让我解释:
我需要一个输出图像的格式(让我们给出一些数字:800x600),并且我必须以纵向屏幕方向拍照。我的相机默认处于横向模式,因此它拍摄的照片宽度远大于高度。但由于我想要纵向预览,我需要旋转图像,因此我得到的图像高度远大于宽度(我猜是原始图像的转置)。
在这种情况下,我需要从更大的垂直延伸的矩形中剪切水平延伸的矩形,我想通过具有可接受的大预览来做到这一点。
从图片中裁剪出图像的问题并没有吓到我(目前),最重要的问题是用户在预览中看到的内容与相机实际捕获的内容之间的匹配。
对于每个可能的电话,我需要:
- 根据所需图像格式选择合适的相机图片尺寸 - 根据图片尺寸和格式选择合适的相机预览尺寸。
- 隐藏将被裁剪的预览部分。
并且具有不失真和大预览的限制。
一般怎么做呢?
我的想法和尝试:
主要算法步骤为:
- 一旦知道所需的格式,即可获得最佳的图片尺寸
- 一旦知道图片尺寸即可获得最佳预览尺寸
- 隐藏预览中无法捕获的部分
- 裁剪图像
尝试过方法1)
A)我通过最小化面积差异来获得最佳图片尺寸(我还可以检查纵横比亲和力不是很重要)。 (Size是自定义类型,与Camera.Size不同)
public Size getOptimalPictureSize(List<Camera.Size> sizes) {
Size opt = new Size();
float objf = Float.MAX_VALUE;
float v;
for(Camera.Size s : sizes){
if(s.height<target_size.width || s.width<target_size.height)
continue;
v = (s.height-target_size.width)*s.width + (s.width-target_size.height)*target_size.width;
if(v<objf){
opt.width=s.width;
opt.height=s.height;
objf=v;
}
}
return opt;
}
B)我通过找到不同宽高比(相对于图片尺寸)之间的最佳折衷来获得最佳预览尺寸:
@Override
public Size getOptimalPreviewSize(Size picSize,List<android.hardware.Camera.Size> sizes) {
Size opt = new Size();
double objf = Double.MAX_VALUE;
double aspratio = picSize.getAspectRatio();
double v;
for(Camera.Size s : sizes){
v = Math.abs( ((double)s.width)/((double)s.height) - aspratio )/(Math.max(((double)s.width)/((double)s.height), aspratio));
if(v<objf){
objf=v;
opt.width=s.width;
opt.height=s.height;
}
}
return opt;
}
C) 隐藏仅显示可捕获部分的方法....(稍后讨论)
** 试用 2) **
A)我通过最小化最优函数来获得图片和预览尺寸,该函数同时权衡相机图像长宽比与所需长宽比之间的不匹配以及预览和图片长宽比之间的不匹配。
public void setOptimalCameraSizes(List<Camera.Size> preview_sizes,List<Camera.Size> picture_sizes,Size preview_out, Size picture_out) {
double objf=Double.MAX_VALUE;
double tmp;
for(Camera.Size pts : picture_sizes){
for(Camera.Size pws : preview_sizes){
tmp = percv(((double)pws.height)/((double)pws.width),target_size.getAspectRatio())
+ percv(((double)pws.width)/((double)pws.height),((double)pts.width)/((double)pts.height));
if(tmp<objf){
preview_out.set(pws.width, pws.height);
picture_out.set(pts.width, pts.height);
objf=tmp;
}
}
}
}
where
percv(a,b) = |a-b|/max(|a|,|b|) 测量相对偏差(因此是无量纲的)。
C) 一些隐藏方法...
好吧,这两种尺寸选择方法是我发现的最好的,并选择了好的尺寸,但是它们有一个来自相机横向方向的生理问题......它们只能产生垂直的矩形图像,这意味着当我绘制预览时,我可以得到两个案例:
1.我设置表面尺寸,以免预览图像变形 -> 由于高度巨大,这反映在图像可见的非常小的有效区域中(因此用户体验受到损害)
2.我设置了最大可能的宽度->我可以获得(这取决于预览纵横比)扭曲的预览,但比情况1大得多。
如何避免这些问题呢??
我认为是算法的 C) 阶段(隐藏阶段)的工作,我尝试:
trial 1:使相机预览超出屏幕尺寸。这将允许我对感兴趣的区域进行任意缩放并使屏幕裁剪预览。我尝试使用滚动视图,但它不起作用,我不知道为什么。拓扑很简单,根滚动视图和带有附加表面视图的 FrameLayout 内部,但表面总是填充屏幕,导致可怕的扭曲。
trial 2:捕获相机帧并通过重写 onPreviewFrame(.) 方法直接操作它们:我在锁定画布时遇到了一个神秘的错误(IllegalArgumentException)
我该如何解决这个问题?