2015-08-18 6 views
5

Attualmente sto lavorando con camera2Basic e sto provando a ottenere i dati di ogni frame per eseguire un po 'di elaborazione dell'immagine. Sto usando l'API di Camera2 in Android5.0, tutto è perfetto quando si fa solo l'anteprima della fotocamera ed è fluido. Ma l'anteprima si blocca quando uso il callback ImageReader.OnImageAvailableListener per ottenere ogni frame di dati, questo causa una cattiva esperienza utente. il seguente è i miei codici correlati:Come ottenere ciascun frame utilizzando l'API camera2 in Android5.0 in tempo reale

Questa è la configurazione per fotocamera e ImageReader, ho impostato il formato di immagine è YUV_420_888

public<T> Size setUpCameraOutputs(CameraManager cameraManager,Class<T> kClass, int width, int height) { 
    boolean flagSuccess = true; 
    try { 
     for (String cameraId : cameraManager.getCameraIdList()) { 
      CameraCharacteristics characteristics = cameraManager.getCameraCharacteristics(cameraId); 
      // choose the front or back camera 
      if (FLAG_CAMERA.BACK_CAMERA == mChosenCamera &&   
        CameraCharacteristics.LENS_FACING_BACK != characteristics.get(CameraCharacteristics.LENS_FACING)) { 
       continue; 
      } 
      if (FLAG_CAMERA.FRONT_CAMERA == mChosenCamera && 
        CameraCharacteristics.LENS_FACING_FRONT != characteristics.get(CameraCharacteristics.LENS_FACING)) { 
       continue; 
      } 
      StreamConfigurationMap map = characteristics.get(
        CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP); 

      Size largestSize = Collections.max(
        Arrays.asList(map.getOutputSizes(ImageFormat.YUV_420_888)), 
        new CompareSizesByArea()); 

      mImageReader = ImageReader.newInstance(largestSize.getWidth(), largestSize.getHeight(), 
        ImageFormat.YUV_420_888, 3); 

      mImageReader.setOnImageAvailableListener(mOnImageAvailableListener, mBackgroundHandler); 
      ... 
      mCameraId = cameraId; 
     } 
    } catch (CameraAccessException e) { 
     e.printStackTrace(); 
    } catch (NullPointerException e) { 

    } 
    ...... 
} 

Quando la fotocamera ha aperto con successo, a creare un CameraCaptureSession per la macchina fotografica anteprima

private void createCameraPreviewSession() { 
    if (null == mTexture) { 
     return; 
    } 

    // We configure the size of default buffer to be the size of camera preview we want. 
    mTexture.setDefaultBufferSize(mPreviewSize.getWidth(), mPreviewSize.getHeight()); 

    // This is the output Surface we need to start preview 
    Surface surface = new Surface(mTexture); 

    // We set up a CaptureRequest.Builder with the output Surface. 
    try { 
     mPreviewRequestBuilder = 
       mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW); 
     mPreviewRequestBuilder.addTarget(mImageReader.getSurface()); 
     mPreviewRequestBuilder.addTarget(surface); 

     // We create a CameraCaptureSession for camera preview 
     mCameraDevice.createCaptureSession(Arrays.asList(surface, mImageReader.getSurface()), 
       new CameraCaptureSession.StateCallback() { 

        @Override 
        public void onConfigured(CameraCaptureSession session) { 
         if (null == mCameraDevice) { 
          return; 
         } 

         // when the session is ready, we start displaying the preview 
         mCaptureSession = session; 

         // Finally, we start displaying the camera preview 
         mPreviewRequest = mPreviewRequestBuilder.build(); 
         try { 
          mCaptureSession.setRepeatingRequest(mPreviewRequest, 
            mCaptureCallback, mBackgroundHandler); 
         } catch (CameraAccessException e) { 
          e.printStackTrace(); 
         } 
        } 

        @Override 
        public void onConfigureFailed(CameraCaptureSession session) { 

        } 
       }, null); 
    } catch (CameraAccessException e) { 
     e.printStackTrace(); 
    } 
} 

l'ultimo è il ImageReader.OnImageAvailableListener richiamata

