Aplicar filtros personalizados a la salida de la cámara

¿Cómo aplico filtros personalizados a cuadros individuales en la salida de la cámara y los muestro?

Lo que he intentado hasta ahora:

mCamera.setPreviewCallback(new CameraGreenFilter()); public class CameraGreenFilter implements PreviewCallback { @Override public void onPreviewFrame(byte[] data, Camera camera) { final int len = data.length; for(int i=0; i<len; ++i){ data[i] *= 2; } } } 
  • Aunque su nombre contiene “verde”, realmente quiero modificar los valores de alguna manera (en este caso, los colores se intensificarían un poco). Para resumir, no funciona.

  • Descubrí que los ‘datos’ de la matriz de bytes son una copia de la salida de la cámara; pero esto realmente no ayuda, porque necesito el buffer ‘real’.

  • Escuché que podrías implementar esto con OpenGL. Eso suena muy complicado.

hay una manera mas facil? De lo contrario, ¿cómo funcionaría este mapeo openGL-surfaceView?

OK, hay varias formas de hacer esto. Pero hay un problema importante con el rendimiento. El byte [] de una cámara está en formato YUV , que debe convertirse a algún tipo de formato RGB, si desea visualizarlo. Esta conversión es una operación bastante costosa y reduce significativamente los fps de salida.

Depende de lo que realmente quieras hacer con la vista previa de la cámara. Porque la mejor solución es dibujar la vista previa de la cámara sin callback y hacer algunos efectos sobre la vista previa de la cámara. Esa es la forma habitual de hacer cosas de reallidad argumentada.

Pero si realmente necesita mostrar la salida de forma manual, existen varias formas de hacerlo. Tu ejemplo no funciona por varias razones. Primero, no estás mostrando la imagen en absoluto. Si llamas a esto:

 mCamera.setPreviewCallback(new CameraGreenFilter()); mCamera.setPreviewDisplay(null); 

de lo que su cámara no muestra la vista previa, debe mostrarla manualmente. Y no puede hacer operaciones costosas en el método onPreviewFrame, ya que la vida útil de los datos es limitada, se sobrescribe en el siguiente fotogtwig. Una sugerencia, use setPreviewCallbackWithBuffer , es más rápido, porque reutiliza un búfer y no tiene que asignar nueva memoria en cada fotogtwig.

Entonces tienes que hacer algo como esto:

 private byte[] cameraFrame; private byte[] buffer; @Override public void onPreviewFrame(byte[] data, Camera camera) { cameraFrame = data; addCallbackBuffer(data); //actually, addCallbackBuffer(buffer) has to be called once sowhere before you call mCamera.startPreview(); } private ByteOutputStream baos; private YuvImage yuvimage; private byte[] jdata; private Bitmap bmp; private Paint paint; @Override //from SurfaceView public void onDraw(Canvas canvas) { baos = new ByteOutputStream(); yuvimage=new YuvImage(cameraFrame, ImageFormat.NV21, prevX, prevY, null); yuvimage.compressToJpeg(new Rect(0, 0, width, height), 80, baos); //width and height of the screen jdata = baos.toByteArray(); bmp = BitmapFactory.decodeByteArray(jdata, 0, jdata.length); canvas.drawBitmap(bmp , 0, 0, paint); invalidate(); //to call ondraw again } 

Para que esto funcione, debe llamar a setWillNotDraw (false) en el constructor de la clase o en algún otro lugar.

En onDraw, puede, por ejemplo, aplicar paint.setColorFilter (filter) , si desea modificar los colores. Puedo publicar un ejemplo de eso, si quieres.

Así que esto funcionará, pero el rendimiento será bajo (menos de 8 fps), porque BitmapFactory.decodeByteArray es lento. Puede intentar convertir datos de YUV a RGB con código nativo y NDK de Android, pero eso es bastante complicado.

La otra opción es usar OpenGL ES. Necesitas GLSurfaceView, donde unes el marco de la cámara como una textura (en GLSurfaceView implementa Camera.previewCallback, por lo que usas onPreviewFrame de la misma manera que en la superficie normal). Pero existe el mismo problema, necesita convertir datos de YUV. Existe una posibilidad: puede visualizar solo datos de luminancia desde la vista previa (imagen en escala de grises) bastante rápido, porque la primera mitad de la matriz de bytes en YUV es solo datos de luminancia sin colores. Por lo tanto, en onPreviewFrame, utiliza arraycopy para copiar la primera mitad de la matriz y luego enlaza la textura de esta manera:

 gl.glGenTextures(1, cameraTexture, 0); int tex = cameraTexture[0]; gl.glBindTexture(GL10.GL_TEXTURE_2D, tex); gl.glTexImage2D(GL10.GL_TEXTURE_2D, 0, GL10.GL_LUMINANCE, this.prevX, this.prevY, 0, GL10.GL_LUMINANCE, GL10.GL_UNSIGNED_BYTE, ByteBuffer.wrap(this.cameraFrame)); //cameraFrame is the first half od byte[] from onPreviewFrame gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_LINEAR); 

No puede obtener aproximadamente 16-18 fps de esta manera y puede usar OpenGL para hacer algunos filtros. Si quieres, puedo enviarte más código, pero es demasiado largo para ponerlo aquí …

Para obtener más información, puedes ver mi pregunta de simillar , pero tampoco hay una buena solución …