Girar la vista previa de la cámara a la cámara Android OpenCV

Estoy tratando de usar OpenCV 2.4.3.2 para crear una aplicación de cámara y hacer algún procesamiento opencv. Me gustaría que fuera capaz de tener múltiples orientaciones de interfaz de usuario, no sólo el paisaje.

El problema es que cuando cambio la orientación a retrato, la imagen sale hacia los lados.

Entiendo que sólo podría girar la imagen de entrada antes de hacer el procesamiento de imágenes (y por lo tanto dejar la orientación sólo como paisaje), que está bien y funciona, pero no resuelve el problema de que el resto de mi interfaz de usuario estará en la orientación equivocada .

También he intentado usar este código para girar la cámara 90deg, pero apenas no parece trabajar.

mCamera.setDisplayOrientation(90); 

O bien no tiene ningún efecto, o a veces sólo hace que la vista previa de ser eliminado

¿Alguien ha hecho esto con éxito con OpenCV? Mi clase se extiende desde JavaCameraView. Imagen de retrato con vista lateral

Editar

He hecho una mejora, que es que he girado la imagen dentro de OpenCV como se muestra en la clase CameraBridgeViewBase.java.

En el método de entrega y trazado:

 if (canvas != null) { canvas.drawColor(0, android.graphics.PorterDuff.Mode.CLEAR); //canvas.drawBitmap(mCacheBitmap, (canvas.getWidth() - mCacheBitmap.getWidth()) / 2, (canvas.getHeight() - mCacheBitmap.getHeight()) / 2, null); //Change to support portrait view Matrix matrix = new Matrix(); matrix.preTranslate((canvas.getWidth() - mCacheBitmap.getWidth()) / 2,(canvas.getHeight() - mCacheBitmap.getHeight()) / 2); if(getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) matrix.postRotate(90f,(canvas.getWidth()) / 2,(canvas.getHeight()) / 2); canvas.drawBitmap(mCacheBitmap, matrix, new Paint()); 

… Básicamente, esto sólo roates la imagen de entrada como así

Imagen de entrada girada 90

Esto es mejor, pero obviamente quiero que esto sea a pantalla completa.

En realidad, sólo puede hacer que el padre de matemáticas de ancho o altura (pantalla completa).

 if (canvas != null) { canvas.rotate(90,0,0); scale = canvas.getWidth() / (float)bitmap.getHeight(); float scale2 = canvas.getHeight() / (float)bitmap.getWidth(); if(scale2 > scale){ scale = scale2; } if (scale != 0) { canvas.scale(scale, scale,0,0); } canvas.drawBitmap(bitmap, 0, -bitmap.getHeight(), null); 

Además, puede hacer que el tamaño de vista previa sea mayor que la pantalla. Simplemente modifique la escala.

Tuve el mismo problema al intentar implementar OpenCV. Fui capaz de solucionarlo haciendo los siguientes cambios en el método deliverAndDrawFrame.

  1. Rotar el objeto de lienzo

     Canvas canvas = getHolder().lockCanvas(); // Rotate canvas to 90 degrees canvas.rotate(90f, canvas.getWidth()/2, canvas.getHeight()/2); 
  2. Cambiar el tamaño del mapa de bits para que se ajuste al tamaño completo del lienzo antes de dibujar

     // Resize Bitmap bitmap = Bitmap.createScaledBitmap(mCacheBitmap, canvas.getHeight(), canvas.getWidth(), true); // Use bitmap instead of mCacheBitmap canvas.drawBitmap(bitmap, new Rect(0,0,bitmap.getWidth(), bitmap.getHeight()), new Rect((int)((canvas.getWidth() - mScale*bitmap.getWidth()) / 2),(int)((canvas.getHeight() - mScale*bitmap.getHeight()) / 2),(int)((canvas.getWidth() - mScale*bitmap.getWidth()) / 2 + mScale*bitmap.getWidth()), (int ((canvas.getHeight() - mScale*bitmap.getHeight()) / 2 + mScale*bitmap.getHeight())), null); 

Modifiqué el CameraBridgeViewBase.java de la siguiente manera:

 protected Size calculateCameraFrameSize(List<?> supportedSizes, ListItemAccessor accessor, int surfaceWidth, int surfaceHeight) { int calcWidth = 0; int calcHeight = 0; if(surfaceHeight > surfaceWidth){ int temp = surfaceHeight; surfaceHeight = surfaceWidth; surfaceWidth = temp; } 

Y en la función "deliverAndDrawFrame":

  if (mScale != 0) { if(canvas.getWidth() > canvas.getHeight()) { canvas.drawBitmap(mCacheBitmap, new Rect(0,0,mCacheBitmap.getWidth(), mCacheBitmap.getHeight()), new Rect((int)((canvas.getWidth() - mScale*mCacheBitmap.getWidth()) / 2), (int)((canvas.getHeight() - mScale*mCacheBitmap.getHeight()) / 2), (int)((canvas.getWidth() - mScale*mCacheBitmap.getWidth()) / 2 + mScale*mCacheBitmap.getWidth()), (int)((canvas.getHeight() - mScale*mCacheBitmap.getHeight()) / 2 + mScale*mCacheBitmap.getHeight())), null); } else { canvas.drawBitmap(mCacheBitmap, rotateMe(canvas, mCacheBitmap), null); } 

Donde rotateMe se define como sigue:

 private Matrix rotateMe(Canvas canvas, Bitmap bm) { // TODO Auto-generated method stub Matrix mtx=new Matrix(); float scale = (float) canvas.getWidth() / (float) bm.getHeight(); mtx.preTranslate((canvas.getWidth() - bm.getWidth())/2, (canvas.getHeight() - bm.getHeight())/2); mtx.postRotate(90,canvas.getWidth()/2, canvas.getHeight()/2); mtx.postScale(scale, scale, canvas.getWidth()/2 , canvas.getHeight()/2 ); return mtx; } 

El FPS de vista previa es más lento porque para la sobrecarga computacional en comparación con el modo horizontal.

Lamentablemente Opencv4Android no es compatible con la cámara de retrato. Pero hay una manera de cómo superarlo. 1) Escriba su cámara personalizada y ajuste su orientación al retrato. 2) Registrarse para su devolución de llamada de vista previa. 3) En onPreviewFrame(byte[]data, Camera camera) crear Mat de vista previa bytes:

 Mat mat = new Mat(previewSize.height, previewSize.width, CvType.CV_8UC1); mat.put(0, 0, data); Core.transpose(mat, mat); Core.flip(mat, mat, -1); // rotates Mat to portrait 

CvType depende del formato de vista previa que usa su cámara.

PD. No olvide liberar todas las instancias de Mat que haya creado cuando haya terminado.

PPS. Es bueno administrar su cámara en un hilo separado para no sobrecargar el hilo de la interfaz de usuario mientras realiza alguna detección.

Parece que la nueva clase OpenCV CameraBridgeViewBase.java es demasiado alto y no da suficiente control sobre el diseño de la vista previa de la cámara. Eche un vistazo a mi código de ejemplo , que se basa en algunas de las muestras más antiguas de OpenCV y usa código puro para Android. Para usar la matriz de bytes pasado en onPreviewFrame , put() en un Mat y convertir de YUV a RGB:

 mYuv = new Mat(previewHeight + previewHeight/2, previewWidth, CvType.CV_8UC1); mYuv.put(0, 0, mBuffer); Imgproc.cvtColor(mYuv, mRgba, Imgproc.COLOR_YUV420sp2RGBA, 4); 

Usted puede ser capaz de encontrar las antiguas muestras de OpenCV4Android en Internet, aunque fueron sacados hace unas pocas versiones. Sin embargo, el código de ejemplo vinculado y el fragmento anterior deben ser suficientes para empezar.

Si está utilizando el openCV 2.4.9, pruebe: 1) copiar el contenido del tutorial opencv mezclado en su código; 2) corregir los errores de desajuste (nombre de la actividad y probablemente la referencia del diseño); 3) Modifica tu manifiesto agregando android:screenOrientation ="landscape" 4) corregir errores de menores y ejecutar !!!! Bbaamm (debería funcionar correctamente ahora)