private final ImageReader.OnImageAvailableListener mOnImageAvailableListener = new ImageReader.OnImageAvailableListener() { 
      @Override 
      public void onImageAvailable(ImageReader reader) { 
       Log.d(TAG, "The onImageAvailable thread id: " + Thread.currentThread().getId()); 
       Image readImage = reader.acquireLatestImage(); 
       readImage.close(); 
      } 
     }; 

Forse faccio la configurazione sbagliata ma provo più volte e non funziona. Forse c'è un altro modo per ottenere i dati del frame piuttosto che ImageReader, ma non lo so. Qualcuno sa come ottenere i dati di ogni frame in tempo reale?

+0

Che tipo di elaborazione stai cercando di fare? Penso che se si desidera salvare l'immagine, utilizzare un ImageReader, ma se si desidera eseguire un'elaborazione in tempo reale efficiente, è necessario inviare i dati al buffer di Surface associato a un'allocazione. – rcsumner

+0

@ Sumner, voglio ottenere ogni fotogramma dalla fotocamera e fare il rilevamento del volto piuttosto che salvare l'immagine. Sembra che la soluzione che offri sia buona, puoi fornire maggiori dettagli? – CJZ

+0

... non hahaha davvero, mi dispiace. Solitamente, salvo solo le immagini per i miei scopi. So che implica l'uso di RenderScript, però. Guarda la documentazione della classe di allocazione. – rcsumner

risposta

0

sto provando le stesse cose, penso che è possibile modificare il formato come

mImageReader = ImageReader.newInstance(largestSize.getWidth(), 
             largestSize.getHeight(), 
             ImageFormat.FLEX_RGB_888, 3); 

Perché utilizzando il YUV può causare CPU per comprimere i dati e può costare un po 'di tempo. RGB può essere mostrato direttamente sul dispositivo. E rilevare che il volto dell'immagine deve essere inserito in un'altra discussione, devi conoscerlo.

1

Non credo che Chen abbia ragione. Il formato dell'immagine ha quasi 0 effetto sulla velocità dei dispositivi che ho testato. Invece, il problema sembra essere con la dimensione dell'immagine. Su un Xperia Z3 compatta con il formato di immagine YUV_420_888, sto offerto un sacco di opzioni diverse in getOutputSizes metodo s' il StreamConfigurationMap:

[1600x1200, 1280x720, 960x720, 720x480, 640x480, 480x320, 320x240, 176x144] 

Per queste rispettive dimensioni, la massima fps ottengo quando si imposta mImageReader.getSurface() come bersaglio per il mPreviewRequestBuilder sono:

[13, 18, 25, 28, 30, 30, 30, 30 ] 

Così una soluzione è quella di utilizzare una risoluzione più bassa per ottenere il tasso che si desidera. Per i curiosi ... notare che questi tempi non sembrano risentire della linea di

mPreviewRequestBuilder.addTarget(surface); 
... 
    mCameraDevice.createCaptureSession(Arrays.asList(surface, mImageReader.getSurface()), 

ero preoccupato che l'aggiunta la superficie dello schermo potrebbe essere l'aggiunta di testa, ma se tolgo che prima linea e il cambiamento il secondo a

mCameraDevice.createCaptureSession(Arrays.asList(mImageReader.getSurface()), 

quindi vedo che i tempi cambiano di meno di 1 fps. Quindi non sembra importare se stai anche visualizzando l'immagine sullo schermo.

Penso che ci sia semplicemente un sovraccarico nell'API camera2 o nel framework di ImageReader che rende impossibile ottenere la piena velocità che TextureView sta ottenendo chiaramente.

Una delle cose più deludenti è che, se si ritorna alla deprecata API della fotocamera, è possibile ottenere facilmente 30 fps impostando uno PreviewCallback tramite il metodo Camera.setPreviewCallbackWithBuffer.Con questo metodo, sono in grado di ottenere 30 fps indipendentemente dalla risoluzione. Nello specifico, sebbene non offra direttamente 1600x1200, offre 1920x1080 e anche 30fps.

Problemi correlati