Aplicar filtros personalizados a la salida de la cámara

¿Cómo puedo aplicar filtros personalizados a fotogramas únicos en la salida de la cámara y mostrarlos.

Lo que he probado 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). Larga historia corta, no funciona.

  • Me imaginé que la matriz de bytes 'datos' es una copia de la salida de la cámara; Pero esto realmente no ayuda, porque necesito el buffer "real".

  • He oído que podrías implementar esto con openGL. Eso suena muy complicado.

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

OK, hay varias maneras de hacer esto. Pero hay un problema significativo con el rendimiento. El byte [] de una cámara está en formato YUV , que tiene que ser convertido a algún tipo de formato RGB, si desea mostrarlo. Esta conversión es bastante costosa operación y reduce significativamente la salida fps.

Depende de lo que realmente quieres hacer con la vista previa de la cámara. Porque la mejor solución es dibujar la vista previa de la cámara sin devolución de llamada y hacer algunos efectos sobre la vista previa de la cámara. Esa es la manera habitual de hacer cosas de realismo argumented.

Pero si realmente necesita mostrar la salida manualmente, hay varias maneras de hacerlo. Su ejemplo no funciona por varias razones. En primer lugar, no está mostrando la imagen en absoluto. Si llama a esto:

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

Que su cámara no muestra la vista previa en absoluto, tiene que mostrarla manualmente. Y no se pueden realizar operaciones costosas en el método onPreviewFrame, porque la duración de los datos es limitada, se sobreescribe en el siguiente fotograma. Una sugerencia, use setPreviewCallbackWithBuffer , es más rápido, ya que reutiliza un búfer y no tiene que asignar nueva memoria en cada marco.

Así que 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 clase o en alguna parte.

En onDraw, por ejemplo, puede aplicar paint.setColorFilter (filtro) , si desea modificar los colores. Puedo publicar algún ejemplo de eso, si quieres.

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

La otra opción es usar openGL ES. Usted necesita GLSurfaceView, donde se enlaza marco de la cámara como una textura (en GLSurfaceView implementar Camera.previewCallback, por lo que utiliza onPreviewFrame mismo modo que en la superficie regular). Pero existe el mismo problema, es necesario convertir datos YUV. Hay una posibilidad: sólo puede mostrar datos de luminancia de la vista preliminar (escala de grises) muy rápido, ya que la primera mitad de la matriz de bytes en YUV es sólo datos de luminancia sin colores. Así que en onPreviewFrame que utiliza arraycopy para copiar la primera mitad de la matriz, y que se vincule la textura como este:

 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); 

Usted no puede obtener unos 16-18 fps de esta manera y puede utilizar openGL para hacer algunos filtros. Puedo enviarte un poco más de código a esto si quieres, pero es demasiado tiempo para poner aquí …

Para más información, puede ver mi pregunta simillar , pero no hay una buena solución tampoco …

  • Android OpenGL ES: ¿Cómo se selecciona un objeto 2D?
  • ¿Por qué eglCreateImageKHR está devolviendo EGL_NO_IMAGE_KHR (en android)?
  • Cuarzo 2D o OpenGL ES? Pros y contras a largo plazo, posibilidad de migración a otras plataformas
  • Pasar variables entre el renderizador y otra clase con queueEvent ()
  • Compresión de textura de Android OpenGL
  • Consejos sobre dibujo de alto rendimiento en Android
  • ¿Se descarta mal el rendimiento del programa en OpenGL?
  • Android Matrix multiplyMM en detalle
  • Desarrollo de juegos Android 2D sin motor
  • Los emuladores de Genymotion dan error Open GL en Kubuntu 13.10 64-bit
  • Grabación de vídeo en pantalla de Android
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.