Lo que tengo que hacer es muy simple, quiero manualmente la pantalla de vista previa de la cámara usando la cámara de devolución de llamada y quiero llegar al menos a 15fps en un dispositivo real. Ni siquiera tengo los colores, solo me falta previsualización de la imagen en escala de grises.
Las imágenes de la cámara son en formato YUV y que tiene que procesar de alguna manera, que es el principal problema de rendimiento. Estoy utilizando la API de 8.

En todos los casos estoy usando la cámara.setPreviewCallbackWithBuffer(), que es más rápido que el de la cámara.setPreviewCallback(). Parece que no puedo conseguir alrededor de 24 fps aquí, si no estoy en la visualización de la vista previa. Así que no es el problema.

He intentado estas soluciones:

1. Mostrar vista previa de la cámara en un SurfaceView como un mapa de bits. Funciona, pero el rendimiento es de alrededor de 6 fps.

baos = new ByteOutputStream();
yuvimage=new YuvImage(cameraFrame, ImageFormat.NV21, prevX, prevY, null);

yuvimage.compressToJpeg(new Rect(0, 0, prevX, prevY), 80, baos);
jdata = baos.toByteArray();

bmp = BitmapFactory.decodeByteArray(jdata, 0, jdata.length); //Convert to Bitmap, this is the main issue, it takes a lot of time

canvas.drawBitmap(bmp , 0, 0, paint);

2. Mostrar vista previa de la cámara en un GLSurfaceView como una textura. Aquí estaba yo solo muestra la luminancia de datos (imagen en escala de grises), que es muy fácil, sólo se requiere de una arraycopy() en cada fotograma. I puede obtener alrededor de 12 fps, pero tengo que aplicar algunos filtros para la vista previa y parece, que no se puede hacer de forma rápida en OpenGL ES 1. Así que no se puede utilizar esta solución. Algunos detalles de este en otra pregunta.


3. Mostrar vista previa de la cámara en un (GL)SurfaceView utilizando NDK para el proceso de selección de entrada YUV datos. Me parece una solución aquí que utiliza parte de la función de C y NDK. Pero no he podido usarlo, aquí algunos detalles más. Pero de todos modos, esta solución se hace para devolver ByteBuffer para mostrar como una textura de OpenGL y no va a ser más rápido que el anterior intento. Así que tendría que modificar para volver int matriz [], que puede ser elaborado con lona.drawBitmap(), pero no entiendo C suficiente para ello.


Así, ¿hay alguna otra manera de que me estoy perdiendo o alguna mejora a los intentos que he probado?

OriginalEl autor Jaa-c | 2011-12-02

