2017-09-22 9 views
0

J'utilise l'API Camera2 d'Android et j'aimerais effectuer un traitement d'image sur les cadres de prévisualisation de la caméra, puis afficher les changements sur l'aperçu (TextureView). À partir de l'exemple courant de camera2video, j'ai installé un ImageReader dans mon openCamera().Android Video Processing - comment connecter la Surface ImageReader à l'aperçu?

mImageReader = ImageReader.newInstance(mVideoSize.getWidth(), 
    mVideoSize.getHeight(), ImageFormat.YUV_420_888, mMaxBufferedImages); 
    mImageReader.setOnImageAvailableListener(mImageAvailable, mBackgroundHandler); 

Dans mon startPreview(), je les ai installé surfaces pour recevoir des trames du CaptureRequest.

SurfaceTexture texture = mTextureView.getSurfaceTexture(); 
    assert texture != null; 
    texture.setDefaultBufferSize(mPreviewSize.getWidth(), mPreviewSize.getHeight()); 
    mPreviewBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW); 

    List<Surface> surfaces = new ArrayList<>(); 

    // Here is where we connect the mPreviewSurface to the mTextureView. 
    mPreviewSurface = new Surface(texture); 
    surfaces.add(mPreviewSurface); 
    mPreviewBuilder.addTarget(mPreviewSurface); 

    // Connect our Image Reader to the Camera to get the preview frames. 
    Surface readerSurface = mImageReader.getSurface(); 
    surfaces.add(readerSurface); 
    mPreviewBuilder.addTarget(readerSurface); 

Ensuite, je vais modifier les données d'image dans le rappel OnImageAvailableListener(). 2 instances de surface

ImageReader.OnImageAvailableListener mImageAvailable = new ImageReader.OnImageAvailableListener() { 
    @Override 
    public void onImageAvailable(ImageReader reader) { 
     try { 
      Image image = reader.acquireLatestImage(); 
      if (image == null) 
       return; 

      final Image.Plane[] planes = image.getPlanes(); 

      // Do something to the pixels. 
      // Black out part of the image. 
      ByteBuffer y_data_buffer = planes[0].getBuffer(); 
      byte[] y_data = new byte[y_data_buffer.remaining()]; 
      y_data_buffer.get(y_data); 
      byte y_value; 
      for (int row = 0; row < image.getHeight()/2; row++) { 
       for (int col = 0; col < image.getWidth()/2; col++) { 
        y_value = y_data[row * image.getWidth() + col]; 
        y_value = 0; 
        y_data[row * image.getWidth() + col] = y_value; 
       } 
      } 

      image.close(); 
     } catch (IllegalStateException e) { 
      Log.d(TAG, "mImageAvailable() Too many images acquired"); 
     } 
    } 
}; 

Comme je le comprends maintenant j'envoie des images, une pour mTextureView et l'autre pour mon ImageReader.

Comment faire pour que mTextureView utilise la même Surface que le ImageReader, ou devrais-je manipuler les données d'image directement depuis la Surface de mTextureView?

Merci

Répondre

1

Si vous souhaitez afficher uniquement la sortie modifiée, alors je ne sais pas pourquoi vous avez deux sorties configurées (le TextureView et le ImageReader).

En général, si vous voulez quelque chose comme

camera -> in-app edits -> display 

Vous avez plusieurs options, en fonction des types de modifications que vous voulez, et divers compromis entre la facilité de codage, la performance, et ainsi de suite.

L'une des options les plus efficaces consiste à effectuer vos modifications en tant que shader OpenGL. Dans ce cas, un GLSurfaceView est probablement l'option la plus simple. Créez un objet SurfaceTexture avec un ID de texture inutilisé dans le contexte EGL de GLSurfaceView et transmettez une Surface créée à partir de SurfaceTexture à la session et aux demandes de la caméra. Ensuite, dans la méthode de dessin SurfaceView, appelez la méthode updateTexImage() de SurfaceTexture, puis utilisez l'ID de texture pour rendre votre sortie comme vous le souhaitez.

Cela nécessite beaucoup de code OpenGL, donc si vous ne le connaissez pas, cela peut être difficile.

Vous pouvez également utiliser RenderScript pour un effet similaire; là, vous aurez une sortie SurfaceView ou TextureView, puis un script RenderScript qui lit à partir d'une allocation d'entrée de la caméra et écrit dans une allocation de sortie à la vue; vous pouvez créer de telles allocations à partir d'une surface. L'exemple d'application Google HdrViewfinderDemo camera2 utilise cette approche. C'est beaucoup moins cool. Troisièmement, vous pouvez simplement utiliser un ImageReader comme vous le faites maintenant, mais vous devrez faire beaucoup de conversion vous-même pour l'écrire à l'écran. L'option la plus simple (mais la plus lente) est d'obtenir un Canvas à partir d'un SurfaceView ou d'un ImageView, et d'y écrire des pixels un par un. Ou vous pouvez le faire via le NDK ANativeWindow, qui est plus rapide mais nécessite l'écriture de code JNI et vous oblige toujours à effectuer des conversions YUV-> RVB vous-même (ou utiliser des API non documentées pour insérer YUV dans l'ANativeWindow et espérer que cela fonctionne).

+0

Donc, si j'utilise l'itinéraire ANEDWindow NDK, la raison de YUV-> RGB est parce que TextureView nécessite le format RVB? – jimbo

+0

Eh bien, il est plus que le NDK expose actuellement que les formats d'image RVB pour sélectionner: https://developer.android.com/ndk/reference/hardware__buffer_8h.html#a06fc87d81c62e9abb8790b6e5713c55ba521c192d1f9b5e85aa68b86ead204fbe –