15

我试图让相机预览在纵向模式下正常工作,其中允许活动本身正常更改方向(即,不被锁定为横向)。

但是,使用setDisplayOrientation()严重破坏了预览的行为。

这可以通过 Google 自己的 ApiDemos 来演示。第一个图像基于示例项目CameraPreviewandroid-17版本,ApiDemos我所做的唯一更改是android:orientation="landscape"从清单中的此活动条目中删除。下图是运行 Android 4.2 的 Nexus 4 显示屏上显示的屏幕截图,摄像头指向 8.5 英寸方形纸:

Nexus 4 ApiDemos 预览,纵向模式

从该预览中不明显的是它旋转了 90 度。您在图像最右侧看到的穿着袜子的脚实际上在正方形的下方。

至少对于横向模式,解决方案是使用setDisplayOrientation(). 但是,如果你修改Preview内部类CameraPreviewto have mCamera.setDisplayOrientation(90);,你会得到:

Nexus 4 ApiDemos 预览,纵向模式,带 setDisplayOrientation(90)

值得注意的是,正方形不再是正方形。

这使用与SurfaceView以前相同的大小。我已经用 之外的一些其他代码重现了这种情况ApiDemos,并且在该代码中我得到了相同的行为TextureView

我简要地认为,问题可能是getSupportedPreviewSizes()基于 可能返回不同的值setDisplayOrientation(),但快速测试表明情况并非如此。

任何人都知道如何setDisplayOrientation()在不破坏推送到预览的图像的纵横比的情况下以纵向模式工作?

谢谢!

4

3 回答 3

15

好的,我想我想通了。众所周知的难题有几个部分,我感谢@kcoppock 的洞察力帮助我解决了其中的一些问题。

首先,在确定要选择的预览尺寸时,您需要考虑方向。例如,getOptimalPreviewSize()from the CameraPreviewofApiDemos忽略方向,仅仅是因为他们的应用程序版本将方向锁定为横向。如果您希望让方向浮动,则需要反转目标纵横比以匹配。那么,哪里getOptimalPreviewSize()有:

double targetRatio=(double)width / height;

你还需要:

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

哪里displayOrientation是一个从 0 到 360 的值,我从大约 100 行一些非常丑陋的代码中确定,这就是为什么我将所有这些都包装在一个我将很快发布的可重用组件中。setDisplayOrientation()代码基于JavaDocs这个 StackOverflow 答案

(顺便说一句,如果您在 2013 年 7 月 1 日之后阅读此内容,并且在此答案中看不到该组件的链接,请发表评论以提醒我,因为我忘记了)

其次,在控制您使用的SurfaceView/的纵横比时,您需要考虑显示方向。TextureView来自的CameraPreview活动ApiDemos有自己Preview ViewGroup的处理纵横比,并且您需要反转纵横比以用于纵向:

    if (displayOrientation == 90
        || displayOrientation == 270) {
      previewWidth=mPreviewSize.height;
      previewHeight=mPreviewSize.width;
    }
    else {
      previewWidth=mPreviewSize.width;
      previewHeight=mPreviewSize.height;
    }

displayOrientation相同的值在哪里(90分别270是纵向和反向纵向,请注意,我没有尝试让反向纵向或反向横向工作,因此可能需要更多调整)。

第三——也是我觉得很气愤的一个——你必须在setPictureSize()调用Camera.Parameters. 否则,就好像图片的纵横比应用于预览帧,搞砸了。

所以我曾经有类似以下的代码:

Camera.Parameters parameters=camera.getParameters();
Camera.Size pictureSize=getHost().getPictureSize(parameters);

parameters.setPreviewSize(mPreviewSize.width, mPreviewSize.height);
parameters.setPictureSize(pictureSize.width, pictureSize.height);
parameters.setPictureFormat(ImageFormat.JPEG);

camera.setParameters(parameters);
camera.startPreview();

这是错误的。你真正需要的是:

Camera.Parameters parameters=camera.getParameters();
Camera.Size pictureSize=getHost().getPictureSize(parameters);

parameters.setPreviewSize(mPreviewSize.width, mPreviewSize.height);

camera.setParameters(parameters);
camera.startPreview();

parameters=camera.getParameters();
parameters.setPictureSize(pictureSize.width, pictureSize.height);
parameters.setPictureFormat(ImageFormat.JPEG);
camera.setParameters(parameters);

如果没有这种变化,即使在横向上,我也会得到一个拉伸的纵横比。这对CameraPreview的来说不是问题ApiDemos,因为他们没有拍照,所以他们从来没有打电话给setPictureSize()