Nota: con este método, la barra de estado aparece en el lado derecho cuando el teléfono está en posición vertical. Como estamos desarrollando un proyecto de cámara, te aconsejo que elimines la barra de estado de la vista previa.

Espero eso ayude !!!

Todas las respuestas aquí son hacks. Yo prefiero esta solución:

Cambio en código JavaCameraView:

 mBuffer = new byte[size]; mCamera.setDisplayOrientation(90); //add this mCamera.addCallbackBuffer(mBuffer); 

Segundo cambio:

 // if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { // mSurfaceTexture = new SurfaceTexture(MAGIC_TEXTURE_ID); // mCamera.setPreviewTexture(mSurfaceTexture); // } else // mCamera.setPreviewDisplay(null); mCamera.setPreviewDisplay(getHolder()); 

Tengo la orientación del retrato con CameraBridgeViewBase, pero tuve que cambiar JavaCameraView.java dentro del OpenCV 🙁 La idea es siguiente: después de la cámara init, hacer siguiente

 setDisplayOrientation(mCamera, 90); mCamera.setPreviewDisplay(getHolder()); 

Y método setDisplayOrientation

 protected void setDisplayOrientation(Camera camera, int angle){ Method downPolymorphic; try { downPolymorphic = camera.getClass().getMethod("setDisplayOrientation", new Class[] { int.class }); if (downPolymorphic != null) downPolymorphic.invoke(camera, new Object[] { angle }); } catch (Exception e1) { } } 

