Vista previa de la cámara de corte para TextureView

Tengo un TextureView con un ancho fijo y la altura y quiero mostrar una vista previa de la cámara dentro de ella. Necesito recortar la vista previa de la cámara para que no se vea estirada dentro de mi TextureView. ¿Cómo hacer el cultivo? ¿Si necesito utilizar OpenGL, cómo atar la textura superficial a OpenGL y cómo hacer la cosecha con OpenGL?

public class MyActivity extends Activity implements TextureView.SurfaceTextureListener { private Camera mCamera; private TextureView mTextureView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_options); mTextureView = (TextureView) findViewById(R.id.camera_preview); mTextureView.setSurfaceTextureListener(this); } @Override public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) { mCamera = Camera.open(); try { mCamera.setPreviewTexture(surface); mCamera.startPreview(); } catch (IOException ioe) { // Something bad happened } } @Override public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) { mCamera.stopPreview(); mCamera.release(); return true; } @Override public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) { } @Override public void onSurfaceTextureUpdated(SurfaceTexture surface) { // Invoked every time there's a new Camera preview frame } } 

Además, después de hacer la vista previa correctamente, necesito poder leer en tiempo real los píxeles encontrados en el centro de la imagen recortada.

Siempre que la solución anterior por @Romanski funciona bien, pero se escala con el cultivo. Si necesita escalar para adaptarse, utilice la siguiente solución. Llame a updateTextureMatrix cada vez que cambie la vista de la superficie: es decir, en onSurfaceTextureAvailable y en los métodos onSurfaceTextureSizeChanged. Observe también que esta solución se basa en que la actividad ignora los cambios de configuración (es decir, android: configChanges = "orientation | screenSize | keyboardHidden" o algo así):

 private void updateTextureMatrix(int width, int height) { boolean isPortrait = false; Display display = getWindowManager().getDefaultDisplay(); if (display.getRotation() == Surface.ROTATION_0 || display.getRotation() == Surface.ROTATION_180) isPortrait = true; else if (display.getRotation() == Surface.ROTATION_90 || display.getRotation() == Surface.ROTATION_270) isPortrait = false; int previewWidth = orgPreviewWidth; int previewHeight = orgPreviewHeight; if (isPortrait) { previewWidth = orgPreviewHeight; previewHeight = orgPreviewWidth; } float ratioSurface = (float) width / height; float ratioPreview = (float) previewWidth / previewHeight; float scaleX; float scaleY; if (ratioSurface > ratioPreview) { scaleX = (float) height / previewHeight; scaleY = 1; } else { scaleX = 1; scaleY = (float) width / previewWidth; } Matrix matrix = new Matrix(); matrix.setScale(scaleX, scaleY); textureView.setTransform(matrix); float scaledWidth = width * scaleX; float scaledHeight = height * scaleY; float dx = (width - scaledWidth) / 2; float dy = (height - scaledHeight) / 2; textureView.setTranslationX(dx); textureView.setTranslationY(dy); } 

También necesita los siguientes campos:

 private int orgPreviewWidth; private int orgPreviewHeight; 

Inicializarlo en onSurfaceTextureAvailable mathod antes de llamar a updateTextureMatrix:

 Camera.Parameters parameters = camera.getParameters(); parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO); Pair<Integer, Integer> size = getMaxSize(parameters.getSupportedPreviewSizes()); parameters.setPreviewSize(size.first, size.second); orgPreviewWidth = size.first; orgPreviewHeight = size.second; camera.setParameters(parameters); 

Método getMaxSize:

 private static Pair<Integer, Integer> getMaxSize(List<Camera.Size> list) { int width = 0; int height = 0; for (Camera.Size size : list) { if (size.width * size.height > width * height) { width = size.width; height = size.height; } } return new Pair<Integer, Integer>(width, height); } 

Y la última cosa – que necesita para corregir la rotación de la cámara. Así que llamar al método setCameraDisplayOrientation en el método Activity onConfigurationChanged (y también hacer la llamada inicial en el método onSurfaceTextureAvailable):

