2013-02-28 7 views
7

Ho provato a cercare questa domanda molto, ma non ho mai visto nessuna risposta soddisfacente, quindi ora ho un'ultima speranza qui.Come modificare l'orientamento dei frame di byte [] acquisiti tramite callbackPreviewFrame?

Ho una configurazione di callback onPreviewFrame. Che dà un byte[] di frame non elaborati con il formato di anteprima supportato (NV21 con il tipo codificato H.264).

Ora, il problema è la richiamata inizia sempre a dare i fotogrammi byte[] da un orientamento fisso, ogni volta che il dispositivo ruota non riflette i fotogrammi catturati . Ho provato con setDisplayOrientation e setRotation ma questi api stanno riflettendo solo per l'anteprima che non viene affatto visualizzata per i fotogrammi catturati byte [].

docs

Android dice anche, Camera.setDisplayOrientation influenza solo l'anteprima la visualizzazione, non i byte del telaio:

Ciò non pregiudica l'ordine dei byte passato in onPreviewFrame (byte [], Camera), immagini JPEG, o registrate video.

Infine C'è un modo, a qualsiasi livello API, per modificare l'orientamento dei frame byte[]?

risposta

3

Un modo possibile se non si cura del formato è la classe YuvImage per ottenere un buffer JPEG, utilizzare questo buffer per creare un Bitmap e ruotarlo all'angolo corrispondente. Qualcosa del genere:

@Override 
public void onPreviewFrame(byte[] data, Camera camera) { 

    Size previewSize = camera.getParameters().getPreviewSize(); 
    ByteArrayOutputStream baos = new ByteArrayOutputStream(); 
    byte[] rawImage = null; 

    // Decode image from the retrieved buffer to JPEG 
    YuvImage yuv = new YuvImage(data, ImageFormat.NV21, previewSize.width, previewSize.height, null); 
    yuv.compressToJpeg(new Rect(0, 0, previewSize.width, previewSize.height), YOUR_JPEG_COMPRESSION, baos); 
    rawImage = baos.toByteArray(); 

    // This is the same image as the preview but in JPEG and not rotated 
    Bitmap bitmap = BitmapFactory.decodeByteArray(rawImage, 0, rawImage.length); 
    ByteArrayOutputStream rotatedStream = new ByteArrayOutputStream(); 

    // Rotate the Bitmap 
    Matrix matrix = new Matrix(); 
    matrix.postRotate(YOUR_DEFAULT_ROTATION); 

    // We rotate the same Bitmap 
    bitmap = Bitmap.createBitmap(bitmap, 0, 0, previewSize.width, previewSize.height, matrix, false); 

    // We dump the rotated Bitmap to the stream 
    bitmap.compress(CompressFormat.JPEG, YOUR_JPEG_COMPRESSION, rotatedStream); 

    rawImage = rotatedStream.toByteArray(); 

    // Do something we this byte array 
} 
+0

ridurre la risoluzione – cxphong

1

Ho modificato il metodo di questa onPreviewFrame Open Source Android Touch-To-Record library prendere recepire e ridimensionare un frame catturato.

Ho definito "yuvIplImage" come segue nel mio metodo setCameraParams().

IplImage yuvIplImage = IplImage.create(mPreviewSize.height, mPreviewSize.width, opencv_core.IPL_DEPTH_8U, 2); 

Questo è il mio metodo onPreviewFrame():

