SetPreviewDisplay y setDisplayOrientation
Estoy sorprendido por el código de ejemplo de la cámara Android de OpenCV. Ellos hacen una clase personalizada que implementa SurfaceHolder.Callback
y poner la siguiente línea dentro del método surfaceChanged
:
mCamera.setPreviewDisplay(null);
La documentación de Android de setPreviewDisplay
explica:
- RuntimeException utilizando Camera.open ()
- Se ha superado el recuento de búfer de undequeued mínimo
- Cómo poner el marcador estable (círculo sólido en la imagen). ¿Cuál estará en la misma posición en el próximo marco de la cámara?
- seleccione la cámara frontal
- Camera.open () que bloquea el hilo de la interfaz de usuario
Este método debe ser llamado antes de startPreview (). La única excepción es que si la superficie de vista previa no se establece (o se establece en nulo) antes de que startPreview () se llame, este método se puede llamar una vez con un parámetro no nulo para establecer la superficie de vista previa. (Esto permite que la configuración de la cámara y la creación de la superficie ocurran en paralelo, ahorrando tiempo.) La superficie de vista previa no puede cambiar de otra forma mientras se está ejecutando la vista previa.
Inusualmente, el código de OpenCV nunca llama a setPreviewDisplay
con un setPreviewDisplay
no nulo. Funciona bien, pero cambiar la rotación de la imagen usando setDisplayOrientation
no funciona. Esta línea tampoco parece hacer nada, ya que obtengo los mismos resultados sin él.
Si llamo setPreviewDisplay
con SurfaceHolder suministrado a surfaceChanged
lugar de null
, la imagen gira, pero no incluye los resultados del procesamiento de la imagen. También recibo una IllegalArgumentException
al llamar a lockCanvas
más adelante.
¿Que esta pasando?
Aquí están las partes (posiblemente) más relevantes de su código, ligeramente simplificado y con métodos inline. Aquí está la versión completa .
Definición de clase
public abstract class SampleViewBase extends SurfaceView implements SurfaceHolder.Callback, Runnable {
Cuando se abre la cámara
mCamera.setPreviewCallbackWithBuffer(new PreviewCallback() { public void onPreviewFrame(byte[] data, Camera camera) { synchronized (SampleViewBase.this) { System.arraycopy(data, 0, mFrame, 0, data.length); SampleViewBase.this.notify(); } camera.addCallbackBuffer(mBuffer); } });
Cuando la superficie cambia
/* Now allocate the buffer */ mBuffer = new byte[size]; /* The buffer where the current frame will be copied */ mFrame = new byte [size]; mCamera.addCallbackBuffer(mBuffer); try { mCamera.setPreviewDisplay(null); } catch (IOException e) { Log.e(TAG, "mCamera.setPreviewDisplay/setPreviewTexture fails: " + e); } [...] /* Now we can start a preview */ mCamera.startPreview();
El método run
public void run() { mThreadRun = true; Log.i(TAG, "Starting processing thread"); while (mThreadRun) { Bitmap bmp = null; synchronized (this) { try { this.wait(); bmp = processFrame(mFrame); } catch (InterruptedException e) { e.printStackTrace(); } } if (bmp != null) { Canvas canvas = mHolder.lockCanvas(); if (canvas != null) { canvas.drawBitmap(bmp, (canvas.getWidth() - getFrameWidth()) / 2, (canvas.getHeight() - getFrameHeight()) / 2, null); mHolder.unlockCanvasAndPost(canvas); } } } Log.i(TAG, "Finishing processing thread"); }
- Cómo deshacerse de la cámara se congela (SurfaceView)?
- Configuración de la orientación EXIF de Android Photo
- Grabación de vídeo de un servicio
- Detección de la falta de cámara trasera
- Cómo renderizar la imagen de la cámara YUV-NV21 de Android en el fondo en libgdx con OpenGLES 2.0 en tiempo real?
- Cámara - cambios de vista previa después de iniciar la grabación de vídeo
- Vista previa de la cámara Android a la imagen final
- Animación suave de una ruta de cámara en una vista de mapa
Me encontré con el mismo problema. En lugar de usar un SurfaceView.Callback
, subclase su clase JavaCameraView
. Ver mi detección de cara en vivo y muestra de dibujo aquí . Entonces era trivial girar la matriz que salía de la cámara de acuerdo con la orientación del dispositivo, antes de procesarla. Extracto pertinente del código vinculado:
@Override public Mat onCameraFrame(Mat inputFrame) { int flipFlags = 1; if(display.getRotation() == Surface.ROTATION_270) { flipFlags = -1; Log.i(VIEW_LOG_TAG, "Orientation is" + getRotation()); } Core.flip(inputFrame, mRgba, flipFlags); inputFrame.release(); Imgproc.cvtColor(mRgba, mGray, Imgproc.COLOR_RGBA2GRAY); if (mAbsoluteFaceSize == 0) { int height = mGray.rows(); if (Math.round(height * mRelativeFaceSize) > 0) { mAbsoluteFaceSize = Math.round(height * mRelativeFaceSize); } } }
Resolví el problema de rotación con OpenCV: después de averiguar cuánto necesita corregir la rotación de la pantalla usando este código , aplico una matriz de rotación a la imagen de la cámara en bruto (después de convertir de YUV a RGB):
Point center = new Point(mFrameWidth/2, mFrameHeight/2); Mat rotationMatrix = Imgproc.getRotationMatrix2D(center, totalRotation, 1);
[…]
Imgproc.cvtColor(mYuv, mIntermediate, Imgproc.COLOR_YUV420sp2RGBA, 4); Imgproc.warpAffine(mIntermediate, mRgba, rotationMatrix, new Size(mFrameHeight, mFrameWidth));
Un problema aparte es que setPreviewDisplay(null)
da una pantalla en blanco en algunos teléfonos. La solución, que he obtenido de aquí y se basa en este reporte de errores y esta pregunta SO, pasa un SurfaceView ocultado, "falso" a la pantalla de vista previa para que comience, pero en realidad muestra la salida en una vista personalizada superpuesta, que yo llamo Vista de cámara. Por lo tanto, después de llamar a setContentView()
en la actividad onCreate()
, se adhieren a este código:
if (VERSION.SDK_INT < VERSION_CODES.HONEYCOMB) { final SurfaceView fakeView = new SurfaceView(this); fakeView.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)); fakeView.setZOrderMediaOverlay(false); final CameraView cameraView = (CameraView) this.findViewById(R.id.cameraview); cameraView.setZOrderMediaOverlay(true); cameraView.fakeView = fakeView; }
A continuación, al configurar la vista preliminar, utilice este código:
try { if (VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB) mCamera.setPreviewTexture(new SurfaceTexture(10)); else mCamera.setPreviewDisplay(fakeView.getHolder()); } catch (IOException e) { Log.e(TAG, "mCamera.setPreviewDisplay fails: "+ e); }
Si sólo está desarrollando para Honeycomb y superiores, simplemente reemplace setPreviewDisplay (null) con mCamera.setPreviewTexture(new SurfaceTexture(10));
Y hacerse con ella. setDisplayOrientation()
todavía no funciona si lo hace, sin embargo, por lo que todavía tendrá que utilizar la solución de matriz de rotación.