Tienes que considerar algunas cosas:

  • OnPreviewFrame () siempre entrega los datos de la cámara en bruto en su rotación assambled
  • GetSupportedPreviewSizes () proporciona las relaciones de aspecto correspondientes
  • Algoritmo necesita analizar el marco en el retrato para detectar objetos correctos.
  • El mapa de bits creado (lado Java) para almacenar el marco resultante también necesita la relación de aspecto correcta

Por lo tanto, para una solución rápida y de alta resolución, cambié JavaCameraView.java y mi parte JNI. En JavaCameraView.java:

 ... if (sizes != null) { /* Select the size that fits surface considering maximum size allowed */ Size frameSize; if(width > height) { frameSize = calculateCameraFrameSize(sizes, new JavaCameraSizeAccessor(), width, height); }else{ frameSize = calculateCameraFrameSize(sizes, new JavaCameraSizeAccessor(), height, width); } ... mCamera.setParameters(params); params = mCamera.getParameters(); int bufFrameWidth, bufFrameHeight; bufFrameWidth = params.getPreviewSize().width; bufFrameHeight = params.getPreviewSize().height; if(width > height) { mFrameWidth = params.getPreviewSize().width; mFrameHeight = params.getPreviewSize().height; }else{ mFrameWidth = params.getPreviewSize().height; mFrameHeight = params.getPreviewSize().width; } ... mFrameChain = new Mat[2]; mFrameChain[0] = new Mat(bufFrameHeight + (bufFrameHeight/2), bufFrameWidth, CvType.CV_8UC1); mFrameChain[1] = new Mat(bufFrameHeight + (bufFrameHeight/2), bufFrameWidth, CvType.CV_8UC1); AllocateCache(); mCameraFrame = new JavaCameraFrame[2]; mCameraFrame[0] = new JavaCameraFrame(mFrameChain[0], bufFrameWidth, bufFrameHeight); mCameraFrame[1] = new JavaCameraFrame(mFrameChain[1], bufFrameWidth, bufFrameHeight); 

Con estos cambios, nos aseguramos de que estamos utilizando el resultado más alto disponible para el retrato (los interruptores de altura / ancho en calculateCameraFrameSize). Todavía estamos manejando el paisaje como entrada de onPreviewFrame () pero creamos un mapa de bits para dibujar en retrato (AllocateCache).

Por último, debemos darle al algoritmo el retrato-marco para permitirle detectar objetos "de pie" y devolverlos para guardar y renderizar el mapa de bits. Así que las siguientes modificaciones a su Actividad:

 public Mat rot90(Mat matImage, int rotflag){ //1=CW, 2=CCW, 3=180 Mat rotated = new Mat(); if (rotflag == 1){ rotated = matImage.t(); flip(rotated, rotated, 1); //transpose+flip(1)=CW } else if (rotflag == 2) { rotated = matImage.t(); flip(rotated, rotated,0); //transpose+flip(0)=CCW } else if (rotflag ==3){ flip(matImage, rotated,-1); //flip(-1)=180 } else if (rotflag != 0){ //if not 0,1,2,3: Log.e(TAG, "Unknown rotation flag("+rotflag+")"); } return rotated; } public Mat onCameraFrame(CvCameraViewFrame inputFrame) { mRgba = rot90(inputFrame.rgba(), 1); mGray = rot90(inputFrame.gray(), 1); ... 

"Jaiprakashgogi" respuesta del desarrollador está trabajando para mí. Pero el problema es que la vista previa todavía se guarda como solo paisaje. Eso significa que si establecemos la vista previa a la vista de la imagen, entonces se muestra como paisaje.

La solución anterior funciona hasta mostrar la vista previa como retrato, pero no se guarda como retrato de forma persistente.

Estaba resuelto ese problema de la siguiente manera.

  1. Convertir los datos de byte o mat en bitmap
  2. Rotar la matriz a 90 grados y aplicar a mapa de bits
  3. Convertir bitmap a matriz de bytes y guardarlo.

Por favor vea el código aquí …

  public String writeToSDFile(byte[] data, int rotation){ byte[] portraitData=null; if(rotation==90){ Log.i(TAG,"Rotation is : "+rotation); Bitmap bitmap= BitmapFactory.decodeByteArray(data,0,data.length); Matrix matrix = new Matrix(); matrix.postRotate(90); Bitmap rotatedBitmap = Bitmap.createBitmap(bitmap , 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true); portraitData=bitmapToByte(rotatedBitmap); } File dir=getDirectory(); String imageTime=""+System.currentTimeMillis(); String fileName=Constants.FILE_NAME+imageTime+"."+Constants.IMAGE_FORMAT; File file = new File(dir, fileName); try { FileOutputStream f = new FileOutputStream(file); if(rotation==90){ f.write(portraitData); }else { f.write(data); } f.close(); } catch (FileNotFoundException e) { e.printStackTrace(); Log.i(TAG, "******* File not found. Did you" + " add a WRITE_EXTERNAL_STORAGE permission to the manifest?"); } catch (IOException e) { e.printStackTrace(); } Log.i(TAG,"\n\nFile written to "+file); return fileName; } // convert bitmap to Byte Array public byte[] bitmapToByte(Bitmap bitmap){ ByteArrayOutputStream outputStream=new ByteArrayOutputStream(); bitmap.compress(Bitmap.CompressFormat.JPEG,100,outputStream); byte[] array=outputStream.toByteArray(); return array; } 

Resuelve completamente el problema.

Tengo el mismo problema, he tenido la figura !! Y no es mi solución:

Como la parte de la primera, En CameraBridgeViewBase.Java , el constructor dos, agregar la inicialización de WindowManager:

 public CameraBridgeViewBase(Context context, int cameraId) { super(context); mCameraIndex = cameraId; getHolder().addCallback(this); mMaxWidth = MAX_UNSPECIFIED; mMaxHeight = MAX_UNSPECIFIED; windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); 

}

 public CameraBridgeViewBase(Context context, AttributeSet attrs) { super(context, attrs); int count = attrs.getAttributeCount(); Log.d(TAG, "Attr count: " + Integer.valueOf(count)); TypedArray styledAttrs = getContext().obtainStyledAttributes(attrs, R.styleable.CameraBridgeViewBase); if (styledAttrs.getBoolean(R.styleable.CameraBridgeViewBase_show_fps, false)) enableFpsMeter(); mCameraIndex = styledAttrs.getInt(R.styleable.CameraBridgeViewBase_camera_id, -1); getHolder().addCallback(this); mMaxWidth = MAX_UNSPECIFIED; mMaxHeight = MAX_UNSPECIFIED; windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); styledAttrs.recycle(); 

}