@Override 
public void onPreviewFrame(byte[] data, Camera camera) 
{ 

    long frameTimeStamp = 0L; 

    if(FragmentCamera.mAudioTimestamp == 0L && FragmentCamera.firstTime > 0L) 
    { 
     frameTimeStamp = 1000L * (System.currentTimeMillis() - FragmentCamera.firstTime); 
    } 
    else if(FragmentCamera.mLastAudioTimestamp == FragmentCamera.mAudioTimestamp) 
    { 
     frameTimeStamp = FragmentCamera.mAudioTimestamp + FragmentCamera.frameTime; 
    } 
    else 
    { 
     long l2 = (System.nanoTime() - FragmentCamera.mAudioTimeRecorded)/1000L; 
     frameTimeStamp = l2 + FragmentCamera.mAudioTimestamp; 
     FragmentCamera.mLastAudioTimestamp = FragmentCamera.mAudioTimestamp; 
    } 

    synchronized(FragmentCamera.mVideoRecordLock) 
    { 
     if(FragmentCamera.recording && FragmentCamera.rec && lastSavedframe != null && lastSavedframe.getFrameBytesData() != null && yuvIplImage != null) 
     { 
      FragmentCamera.mVideoTimestamp += FragmentCamera.frameTime; 

      if(lastSavedframe.getTimeStamp() > FragmentCamera.mVideoTimestamp) 
      { 
       FragmentCamera.mVideoTimestamp = lastSavedframe.getTimeStamp(); 
      } 

      try 
      { 
       yuvIplImage.getByteBuffer().put(lastSavedframe.getFrameBytesData()); 

       IplImage bgrImage = IplImage.create(mPreviewSize.width, mPreviewSize.height, opencv_core.IPL_DEPTH_8U, 4);// In my case, mPreviewSize.width = 1280 and mPreviewSize.height = 720 
       IplImage transposed = IplImage.create(mPreviewSize.height, mPreviewSize.width, yuvIplImage.depth(), 4); 
       IplImage squared = IplImage.create(mPreviewSize.height, mPreviewSize.height, yuvIplImage.depth(), 4); 

       int[] _temp = new int[mPreviewSize.width * mPreviewSize.height]; 

       Util.YUV_NV21_TO_BGR(_temp, data, mPreviewSize.width, mPreviewSize.height); 

       bgrImage.getIntBuffer().put(_temp); 

       opencv_core.cvTranspose(bgrImage, transposed); 
       opencv_core.cvFlip(transposed, transposed, 1); 

       opencv_core.cvSetImageROI(transposed, opencv_core.cvRect(0, 0, mPreviewSize.height, mPreviewSize.height)); 
       opencv_core.cvCopy(transposed, squared, null); 
       opencv_core.cvResetImageROI(transposed); 

       videoRecorder.setTimestamp(lastSavedframe.getTimeStamp()); 
       videoRecorder.record(squared); 
      } 
      catch(com.googlecode.javacv.FrameRecorder.Exception e) 
      { 
       e.printStackTrace(); 
      } 
     } 

     lastSavedframe = new SavedFrames(data, frameTimeStamp); 
    } 
} 

Questo codice utilizza un metodo "YUV_NV21_TO_BGR", che ho trovato da questo link

Fondamentalmente questo metodo viene utilizzato per risolvere, che Chiamo come, "Il problema del Green Devil su Android". Puoi vedere altri sviluppatori Android che affrontano lo stesso problema su altri thread SO. Prima di aggiungere il metodo "YUV_NV21_TO_BGR" quando ho appena preso la trasposizione di YuvIplImage, ancora più importante una combinazione di trasposizione, flip (con o senza ridimensionamento), c'era un output verdastro nel video risultante. Questo metodo "YUV_NV21_TO_BGR" ha salvato il giorno. Grazie a @David Han dall'alto thread dei gruppi di google.

Inoltre, dovresti sapere che tutta questa elaborazione (trasporre, capovolgere e ridimensionare), in onPreviewFrame, richiede molto tempo e ti fa perdere molto tempo nella frequenza dei fotogrammi al secondo (FPS). Quando ho usato questo codice, all'interno del metodo onPreviewFrame, l'FPS risultante del video registrato era sceso a 3 fotogrammi/sec da 30 fps.

Vorrei consigliare di non utilizzare questo approccio. Piuttosto si può andare per l'elaborazione post-registrazione (trasporre, capovolgere e ridimensionare) del file video utilizzando JavaCV in un AsyncTask. Spero che questo ti aiuti.

Problemi correlati