web-dev-qa-db-fra.com

Comment obtenir des données de prévisualisation brutes d'objet Camera au moins 15 images par seconde sous Android?

Je dois obtenir des données d'aperçu brutes de l'objet Camera au moins 15 images par seconde , mais je ne peux obtenir une image qu'en 110 millisecondes, ce qui signifie que je ne peux obtenir que 9 images par seconde. Je briefe mon code ci-dessous.

Camera mCamera = Camera.open();
Camera.Parameters parameters = mCamera.getParameters();
parameters.setPreviewFrameRate(30);
parameters.setPreviewFpsRange(15000,30000);
mCamera.setParameters(parameters);
mCamera.addCallbackBuffer(new byte[dataBufferSize]);
//dataBufferSize stands for the byte size for a picture frame
mCamera.addCallbackBuffer(new byte[dataBufferSize]);
mCamera.addCallbackBuffer(new byte[dataBufferSize]);
mCamera.setPreviewDisplay(videoCaptureViewHolder);
//videoCaptureViewHolder is a SurfaceHolder object
mCamera.setPreviewCallbackWithBuffer(new Camera.PreviewCallback() {
  private long timestamp=0;
  public synchronized void onPreviewFrame(byte[] data, Camera camera) {
    Log.v("CameraTest","Time Gap = "+(System.currentTimeMillis()-timestamp));
    timestamp=System.currentTimeMillis();
    //do picture data process
    camera.addCallbackBuffer(data);
    return;
  }
}
mCamera.startPreview();

Dans le code expliqué ci-dessus, dataBufferSize et videoCaptureViewHolder sont définis et calculés ou affectés dans d'autres instructions.

Je lance mon code, je peux voir un aperçu à l'écran et je reçois le journal ci-dessous:

...
V/CameraTest( 5396): Time Gap = 105
V/CameraTest( 5396): Time Gap = 112
V/CameraTest( 5396): Time Gap = 113
V/CameraTest( 5396): Time Gap = 115
V/CameraTest( 5396): Time Gap = 116
V/CameraTest( 5396): Time Gap = 113
V/CameraTest( 5396): Time Gap = 115
...

Cela signifie que onPreviewFrame (données byte [], Camera camera) est appelé toutes les 110 millisecondes afin que je ne puisse obtenir plus de 9 images par seconde. Et quel que soit le taux de trame de prévisualisation que j'ai défini par problème setPreviewFrameRate () et quelle plage de prévisualisation Fps que j'ai définie par problème setPreviewFpsRange () , le journal est identique.

Est-ce que quelqu'un pourrait m'aider à résoudre ce problème? J'ai besoin d'obtenir des données de prévisualisation brutes à partir de l'objet Caméra au moins 15 images par seconde. Merci d'avance.

Je mets tout mon code ci-dessous.

CameraTest.Java

package test.cameratest;

import Java.io.IOException;
import Java.util.Iterator;
import Java.util.List;
import Android.app.Activity;
import Android.graphics.ImageFormat;
import Android.hardware.Camera;
import Android.hardware.Camera.ErrorCallback;
import Android.hardware.Camera.Parameters;
import Android.hardware.Camera.Size;
import Android.os.Bundle;
import Android.util.Log;
import Android.view.SurfaceHolder;
import Android.view.SurfaceView;
import Android.view.SurfaceHolder.Callback;

public class CameraTestActivity extends Activity {
    SurfaceView mVideoCaptureView;
    Camera mCamera;
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        mVideoCaptureView = (SurfaceView) findViewById(R.id.video_capture_surface);
        SurfaceHolder videoCaptureViewHolder = mVideoCaptureView.getHolder();
        videoCaptureViewHolder.setType(SurfaceHolder.SURFACE_TYPE_Push_BUFFERS);
        videoCaptureViewHolder.addCallback(new Callback() {
            public void surfaceDestroyed(SurfaceHolder holder) {
            }

            public void surfaceCreated(SurfaceHolder holder) {
                startVideo();
            }

            public void surfaceChanged(SurfaceHolder holder, int format,
                    int width, int height) {
            }
        });
    }
    private void startVideo() {
        SurfaceHolder videoCaptureViewHolder = null;
        try {
            mCamera = Camera.open();
        } catch (RuntimeException e) {
            Log.e("CameraTest", "Camera Open filed");
            return;
        }
        mCamera.setErrorCallback(new ErrorCallback() {
            public void onError(int error, Camera camera) {
            }
        }); 
        Camera.Parameters parameters = mCamera.getParameters();
        parameters.setPreviewFrameRate(30);
        parameters.setPreviewFpsRange(15000,30000);
        List<int[]> supportedPreviewFps=parameters.getSupportedPreviewFpsRange();
        Iterator<int[]> supportedPreviewFpsIterator=supportedPreviewFps.iterator();
        while(supportedPreviewFpsIterator.hasNext()){
            int[] tmpRate=supportedPreviewFpsIterator.next();
            StringBuffer sb=new StringBuffer();
            sb.append("supportedPreviewRate: ");
            for(int i=tmpRate.length,j=0;j<i;j++){
                sb.append(tmpRate[j]+", ");
            }
            Log.v("CameraTest",sb.toString());
        }

        List<Size> supportedPreviewSizes=parameters.getSupportedPreviewSizes();
        Iterator<Size> supportedPreviewSizesIterator=supportedPreviewSizes.iterator();
        while(supportedPreviewSizesIterator.hasNext()){
            Size tmpSize=supportedPreviewSizesIterator.next();
            Log.v("CameraTest","supportedPreviewSize.width = "+tmpSize.width+"supportedPreviewSize.height = "+tmpSize.height);
        }

        mCamera.setParameters(parameters);
        if (null != mVideoCaptureView)
            videoCaptureViewHolder = mVideoCaptureView.getHolder();
        try {
            mCamera.setPreviewDisplay(videoCaptureViewHolder);
        } catch (Throwable t) {
        }
        Log.v("CameraTest","Camera PreviewFrameRate = "+mCamera.getParameters().getPreviewFrameRate());
        Size previewSize=mCamera.getParameters().getPreviewSize();
        int dataBufferSize=(int)(previewSize.height*previewSize.width*
                               (ImageFormat.getBitsPerPixel(mCamera.getParameters().getPreviewFormat())/8.0));
        mCamera.addCallbackBuffer(new byte[dataBufferSize]);
        mCamera.addCallbackBuffer(new byte[dataBufferSize]);
        mCamera.addCallbackBuffer(new byte[dataBufferSize]);
        mCamera.setPreviewCallbackWithBuffer(new Camera.PreviewCallback() {
            private long timestamp=0;
            public synchronized void onPreviewFrame(byte[] data, Camera camera) {
                Log.v("CameraTest","Time Gap = "+(System.currentTimeMillis()-timestamp));
                timestamp=System.currentTimeMillis();
                try{
                    camera.addCallbackBuffer(data);
                }catch (Exception e) {
                    Log.e("CameraTest", "addCallbackBuffer error");
                    return;
                }
                return;
            }
        });
        try {
            mCamera.startPreview();
        } catch (Throwable e) {
            mCamera.release();
            mCamera = null;
            return;
        }
    }
    private void stopVideo() {
        if(null==mCamera)
            return;
        try {
            mCamera.stopPreview();
            mCamera.setPreviewDisplay(null);
            mCamera.setPreviewCallbackWithBuffer(null);
            mCamera.release();
        } catch (IOException e) {
            e.printStackTrace();
            return;
        }
        mCamera = null;
    }
    public void finish(){
        stopVideo();
        super.finish();
    };
}

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:Android="http://schemas.Android.com/apk/res/Android"
      package="test.cameratest"
      Android:versionCode="1"
      Android:versionName="1.0">
    <uses-sdk Android:minSdkVersion="9" Android:targetSdkVersion="10" Android:maxSdkVersion="10"/>
    <uses-permission Android:name="Android.permission.CAMERA" />
    <uses-permission Android:name="Android.permission.INTERNET" />
    <uses-permission Android:name="Android.permission.VIBRATE" />
    <uses-permission Android:name="Android.permission.ACCESS_WIFI_STATE" />
    <uses-permission Android:name="Android.permission.WAKE_LOCK" />
    <uses-permission Android:name="Android.permission.RECORD_AUDIO" />
    <uses-permission Android:name="Android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>
    <uses-permission Android:name="Android.permission.READ_CONTACTS"/>
    <uses-permission Android:name="Android.permission.MODIFY_AUDIO_SETTINGS"/>
    <uses-permission Android:name="Android.permission.ACCESS_NETWORK_STATE"/>
    <uses-permission Android:name="Android.permission.PROCESS_OUTGOING_CALLS"/>
    <uses-permission Android:name="Android.permission.CALL_PHONE"/>
    <uses-permission Android:name="Android.permission.BOOT_COMPLETED"/>
    <uses-permission Android:name="Android.permission.WRITE_EXTERNAL_STORAGE"/>
    <uses-permission Android:name="Android.permission.WRITE_SETTINGS" />    
    <application Android:icon="@drawable/icon" Android:label="@string/app_name">
        <activity Android:name=".CameraTestActivity"
                  Android:label="@string/app_name">
            <intent-filter>
                <action Android:name="Android.intent.action.MAIN" />
                <category Android:name="Android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>