 public static void setCameraDisplayOrientation(Activity activity, int cameraId, Camera camera) { Camera.CameraInfo info = new Camera.CameraInfo(); Camera.getCameraInfo(cameraId, info); int rotation = activity.getWindowManager().getDefaultDisplay().getRotation(); int degrees = 0; switch (rotation) { case Surface.ROTATION_0: degrees = 0; break; case Surface.ROTATION_90: degrees = 90; break; case Surface.ROTATION_180: degrees = 180; break; case Surface.ROTATION_270: degrees = 270; break; } int result; if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) { result = (info.orientation + degrees) % 360; result = (360 - result) % 360; // compensate the mirror } else { // back-facing result = (info.orientation - degrees + 360) % 360; } camera.setDisplayOrientation(result); Camera.Parameters params = camera.getParameters(); params.setRotation(result); camera.setParameters(params); } 

Simplemente calcule la relación de aspecto, genere una matriz de escala y aplíquela al TextureView. Con base en la relación de aspecto de la superficie y la relación de aspecto de la imagen de vista previa, la imagen de vista previa se recorta en la parte superior e inferior o izquierda y derecha. Otra solución que descubrí es que si abre la cámara antes de que SurfaceTexture esté disponible, la vista preliminar ya está escalada automáticamente. Sólo intenta mover mCamera = Camera.open (); A su función onCreate después de configurar el SurfaceTextureListener. Esto funcionó para mí en la N4. Con esta solución probablemente obtendrá problemas al girar de retrato a paisaje. Si necesita soporte de retrato y paisaje, ¡tome la solución con la matriz de escala!

 private void initPreview(SurfaceTexture surface, int width, int height) { try { camera.setPreviewTexture(surface); } catch (Throwable t) { Log.e("CameraManager", "Exception in setPreviewTexture()", t); } Camera.Parameters parameters = camera.getParameters(); previewSize = parameters.getSupportedPreviewSizes().get(0); float ratioSurface = width > height ? (float) width / height : (float) height / width; float ratioPreview = (float) previewSize.width / previewSize.height; int scaledHeight = 0; int scaledWidth = 0; float scaleX = 1f; float scaleY = 1f; boolean isPortrait = false; if (previewSize != null) { parameters.setPreviewSize(previewSize.width, previewSize.height); if (display.getRotation() == Surface.ROTATION_0 || display.getRotation() == Surface.ROTATION_180) { camera.setDisplayOrientation(display.getRotation() == Surface.ROTATION_0 ? 90 : 270); isPortrait = true; } else if (display.getRotation() == Surface.ROTATION_90 || display.getRotation() == Surface.ROTATION_270) { camera.setDisplayOrientation(display.getRotation() == Surface.ROTATION_90 ? 0 : 180); isPortrait = false; } if (isPortrait && ratioPreview > ratioSurface) { scaledWidth = width; scaledHeight = (int) (((float) previewSize.width / previewSize.height) * width); scaleX = 1f; scaleY = (float) scaledHeight / height; } else if (isPortrait && ratioPreview < ratioSurface) { scaledWidth = (int) (height / ((float) previewSize.width / previewSize.height)); scaledHeight = height; scaleX = (float) scaledWidth / width; scaleY = 1f; } else if (!isPortrait && ratioPreview < ratioSurface) { scaledWidth = width; scaledHeight = (int) (width / ((float) previewSize.width / previewSize.height)); scaleX = 1f; scaleY = (float) scaledHeight / height; } else if (!isPortrait && ratioPreview > ratioSurface) { scaledWidth = (int) (((float) previewSize.width / previewSize.height) * width); scaledHeight = height; scaleX = (float) scaledWidth / width; scaleY = 1f; } camera.setParameters(parameters); } // calculate transformation matrix Matrix matrix = new Matrix(); matrix.setScale(scaleX, scaleY); textureView.setTransform(matrix); } 

Usted podría manipular los byte[] data del byte[] data de onPreview() .

Creo que tendrás que:

  • Ponerlo en un mapa de bits
  • Hacer el recorte en el Bitmap
  • Hacer un poco de estiramiento / redimensionamiento
  • Y pasar el Bitmap de Bitmap a su SurfaceView

Esto no es una manera muy performant. Tal vez usted puede manipular el byte[] directamente, pero tendrá que lidiar con formatos de imagen como NV21.

La respuesta dada por @SatteliteSD es la más apropiada. Cada cámara sólo admite ciertos tamaños de vista previa que se establecen en HAL. Por lo tanto, si los tamaños de vista previa disponibles no son suficientes, entonces necesitará extraer datos de onPreview

Acabo de hacer una aplicación de trabajo donde tenía que mostrar una vista previa con x2 de la entrada real sin obtener un aspecto pixelado. Es decir. Necesitaba mostrar la parte central de una vista previa en vivo 1280×720 en un TextureView 640×360.

Esto es lo que hice.

Establecer la vista previa de la cámara a la resolución x2 de lo que necesitaba:

 params.setPreviewSize(1280, 720); 

A continuación, escala la vista de textura en consecuencia:

 this.captureView.setScaleX(2f); this.captureView.setScaleY(2f); 

Esto se ejecuta sin ningún contratiempo en los dispositivos pequeños.

Para las manipulaciones en tiempo real de las imágenes de vista previa de la cámara, OpenCV para Android está perfectamente adaptado. Encontrarás todas las muestras necesarias aquí: http://opencv.org/platforms/android/opencv4android-samples.html , y como verás funciona muy bien en tiempo real.

Descargo de responsabilidad: la creación de una lib de opencv en android puede ser bastante el dolor de cabeza dependiendo de cómo se experimentan con c ++ / opencv / ndk. En todos los casos, escribir código opencv nunca es simple, pero por otra parte es una biblioteca muy poderosa.

  • Android: reproduce el mismo video en varias vistas
  • Android: reproducir muchos videos simultáneamente
  • ¿Qué puedo hacer cuando el BufferQueue ha sido abandonado?
  • TextureView con vista previa de la cámara
  • Añadir etiquetas de texto en el archivo de salida de vídeo utilizando la API de la cámara2
  • Cómo dibujo Bitmap en TextureView
  • SurfaceView vs TextureView para la cámara?
  • Realice un ajuste de vídeo en un TextureView (Partes de él están fuera (no visibles)) con MediaPlayer
  • Cámara2 con un SurfaceView
  • Cómo reducir la latencia en la decodificación de video / avc de MediaCodec
  • Android - Cómo recortar la previsualización de la cámara con TextureView
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.