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.
- Textura transparente semi-transparente no funciona
- Cómo tomar la instantánea de RTSP streaming con LibVLC para Android
- Reproducción de un video después de que un video en TextureView de Android tenga problemas
- Android TextureView / Dibujo / Rendimiento de la pintura
- Se ha abandonado BufferQueue: Al reproducir vídeo con TextureView
- Obtener Bitmap de TextureView eficientemente
- ¿Es esto posible tener la opinión de la textura para la cámara en forma circular para el androide?
- Android SurfaceTexture.detachFromGLContext Error durante detachFromGLContext
- ¿Cómo es que una vista previa de la cámara en una vista de textura es mucho más borrosa que en una vista de superficie?
- Los métodos SurfaceTexture no siempre se utilizan en Android 7.0
- TextureView setBackgroundColor no funciona
- El video de TextureView que se reproduce se vuelve negro después de onPause
- ¿Cómo utilizo las clases de "superficie" de Android?
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
deBitmap
a suSurfaceView
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 DrawerLayout no muestra el icono del indicador derecho
- ¿Funciona BluetoothLeAdvertiser en un Nexus 5 con Android 5.0?