Camera setDisplayOrientation () en el modo de retrato rompe la relación de aspecto

Estoy intentando que las previsualizaciones de la cámara funcionen correctamente en el modo vertical, donde la actividad en sí se le permite cambiar la orientación normalmente (es decir, no se bloquea en el paisaje).

El uso de setDisplayOrientation() rompe seriamente el comportamiento de las previsualizaciones, sin embargo.

Esto puede ser demostrado por el propio ApiDemos de Google. La primera imagen se basa en CameraPreview de la edición android-17 del proyecto de ejemplo de ApiDemos , donde el único cambio que hice fue eliminar android:orientation="landscape" de la entrada de esta actividad en el manifiesto. La siguiente imagen es una captura de pantalla de lo que aparece en la pantalla de un Nexus 4, con Android 4.2, con la cámara apuntando a un papel de 8,5 ":

Vista previa de Nexus 4 ApiDemos, modo de retrato

Lo que no es obvio de esa vista previa es que se gira 90 grados. Los pies vestidos de calcetín que ves en la extrema derecha de la imagen estaban realmente debajo del cuadrado.

La solución para esto, al menos para el modo horizontal, es usar setDisplayOrientation() . Pero, si modifica la clase interna Preview de CameraPreview para que mCamera.setDisplayOrientation(90); , usted consigue esto:

Nexus 4 Vista previa de ApiDemos, Modo de retrato, con setDisplayOrientation (90)

En particular, el cuadrado ya no es cuadrado.

Esto utiliza el mismo tamaño de SurfaceView que antes. He reproducido este escenario con algún otro código, fuera de ApiDemos , y obtengo el mismo comportamiento con TextureView en ese código.

Pensé brevemente que el problema podría ser que getSupportedPreviewSizes() podría devolver valores diferentes basados ​​en setDisplayOrientation() , pero una prueba rápida sugiere que este no es el caso.

Alguien tiene alguna idea de cómo obtener setDisplayOrientation() para trabajar en modo retrato sin destruir la relación de aspecto de las imágenes empujadas a la vista previa?

¡Gracias!

OK, creo que me di cuenta de esto. Había algunas piezas en el rompecabezas proverbial, y le agradezco @ kcoppock por la visión que me ayudó a resolver algo de esto.

En primer lugar, debe tener en cuenta la orientación al determinar qué tamaño de vista previa elegir. Por ejemplo, el getOptimalPreviewSize() de CameraPreview de ApiDemos es ajeno a la orientación, simplemente porque su versión de esa aplicación tiene la orientación bloqueada en el paisaje. Si desea dejar flotar la orientación, debe invertir la relación de aspecto de destino para que coincida. Por lo tanto, donde getOptimalPreviewSize() tiene:

 double targetRatio=(double)width / height; 

también necesitas:

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

donde displayOrientation es un valor de 0 a 360, que estoy determinando a partir de unas 100 líneas de algún código seriamente feo, por lo que estoy envolviendo todo esto en un componente reutilizable que publicaré en breve. Ese código se basa en el setDisplayOrientation() JavaDocs y esta respuesta StackOverflow .

(BTW, si usted está leyendo esto después del 1 de julio 2013, y usted no ve un acoplamiento en esta respuesta a ese componente, publicar un comentario para recordarme, como lo olvidé)

En segundo lugar, debe tener en cuenta esa orientación de pantalla al controlar la relación de aspecto del SurfaceView / TextureView que está utilizando. La actividad de ApiDemos de ApiDemos tiene su propio Preview ViewGroup que maneja la relación de aspecto, y allí es necesario invertir la relación de aspecto para usar en el retrato:

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

donde displayOrientation es el mismo valor ( 90 y 270 son retrato y retro-retrato respectivamente, y tenga en cuenta que no he intentado conseguir retroceso-retrato o reverso-paisaje para trabajar, por lo que puede ser más ajustes requeridos).

En tercer lugar – y uno que me parece irritante – debe iniciar la vista previa antes de llamar a setPictureSize() en el Camera.Parameters . De lo contrario, es como si la relación de aspecto de la imagen se aplica a los marcos de vista previa, atornillar las cosas.

Así que solía tener código similar al siguiente:

 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(); 

y eso está mal. Lo que realmente necesita es:

 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); 

Sin ese cambio, obtendría una relación de aspecto estirada, incluso en el paisaje. Esto no fue un problema para CameraPreview de ApiDemos , ya que no estaban tomando fotos, y por eso nunca llamaron setPictureSize() .