但是,到目前为止,在 Nexus 4 上,我现在有纵向和横向的方形纵横比,具有浮动方向(即,未锁定为横向)。

如果我遇到其他设备所需的其他黑客攻击,我会尽量记住修改这个问题,并在它们发布时链接到我的CameraView/组件。CameraFragment


更新#1

嗯,当然,不可能这么简单。:-)

解决了setPictureSize()搞砸纵横比的问题……直到你拍照为止。此时,预览切换到错误的纵横比。

一种可能性是将图片限制为与预览具有相同(或非常接近)纵横比的图片,因此不存在打嗝。我不喜欢这个答案,因为用户应该能够获得相机提供的任何图片尺寸。

您可以通过以下方式限制损坏范围:

  • Camera.Parameters仅在拍照前更新图片大小等
  • 拍照Camera.Parameters后恢复到拍照前

在拍摄照片的瞬间,这仍然会显示错误的纵横比。对此的长期解决方案 - 我认为 - 将在拍摄照片时暂时用静止图像(最后一个预览帧)替换相机预览。我最终会试试这个。


更新#2我的 CWAC-Camera 库的 v0.0.1 现在可用,包含上述代码。

于 2013-06-16T12:31:00.760 回答
1

好吧,伙计们,在过去的两周里,我真的为这个 f* * * 问题发疯了。好吧,有几件事要知道:

  • 有时你只会看到黑色边框,如果操作栏或软按钮可见,这在某些设备上是可能的,我的建议: *不在乎* 否则你会发疯,或者剪切预览,两者都不好......

@CommonsWare 所做的更改非常完美,但不能直接应用于API 级别 8 示例中的 CameraPreview (链接如下),所以这里是差异

为正确的纵向比例做:
方法是一样的,只是应用差异

public void setCamera(Camera camera, boolean portrait) {
    mCamera = camera;
    this.portrait = portrait;
    if (mCamera != null) {
        mSupportedPreviewSizes = mCamera.getParameters().getSupportedPreviewSizes();
        requestLayout();
    }
}

public void switchCamera(Camera camera, boolean portrait) throws IOException {
   setCamera(camera, portrait);
   camera.setPreviewDisplay(mHolder);       
   Camera.Parameters parameters = camera.getParameters();
   parameters.setPreviewSize(mPreviewSize.width, mPreviewSize.height);
   requestLayout();

   camera.setParameters(parameters);
}

在:

@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {…}

改变:

int previewWidth = width;
int previewHeight = height;
if (mPreviewSize != null) {
      previewWidth = mPreviewSize.width;
      previewHeight = mPreviewSize.height;
}

到:

if (portrait && mPreviewSize != null) {
      previewWidth = mPreviewSize.height;
      previewHeight = mPreviewSize.width;
}
else if (mPreviewSize != null){
      previewWidth = mPreviewSize.width;
      previewHeight = mPreviewSize.height;
}

在:

private Size getOptimalPreviewSize(List<Size> sizes, int w, int h) {…}

改变:

double targetRatio = (double) w / h;

到:

double targetRatio = 1;
if (portrait) {
    targetRatio= (double) w / h;
}

最后启动相机以进行肖像模式,例如:

try {
     mCamera = openFrontFacingCameraGingerbread();
     if (portraitMode) {
       mCamera.setDisplayOrientation(90);
     }
     preview.setCamera(mCamera, portraitMode);
} catch (Exception e) {
     e.printStackTrace();
     handleCameraUnavailable();
}

呜呜呜!对我来说,预览也没有问题,保存图像也没有问题。因为我的活动位置被锁定,所以我通过 OrientationChangedListener 获取显示方向,以保存具有正确方向的图像。

使用类似的东西:
http ://www.androidzeitgeist.com/2013/01/fixing-rotation-camera-picture.html

但它们已正确预览和保存。

我希望我可以帮助很多人,API 级别 8 的原始代码
https ://android.googlesource.com/platform/development/+/master/samples/ApiDemos/src/com/example/android/应用了更改的apis/graphics/CameraPreview.java

于 2014-03-05T15:09:28.867 回答
-1

请更改代码上的相机方向,您setDisplayorientation()不应该getDisplayOrientation()

if (this.getResources().getConfiguration().orientation != Configuration.ORIENTATION_LANDSCAPE) 
{   
  camera.setDisplayOrientation(0);

} 
于 2015-04-08T05:40:25.203 回答