</manifest>
30
EricOops

Je suis effrayé, vous ne pouvez pas. Le paramètre de prévisualisation du nombre d'images par seconde est un indice pour l'application de la caméra (qui s'exécute dans un processus séparé) - et il est libre de l'accepter ou de l'ignorer en silence. Il n’est pas non plus lié à la récupération du cadre de prévisualisation

Lorsque vous demandez un aperçu de l'image, vous indiquez simplement à une application externe que vous souhaitez l'avoir. La mémoire tampon est allouée dans l'application Caméra, puis transmise à votre activité via le segment de mémoire mmaped - cela prend du temps. 

Vous pouvez obtenir les performances souhaitées sur certains appareils, mais pas nécessairement sur ceux avec lesquels vous jouez. 

Si vous avez besoin d'une cadence d'images définie, vous devrez capturer une vidéo, puis analyser/décompresser le flux binaire résultant. 

7

Mon expérience avec l'appareil photo a été fastidieuse et dépendante du matériel. Essayez donc de l'exécuter sur un autre matériel si vous le pouvez. 

Cela pourrait également valoir la peine d’essayer quelques réglages supplémentaires de l’appareil photo.

Merci d’avoir inclus un exemple de code.

6
leorleor

Ca ne devrait pas être un problème. Mon application androangelo (disponible sur le marché) affiche jusqu'à 30 images par seconde (au moins j'ai mis en place un frein de vitesse pour le ralentir).