Pero, hasta ahora, en un Nexus 4, ahora tengo proporciones de aspecto cuadrado para el retrato y el paisaje, con una orientación flotante (es decir, no bloqueado al paisaje).

Voy a tratar de recordar para corregir esta pregunta si me encuentro con otros hacks requeridos por otros dispositivos, además de vincular a mi CameraView / CameraFragment componentes cuando se liberan.


ACTUALIZACIÓN # 1

Bueno, por supuesto, no podría ser casi tan simple. Todos los derechos reservados

La solución para el problema donde setPictureSize() atornilla la relación de aspecto funciona … hasta que usted toma una imagen. En ese momento, la vista previa cambia a la relación de aspecto incorrecta.

Una posibilidad sería limitar las imágenes a las que tienen la misma relación de aspecto (o muy cerca) con la vista previa, por lo que el hipo no existe. No me gusta esa respuesta, ya que el usuario debe ser capaz de obtener el tamaño de imagen que la cámara ofrece.

Puede limitar el alcance del daño mediante:

  • Sólo actualizar el tamaño de la imagen, etc de la Camera.Parameters antes de tomar la foto
  • Revertir a la Camera.Parameters antes de tomar fotografías.Parameters después de tomar la foto

Esto todavía presenta la relación de aspecto incorrecta durante los momentos mientras se toma la fotografía. La solución a largo plazo para este – creo – será reemplazar temporalmente la vista previa de la cámara con una imagen fija (el último marco de vista previa) mientras se toma la foto. Lo intentaré eventualmente.


UPDATE # 2 : v0.0.1 de mi CWAC-Cámara de la biblioteca está ahora disponible, incorporando el citado código.

Ok chicos, me estaba volviendo loco las últimas dos semanas sobre este problema f * * *. tan bien, pocas cosas que saber:

  • A veces solo obtienes bordes negros , si la barra de acción o botones suaves son visibles, lo más posible en algunos dispositivos, mi consejo: * no te preocupes * o te volverás loco, o cortar vistas previas, ambas no buenas …

los cambios de @CommonsWare son simplemente perfectos, pero no se pueden aplicar directamente a la vista de cámara desde las muestras API Level 8 (enlace a continuación), así que aquí está el diff :

para la relación de aspecto correcta en retrato hacer:
los métodos son los mismos, sólo aplique diff

 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); } 

en:

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

cambio:

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

a:

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

en:

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

cambio:

 double targetRatio = (double) w / h; 

a:

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

finalmente comenzando la cámara para el modo de retrato con algo como:

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

WUUHUUUU! Para mí, finalmente, ni problemas en la vista previa, y no hay problemas en el ahorro de imágenes. Debido a que mi ubicación de actividad está bloqueada, obtengo la orientación de la pantalla a través de OrientationChangedListener para guardar imágenes con la orientación correcta.

usando algo como:
http://www.androidzeitgeist.com/2013/01/fixing-rotation-camera-picture.html

Pero se prevé y se guardan correctamente.

Espero poder ayudar a mucha gente con eso, no es el código original de API Nivel 8 :
https://android.googlesource.com/platform/development/+/master/samples/ApiDemos/src/com/example/android/apis/graphics/CameraPreview.java donde se aplicaron los cambios.

Por favor, cambie la orientación de la cámara en su código y debe setDisplayorientation() not getDisplayOrientation()

 if (this.getResources().getConfiguration().orientation != Configuration.ORIENTATION_LANDSCAPE) { camera.setDisplayOrientation(0); } 
  • Cómo aumentar la resolución de la cámara cuando se utiliza IMAGE_CAPTURE intención
  • Política de privacidad de la cámara Android
  • ¿Alguna posibilidad de reducir el tiempo de obturación con el acceso a la cámara NDK de Android?
  • Cámara Android: diferencia entre PictureCallback, ShutterCallback y PreviewCallback
  • Resumen: Tome una foto utilizando Intención de la cámara y muestre la foto con la orientación correcta (funciona con todos los dispositivos)
  • Java.lang.OutOfMemoryError Una aplicación de cámara
  • Android - ¿Cómo puedo despertar el teléfono de un sueño duro para tomar una foto?
  • ¿Cómo obtengo el búfer de la cámara Android en bruto en C con JNI?
  • Android webview página recarga después de la captura de imagen y cargar en android 5 +
  • Procesamiento de vídeo en Android
  • Vista previa de la cámara
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.