android - cámara - open camera descargar
Gire la vista previa de la cámara a la cámara Portrait Portrait OpenCV (11)
Intento usar OpenCV 2.4.3.2 para crear una aplicación de cámara y hacer un poco de procesamiento de opencv. Me gustaría que pueda tener múltiples orientaciones de IU, no solo Paisaje.
El problema es que cuando cambio la orientación a vertical, la imagen sale de lado.
Entiendo que podría rotar la imagen de entrada antes de procesar la imagen (y así dejar la orientación solo como paisaje), lo cual está bien y funciona, pero no resuelve el problema de que el resto de mi UI estará en la orientación incorrecta .
También he intentado usar este código para girar la cámara 90 grados, pero parece que no funciona.
mCamera.setDisplayOrientation(90);
O no tiene ningún efecto, o algunas veces solo hace que la vista previa se bloquee
¿Alguien ha hecho esto con éxito con OpenCV? Mi clase se extiende desde JavaCameraView.
Editar
He realizado una mejora, que es que he girado la imagen dentro de OpenCV tal como se muestra en la clase CameraBridgeViewBase.java.
En el método de entrega y dibujo de cuadros:
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 rootea la imagen de entrada como tal
Esto es mejor, pero obviamente quiero que esté en pantalla completa.
La respuesta del desarrollador "jaiprakashgogi" está funcionando para mí. pero el problema es que la vista previa todavía se guarda como paisaje solamente. eso significa que si configuramos la vista previa de la vista de imagen, entonces se muestra como horizontal.
La solución anterior funciona hasta mostrar la vista previa como retrato pero no se guarda como retrato persistentemente.
Me resolvieron ese problema de la siguiente manera.
- convertir los datos de byte o mat en bitmap
- girar la matriz a 90 grados y aplicar a mapa de bits
- convertir mapa de bits a matriz de bytes y guardarlo.
por favor mira mi 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;
}
Soluciona mi problema por completo.
Lamentablemente, Opencv4Android no es compatible con la cámara de retrato. Pero hay una manera de superarlo. 1) Escribe tu cámara personalizada y configura su orientación para retratos. 2) Registrarse para obtener una devolución de llamada previa. 3) En onPreviewFrame(byte[]data, Camera camera)
crear Mat
de bytes de vista previa:
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 de un formato de vista previa que esté usando su cámara.
PD. no olvides lanzar todas las instancias Mat que hayas creado cuando hayas terminado.
PPS. es bueno administrar la cámara en una secuencia separada para no sobrecargar el subproceso de la interfaz de usuario mientras se realiza una detección.
Modifiqué 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 de la siguiente manera:
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;
}
La vista previa de FPS es más lenta porque para gastos generales computacionales en comparación con el modo horizontal.
Parece que la nueva clase OpenCV CameraBridgeViewBase.java
es de muy alto nivel y no le da suficiente control sobre el diseño de la vista previa de la cámara. Eche un vistazo a mi código de muestra , que se basa en algunas de las muestras antiguas de OpenCV y usa código puro de Android. Para usar la matriz de bytes pasada en onPreviewFrame
, onPreviewFrame
en una Mat y conviértala 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);
Es posible que pueda encontrar las antiguas muestras de OpenCV4Android en Internet, aunque se sacaron hace algunas versiones. Sin embargo, el código de muestra vinculado y el fragmento anterior deberían ser suficientes para comenzar.
Si está utilizando openCV 2.4.9, intente: 1) copiar el contenido del proceso mixto de OpenCV en su código; 2) corregir los errores de desajuste (nombre de la actividad y, probablemente, referencia de diseño); 3) Modifique su manifiesto agregando android:screenOrientation ="landscape"
4) corrija los errores menores y ejecute !!!! 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, le aconsejamos que elimine la barra de estado de la vista previa.
Espero eso ayude !!!
Tengo orientación vertical con CameraBridgeViewBase, pero tuve que cambiar JavaCameraView.java dentro de OpenCV :( La idea es siguiente: después de iniciar la cámara, haga lo siguiente
setDisplayOrientation(mCamera, 90);
mCamera.setPreviewDisplay(getHolder());
y el 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 cámara sin formato en su rotación ensamblada
- getSupportedPreviewSizes () da las proporciones de aspecto correspondientes
- Algoritmo necesita analizar el cuadro en vertical para detectar objetos correctos.
- el mapa de bits creado (lado de Java) para almacenar el cuadro resultante también necesita la relación de aspecto correcta
Entonces, 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 la mayor cantidad de resultados disponibles para retrato (cambia altura / ancho en calculateCameraFrameSize). Todavía estamos manejando el paisaje como entrada desde onPreviewFrame () pero creamos un mapa de bits para dibujar en retrato (AllocateCache).
Por último, necesitamos darle al algoritmo el marco vertical para que pueda detectar objetos "de pie" y devolverlos para guardar y renderizar el mapa de bits. Entonces, siguiendo las modificaciones a tu 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);
...
Todas las respuestas aquí son pirateos. prefiero esta solución:
cambio en el 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());
Tuve el mismo problema al intentar implementar OpenCV. Pude solucionarlo realizando los siguientes cambios en el método deliverAndDrawFrame.
Girar el objeto canvas
Canvas canvas = getHolder().lockCanvas(); // Rotate canvas to 90 degrees canvas.rotate(90f, canvas.getWidth()/2, canvas.getHeight()/2);
Cambia el tamaño del mapa de bits para que se ajuste a todo el tamaño 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); // Unlock canvas getHolder().unlockCanvasAndPost(canvas);
en realidad, puedes simplemente hacer que el padre matemático sea ancho o alto (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 más grande que la pantalla. Simplemente modifique la escala.
tengo el mismo problema, ¡lo he descubierto! y allí está mi solución:
como parte de la primera, en CameraBridgeViewBase.Java
, el dos constructor, agrega 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();
}
luego, debe 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 agrega esta función 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;
}
}
Está bien, y si esta respuesta es útil para ti, marca la Reputación de Ayuda ''aceptada''