Veuillez vérifier attentivement si votre journal est rempli d'instructions de ramasse-miettes. C'est le cas si trop peu de tampons sont ajoutés. Cela avait été le truc pour moi. Au moins je suis venu ajouter 20! tampons à la caméra.

Ensuite, le traitement de chaque image doit avoir lieu dans un thread séparé. Lorsqu'une image est dans le fil pour le traitement, le rappel doit ignorer l'image en cours.

4
Kai

À mon sens, Android ne permet pas à l'utilisateur de définir un nombre d'images par seconde fixe, et ne garantit pas que la valeur des fps que vous spécifiez sera respectée, en raison du temps de pose des images défini par le matériel ou le firmware de l'appareil photo. La fréquence d'images que vous observez peut être fonction des conditions d'éclairage. Par exemple, un certain téléphone peut vous donner un taux d'aperçu de 30 images par seconde à la lumière du jour, mais seulement 7 images par seconde si vous filmez dans des conditions de faible luminosité.

2
Yevgeni Litvin

Une chose qui semble augmenter la fluidité de l'aperçu, sinon nécessairement le FPS réel, est de définir le previewFormat sur YV12 si cette option est prise en charge. C'est moins d'octets à copier, aligné sur 16 octets et éventuellement optimisé d'autres manières:

    // PREVIEW FORMATS
    List<Integer> supportedPreviewFormats = parameters.getSupportedPreviewFormats();
    Iterator<Integer> supportedPreviewFormatsIterator = supportedPreviewFormats.iterator();
    while(supportedPreviewFormatsIterator.hasNext()){
        Integer previewFormat =supportedPreviewFormatsIterator.next();
        // 16 ~ NV16 ~ YCbCr
        // 17 ~ NV21 ~ YCbCr ~ DEFAULT
        // 4  ~ RGB_565
        // 256~ JPEG
        // 20 ~ YUY2 ~ YcbCr ...
        // 842094169 ~ YV12 ~ 4:2:0 YCrCb comprised of WXH Y plane, W/2xH/2 Cr & Cb. see documentation
        Log.v("CameraTest","Supported preview format:"+previewFormat);
        if (previewFormat == ImageFormat.YV12) {
            parameters.setPreviewFormat(previewFormat);
            Log.v("CameraTest","SETTING FANCY YV12 FORMAT");                
        }
    }

http://developer.Android.com/reference/Android/graphics/ImageFormat.html#YV12 décrit le format. Cela plus quelques tampons de réserve me donnent des "intervalles de temps" aussi bas que 80 ... qui ne sont toujours pas "assez bons", mais ... meilleurs? (En fait, j'en ai un à 69 ans ... mais en réalité, ils sont plutôt autour de 90 en moyenne). Vous ne savez pas à quel point l'exploitation ralentit les choses?

Régler la taille de preview à 320x240 (au lieu de 1280x720) permet d’obtenir une plage de 50 à 70 ms ... alors c’est peut-être ce que vous devez faire? Certes, ces petites données peuvent être beaucoup moins utiles.

// tous testés sur Nexus4

1
Kaolin Fire

Je déclare généralement un lockCameraUse booléen global. La fonction de rappel ressemble généralement à ceci.

  public void onPreviewFrame(byte[] data, Camera camera) {
    if (lockCameraUse) {
      camera.addCallbackBuffer(data);
      return;
    }
    lockCameraUse = true;
    // processinng data

    // done processing data
    camera.addCallbackBuffer(data);
    lockCameraUse = false;
    return;
  }
0
DXM