6 Comentarios

  1. 7

    Estoy trabajando en exactamente el mismo problema, pero no tengo tan lejos como tú.

    Han considerado que el dibujo de los píxeles directamente a la lona sin codificación a JPEG primero? Dentro de la OpenCV kit de http://sourceforge.net/projects/opencvlibrary/files/opencv-android/2.3.1/OpenCV-2.3.1-android-bin.tar.bz2/download (que en realidad no usar opencv; no te preocupes), existe un proyecto llamado tutorial-0-androidcamera que demuestra la conversión de la YUV píxeles RGB y, a continuación, escribir directamente a un mapa de bits.

    El código correspondiente, en esencia:

    public void onPreviewFrame(byte[] data, Camera camera, int width, int height) {
    int frameSize = width*height;
    int[] rgba = new int[frameSize+1];
    //Convert YUV to RGB
    for (int i = 0; i < height; i++)
    for (int j = 0; j < width; j++) {
    int y = (0xff & ((int) data[i * width + j]));
    int u = (0xff & ((int) data[frameSize + (i >> 1) * width + (j & ~1) + 0]));
    int v = (0xff & ((int) data[frameSize + (i >> 1) * width + (j & ~1) + 1]));
    y = y < 16 ? 16 : y;
    int r = Math.round(1.164f * (y - 16) + 1.596f * (v - 128));
    int g = Math.round(1.164f * (y - 16) - 0.813f * (v - 128) - 0.391f * (u - 128));
    int b = Math.round(1.164f * (y - 16) + 2.018f * (u - 128));
    r = r < 0 ? 0 : (r > 255 ? 255 : r);
    g = g < 0 ? 0 : (g > 255 ? 255 : g);
    b = b < 0 ? 0 : (b > 255 ? 255 : b);
    rgba[i * width + j] = 0xff000000 + (b << 16) + (g << 8) + r;
    }
    Bitmap bmp = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
    bmp.setPixels(rgba, 0/* offset */, width /* stride */, 0, 0, width, height);
    Canvas canvas = mHolder.lockCanvas();
    if (canvas != null) {
    canvas.drawBitmap(bmp, (canvas.getWidth() - width) / 2, (canvas.getHeight() - height) / 2, null);
    mHolder.unlockCanvasAndPost(canvas);
    } else {
    Log.w(TAG, "Canvas is null!");
    }
    bmp.recycle();
    }

    Por supuesto, usted tendría que adaptar para satisfacer sus necesidades (ex. no asignar rgba cada fotograma), pero puede ser un comienzo. Me encantaría ver si funciona para ti o no, todavía estoy luchando problemas ortogonal a la suya en el momento.

    Voy a probar esta mañana, pero estoy bastante seguro de que no habrá ninguna mejora de rendimiento, porque esto funciona más o menos la misma que la versión con YuvImage y BitmapFactory clases. Pero voy a darle una oportunidad y vamos a ver. Gracias.
    Así que, como me esperaba, tiene el peor desempeño desde todos los casos, incluso cuando traté de optimizar un poco…

    OriginalEl autor Michael

  2. 4

    Creo que Michael está en el camino correcto. En primer lugar usted puede probar este método para convertir de RGB a escala de Grises. Es evidente que se está haciendo casi la misma cosa como la suya,pero un poco más rápido de lo que usted desea.

    //YUV Space to Greyscale
    static public void YUVtoGrayScale(int[] rgb, byte[] yuv420sp, int width, int height){
    final int frameSize = width * height;
    for (int pix = 0; pix < frameSize; pix++){
    int pixVal = (0xff & ((int) yuv420sp[pix])) - 16;
    if (pixVal < 0) pixVal = 0;
    if (pixVal > 255) pixVal = 255;
    rgb[pix] = 0xff000000 | (pixVal << 16) | (pixVal << 8) | pixVal;
    }
    }

    }

    Segundo, no se pueden crear un montón de trabajo para el recolector de basura. Los mapas de bits y vectores van a ser de un tamaño fijo. Crear una vez, no en onFramePreview.

    Hacer que usted va a terminar con algo que se parece a esto:

        public PreviewCallback callback = new PreviewCallback() {
    @Override
    public void onPreviewFrame(byte[] data, Camera camera) {
    if ( (mSelectView == null) || !inPreview )
    return;
    if (mSelectView.mBitmap == null)
    {
    //initialize SelectView bitmaps, arrays, etc
    //mSelectView.mBitmap = Bitmap.createBitmap(mSelectView.mImageWidth, mSelectView.mImageHeight, Bitmap.Config.RGB_565);
    //etc
    }
    //Pass Image Data to SelectView
    System.arraycopy(data, 0, mSelectView.mYUVData, 0, data.length);
    mSelectView.invalidate();
    }
    };

    Y, a continuación, el lienzo donde quieres poner este aspecto:

    class SelectView extends View {
    Bitmap mBitmap;
    Bitmap croppedView;
    byte[] mYUVData;
    int[] mRGBData;
    int mImageHeight;
    int mImageWidth;
    public SelectView(Context context){
    super(context);
    mBitmap = null;
    croppedView = null;
    }
    @Override
    protected void onDraw(Canvas canvas){
    if (mBitmap != null)
    {
    int canvasWidth = canvas.getWidth();
    int canvasHeight = canvas.getHeight();
    //Convert from YUV to Greyscale
    YUVtoGrayScale(mRGBData, mYUVData, mImageWidth, mImageHeight);
    mBitmap.setPixels(mRGBData, 0, mImageWidth, 0, 0, mImageWidth, mImageHeight);
    Rect crop = new Rect(180, 220, 290, 400);
    Rect dst = new Rect(0, 0, canvasWidth, (int)(canvasHeight/2));
    canvas.drawBitmap(mBitmap, crop, dst, null);
    }
    super.onDraw(canvas);
    }

    Este ejemplo muestra una recortada y distorsionada de selección de la vista previa de la cámara en tiempo real, pero usted consigue la idea. Se ejecuta en alta FPS en un Nexus S en escala de grises y deben trabajar para sus necesidades.

    Second, don't create a ton of work for the garbage collector.. Su onDraw nos diga lo contrario.

    OriginalEl autor sarwar

  3. 1

    No es esto lo que quieres? Sólo tiene que utilizar un SurfaceView en su diseño, a continuación, en algún lugar en el init como onResume():

    SurfaceView surfaceView = ...
    SurfaceHolder holder = surfaceView.getHolder();
    ...
    Camera camera = ...;
    camera.setPreviewDisplay(holder);

    Sólo envía las tramas directamente a la vista tan rápido como llegan.

    Si quieres escala de grises, modificar los parámetros de la cámara con setColorEffect("mono").

    Gracias, pero que realmente necesita para utilizar la devolución de llamada e imágenes de la pantalla manualmente. No necesito sólo en escala de grises de la imagen, estoy usando algunos más de los filtros, que no he mencionado en mi pregunta…

    OriginalEl autor Sean Owen

  4. 1

    Muy básico y simple los efectos de, no es

    Camera.Parameters parameters = mCamera.getParameters();
    parameters.setColorEffect(Parameters.EFFECT_AQUA);

    Me di cuenta de que este los efectos de hacer de manera DIFERENTE dependiendo del dispositivo.
    Por ejemplo, en mi teléfono (galaxy s II) se ve un poco como un efecto cómico, como en contraste con el galaxy s 1 es ‘sólo’ un tono azul.

    Es pro: funciona como live-vista previa.

    Miré a mi alrededor algunas otras aplicaciones de la cámara y que, obviamente, también se enfrentaron a este problema.
    Entonces, ¿qué hicieron?
    Que se está capturando la cámara por defecto de la imagen, aplicando un filtro a los datos de mapa de bits, y mostrar esta imagen en un simple ImageView. Lo cierto es que no se que tan fresca como en la vista previa en vivo, pero no enfrentan los problemas de rendimiento.

    Gracias, yo sé acerca de estos efectos de cámara, pero yo quería hacer algo de contraste ajuste, sustitución de color, etc. Pero tienes razón, parece que realmente no hay una buena solución a la vista previa en vivo, así que voy a tener que hacer algo como lo has descrito… :/

    OriginalEl autor poitroae

  5. 0

    Creo que he leído en un blog que la escala de grises de datos es en la primera, x*y bytes. Yuv debe representar la luminancia, por lo que los datos están allí, aunque no es perfecto en escala de grises. Su gran relativa brillo, pero no en escala de grises, ya que cada color no es tan brillante como el uno al otro en rgb. El verde es generalmente una fuerte peso en la luminosidad de las conversiones. Espero que esto ayude!

    OriginalEl autor rickstockham

  6. 0

    ¿Hay alguna razón en especial que se ve forzado a usar GLES 1.0 ?

    Porque si no, ver la aceptación respuesta aquí:
    Android SDK: Obtener primas de vista previa de imagen de la cámara sin mostrar

    Generalmente se menciona el uso de la Cámara.setPreviewTexture() en combinación con GLES 2.0.
    En GLES 2.0 se puede representar en pantalla completa-quad en toda la pantalla, para crear cualquier tipo de efecto que desea.

    Es probablemente la manera más rápida posible.

    Ejemplo: github.com/google/grafika/blob/master/src/com/android/grafika/… . Actualmente cuenta con un sombreado que pone un B&W de la imagen en el canal de color rojo (de la búsqueda para «rosyFragment»). Asignar el mismo valor de R/G/B produciría un B&W de la imagen.

    OriginalEl autor ZeDuS

Dejar respuesta

Please enter your comment!
Please enter your name here