Entonces, es necesario reemplazar la función deliverAndDrawFrame(CvCameraViewFrame frame) siguiente manera,

 protected void deliverAndDrawFrame(CvCameraViewFrame frame) { Mat modified; if (mListener != null) { modified = mListener.onCameraFrame(frame); } else { modified = frame.rgba(); } boolean bmpValid = true; if (modified != null) { try { Utils.matToBitmap(modified, mCacheBitmap); } catch (Exception e) { Log.e(TAG, "Mat type: " + modified); Log.e(TAG, "Bitmap type: " + mCacheBitmap.getWidth() + "*" + mCacheBitmap.getHeight()); Log.e(TAG, "Utils.matToBitmap() throws an exception: " + e.getMessage()); bmpValid = false; } } if (bmpValid && mCacheBitmap != null) { Canvas canvas = getHolder().lockCanvas(); if (canvas != null) { canvas.drawColor(0, android.graphics.PorterDuff.Mode.CLEAR); int rotation = windowManager.getDefaultDisplay().getRotation(); int degrees = 0; // config degrees as you need switch (rotation) { case Surface.ROTATION_0: degrees = 90; break; case Surface.ROTATION_90: degrees = 0; break; case Surface.ROTATION_180: degrees = 270; break; case Surface.ROTATION_270: degrees = 180; break; } Matrix matrix = new Matrix(); matrix.postRotate(degrees); Bitmap outputBitmap = Bitmap.createBitmap(mCacheBitmap, 0, 0, mCacheBitmap.getWidth(), mCacheBitmap.getHeight(), matrix, true); if (outputBitmap.getWidth() <= canvas.getWidth()) { mScale = getRatio(outputBitmap.getWidth(), outputBitmap.getHeight(), canvas.getWidth(), canvas.getHeight()); } else { mScale = getRatio(canvas.getWidth(), canvas.getHeight(), outputBitmap.getWidth(), outputBitmap.getHeight()); } if (mScale != 0) { canvas.scale(mScale, mScale, 0, 0); } Log.d(TAG, "mStretch value: " + mScale); canvas.drawBitmap(outputBitmap, 0, 0, null); if (mFpsMeter != null) { mFpsMeter.measure(); mFpsMeter.draw(canvas, 20, 30); } getHolder().unlockCanvasAndPost(canvas); } } 

}

Y añadir esta funtion extra,

 private float getRatio(int widthSource, int heightSource, int widthTarget, int heightTarget) { if (widthTarget <= heightTarget) { return (float) heightTarget / (float) heightSource; } else { return (float) widthTarget / (float) widthSource; } 

}

It's okey, y Si esta respuesta es útil para usted, marque "aceptado" Help Reputation

No es necesario girar la pantalla.

Edite su archivo Manifest.xml con la línea siguiente:

  <activity android:screenOrientation="landscape" android:configChanges="keyboardHidden|orientation|screenSize"> 

Esto proporcionará una vista previa de la cámara a pantalla completa, que funcionan de la misma manera que la vista previa de la cámara del sistema.

  • Error OpenCV: No se puede cargar la biblioteca de información para OpenCV
  • Código de desbloqueo de la cara en el proyecto de código abierto de Android?
  • OpenCv en Android: detección de puntos clave en imágenes de archivo
  • Cortar la imagen después de seleccionar el área mediante la detección de bordes en android
  • Detección de imágenes en paralelo y vista previa de la cámara OpenCV Android
  • Android - utiliza la cámara sin vista de superficie o textureview
  • Reconocimiento y seguimiento de objetos en movimiento más rápido en Android
  • Construyendo OpenCV 2.4.5 en android ADT: "ndk-build" no se encuentra en PATH
  • OpenCV en Android - Encabezados; No hay tal directorio de archivos
  • Android 4.2 ndk: error de carga de la biblioteca: load_library (linker.cpp: 750) || soinfo_link_image || libhoudini.so || opencv
  • La aplicación se estrelló después de la señal fatal 11 (SIGSEGV) en 0x00000020 (código = 1) - Android
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.