2011-12-03 11 views
15

Come applicare i filtri personalizzati ai singoli fotogrammi nell'output della fotocamera e mostrarli.Applicare filtri personalizzati all'uscita della telecamera

Quello che ho provato finora:

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; 
     } 
    } 
} 
  • Anche se il suo nome contiene "verde" Io in realtà voglio solo modificare i valori in qualche modo (in questo caso, i colori sarebbero intensificati un po ') . Per farla breve, non funziona.

  • Ho capito che l'array di byte "dati" è una copia dell'uscita della telecamera; ma questo non aiuta molto, perché ho bisogno del buffer 'reale'.

  • Ho sentito che è possibile implementarlo con openGL. Sembra molto complicato.

C'è un modo più semplice? Altrimenti, come funzionerebbe questa mappatura openGL-surfaceView?

risposta

36

OK, ci sono diversi modi per farlo. Ma c'è un problema significativo con le prestazioni. Il byte [] di una telecamera è YUV format, che deve essere convertito in una sorta di formato RGB, se si desidera visualizzarlo. Questa conversione è un'operazione piuttosto costosa e abbassa significativamente l'output fps.

Dipende da ciò che si desidera effettivamente fare con l'anteprima della fotocamera. Perché la soluzione migliore è disegnare l'anteprima della fotocamera senza richiamata e apportare alcuni effetti sull'anteprima della fotocamera. Questo è il solito modo di fare argomenti di realismo argomentati.

Ma se è davvero necessario visualizzare l'output manualmente, ci sono diversi modi per farlo. Il tuo esempio non funziona per diversi motivi. Innanzitutto, non stai visualizzando l'immagine. Se chiami questo:

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

rispetto alla tua fotocamera non visualizza affatto l'anteprima, devi visualizzarlo manualmente. Inoltre, non è possibile eseguire operazioni costose nel metodo onPreviewFrame, poiché la durata dei dati è limitata, è sovrascritta nel frame successivo. Un suggerimento, usare setPreviewCallbackWithBuffer, è più veloce, perché riutilizza un buffer e non deve allocare nuova memoria su ciascun frame.

Così si hanno a che fare qualcosa di simile:

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 
} 

Per fare questo lavoro, è necessario chiamare setWillNotDraw(false) nel costruttore della classe o da qualche parte.

In onDraw, è possibile ad esempio applicare paint.setColorFilter(filter), se si desidera modificare i colori. Posso postare un esempio di questo, se vuoi.

Quindi funzionerà, ma le prestazioni saranno basse (meno di 8fps), poiché BitmapFactory.decodeByteArray è lento. Puoi provare a convertire i dati da YUV a RGB con codice nativo e NDK Android, ma è piuttosto complicato.

L'altra opzione è utilizzare openGL ES. È necessario GLSurfaceView, in cui si lega la cornice della telecamera come una trama (in GLSurfaceView implementare Camera.previewCallback, in modo da utilizzare onPreviewFrame allo stesso modo della superficie normale). Ma c'è lo stesso problema, è necessario convertire i dati YUV.C'è una possibilità: è possibile visualizzare solo i dati di luminanza dall'anteprima (immagine in scala di grigi) piuttosto velocemente, perché la prima metà dell'array di byte in YUV è solo dati di luminanza senza colori. Così il onPreviewFrame si utilizza arraycopy copiare la prima metà della matrice, e poi si associa il tessuto in questo modo:

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

Non puoi ottenere circa 16-18 fps in questo modo e si può usare openGL per fare alcuni filtri . Posso mandarti qualche altro codice per questo, se vuoi, ma è troppo lungo per inserirlo qui ...

Per ulteriori informazioni, è possibile vedere il mio simillar question, ma non c'è una buona soluzione sia ...

+0

Si dice che i dati YUV devono essere convertiti in dati RGB per essere visualizzabili e ciò equivale a circa 8 fps. Che cosa fa il framework Android per ottenere una frequenza di visualizzazione così elevata (nessun filtro applicato)? Pensi che stiano avvolgendo tutto in openGL ES per ottenere circa 20 fps? :) – poitroae

+0

Più probabilmente, non stanno usando API Android :) Potrebbe essere scritto in codice nativo o qualcosa del genere (è solo un'ipotesi) ... Ma Android è opensource, quindi puoi provare a scoprire come il loro codice effettivamente funziona :) –

+0

Jaa-c, mi chiedevo se sarebbe stato possibile per voi di pubblicare un esempio usando il codice che hai scritto sopra (non l'openGL). Non riesco a capire come aggiungere tutto questo per funzionare correttamente, e capisco che questa è una domanda più vecchia, ma non è molto che io possa trovare su Google a riguardo. Oppure puoi fare un mini tutorial su quali classi prendere per implementarlo? –

Problemi correlati