Comment définir correctement l'orientation de la caméra Android?

89

Je souhaite définir l'orientation de la caméra en fonction de l'orientation de l'appareil sous Android, mais rien ne semble fonctionner. J'ai essayé de faire pivoter la Surface ainsi que les paramètres de l'appareil photo, mais l'aperçu de l'appareil photo en mode portrait est toujours à l'envers. J'aurais besoin de le faire pivoter de 90 degrés dans le sens des aiguilles d'une montre pour que ce soit correct. Voici le code que j'utilise actuellement qui ne fonctionne qu'en mode paysage.

    SurfaceHolder.Callback surfaceCallback = new SurfaceHolder.Callback() {

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
        camera.stopPreview();
        camera.release();
        camera = null;
    }

    @Override
    public void surfaceCreated(SurfaceHolder holder) {          
        initCamera();           
    }

    private Size getOptimalPreviewSize(List<Size> sizes, int w, int h) {
        final double ASPECT_TOLERANCE = 0.2;
        double targetRatio = (double) w / h;
        if (sizes == null)
            return null;

        Size optimalSize = null;
        double minDiff = Double.MAX_VALUE;

        int targetHeight = h;

        // Try to find an size match aspect ratio and size
        for (Size size : sizes) {
            Log.d(TAG, "Checking size " + size.width + "w " + size.height
                    + "h");
            double ratio = (double) size.width / size.height;
            if (Math.abs(ratio - targetRatio) > ASPECT_TOLERANCE)
                continue;
            if (Math.abs(size.height - targetHeight) < minDiff) {
                optimalSize = size;
                minDiff = Math.abs(size.height - targetHeight);
            }
        }

        // Cannot find the one match the aspect ratio, ignore the
        // requirement
        if (optimalSize == null) {
            minDiff = Double.MAX_VALUE;
            for (Size size : sizes) {
                if (Math.abs(size.height - targetHeight) < minDiff) {
                    optimalSize = size;
                    minDiff = Math.abs(size.height - targetHeight);
                }
            }
        }
        return optimalSize;
    }

    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
        Camera.Parameters parameters = camera.getParameters();

        List<Size> sizes = parameters.getSupportedPreviewSizes();
        Size optimalSize = getOptimalPreviewSize(sizes, width, height);         
        Log.d(TAG, "Surface size is " + width + "w " + height + "h");
        Log.d(TAG, "Optimal size is " + optimalSize.width + "w " + optimalSize.height + "h");           
        parameters.setPreviewSize(optimalSize.width, optimalSize.height);           
        // parameters.setPreviewSize(width, height);            
        camera.setParameters(parameters);
        camera.startPreview();
    }
};  
Abhinav
la source
6
AFAIK, l'aperçu de l'appareil photo ne fonctionne vraiment qu'en mode paysage, au moins pour la version 2.2 et antérieure. Je suppose que c'est la raison pour laquelle les activités qui font un aperçu de la caméra ont tendance à masquer la barre de notification du système et n'ont pas de titres ... bien qu'elles soient apparemment portrait, je pense qu'elles sont "vraiment" paysage.
Reuben Scratton

Réponses:

71

De l'autre membre et mon problème:

Le problème de rotation de la caméra dépend de différents appareils et de certaines versions.

Version 1.6: pour résoudre le problème de rotation, et c'est bon pour la plupart des appareils

if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT)
        {   
            p.set("orientation", "portrait");
            p.set("rotation",90);
        }
        if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE)
        {                               
            p.set("orientation", "landscape");          
            p.set("rotation", 90);
        }

Version 2.1: dépend du type de périphérique, par exemple, impossible de résoudre le problème avec XPeria X10, mais c'est bon pour X8 et Mini

Camera.Parameters parameters = camera.getParameters();
parameters.set("orientation", "portrait");
camera.setParameters(parameters);

Version 2.2: pas pour tous les appareils

camera.setDisplayOrientation(90);

http://code.google.com/p/android/issues/detail?id=1193#c42

LTEHUB
la source
3
qu'entendez-vous par la version ici?
Md. Sulayman
31

À partir des Javadocs pour setDisplayOrientation(int)(nécessite le niveau d'API 9):

 public static void setCameraDisplayOrientation(Activity activity,
         int cameraId, android.hardware.Camera camera) {
     android.hardware.Camera.CameraInfo info =
             new android.hardware.Camera.CameraInfo();
     android.hardware.Camera.getCameraInfo(cameraId, info);
     int rotation = activity.getWindowManager().getDefaultDisplay()
             .getRotation();
     int degrees = 0;
     switch (rotation) {
         case Surface.ROTATION_0: degrees = 0; break;
         case Surface.ROTATION_90: degrees = 90; break;
         case Surface.ROTATION_180: degrees = 180; break;
         case Surface.ROTATION_270: degrees = 270; break;
     }

     int result;
     if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
         result = (info.orientation + degrees) % 360;
         result = (360 - result) % 360;  // compensate the mirror
     } else {  // back-facing
         result = (info.orientation - degrees + 360) % 360;
     }
     camera.setDisplayOrientation(result);
 }
Jason Robinson
la source
1
@Derzu La classe CameraInfon'a pas été introduite avant le niveau d'API 9, donc la méthode que j'ai publiée nécessite le niveau d'API 9.
Jason Robinson
2
result = (360 - result) % 360; // compensate the mirrordevraient être supprimées , les images de la caméra frontale sont sinon tournées incorrectement
stevo.mit
@ stevo.mit l'avez-vous vérifié sur plusieurs appareils? J'ai utilisé ce code pour la rotation plusieurs fois et je n'ai pas rencontré de rotation incorrecte.
Jason Robinson
2
@ Jason Robinson J'ai une liste de modèles sur lesquels le niveau d'API est supérieur à 9 mais cette méthode n'a toujours aucun effet. Je ne sais pas comment gérer son problème matériel. Liste des périphériques rotation_issue_models = Arrays.asList ("GT-S5360", "GT-S6802", "GT-S5830C", "GT-S5830I", "DROID2", "GLOBAL", "XT557", "Desire HD", " PC36100 "," GT-I9000 "," ADR6350 "," Mi-One Plus "," SGH-T989 "," GT-I9100 "," GT-I9001 ");
Vikram
1
@AbdulMohsin Regardez developer.android.com/reference/android/hardware/… , en particulier CAMERA_FACING_BACK et CAMERA_FACING_FRONT.
Jason Robinson
25

Cette solution fonctionnera pour toutes les versions d'Android. Vous pouvez utiliser la réflexion en Java pour le faire fonctionner sur tous les appareils Android:

Fondamentalement, vous devez créer un wrapper de réflexion pour appeler Android 2.2 setDisplayOrientation, au lieu d'appeler la méthode spécifique.

La méthode:

    protected void setDisplayOrientation(Camera camera, int angle){
    Method downPolymorphic;
    try
    {
        downPolymorphic = camera.getClass().getMethod("setDisplayOrientation", new Class[] { int.class });
        if (downPolymorphic != null)
            downPolymorphic.invoke(camera, new Object[] { angle });
    }
    catch (Exception e1)
    {
    }
}

Et au lieu d'utiliser camera.setDisplayOrientation (x), utilisez setDisplayOrientation (camera, x) :

    if (Integer.parseInt(Build.VERSION.SDK) >= 8)
        setDisplayOrientation(mCamera, 90);
    else
    {
        if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT)
        {
            p.set("orientation", "portrait");
            p.set("rotation", 90);
        }
        if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE)
        {
            p.set("orientation", "landscape");
            p.set("rotation", 90);
        }
    }   
hnviet
la source
1
La partie else fonctionne sur certains appareils 2.1 mais pas TOUS (voir l'explication de maydenec ci-dessus).
Eric Chen
1
Je pense que p est de type Camera.Parameters. Essayez d'ajouter la ligne suivante:Camera.Parameters p = camera.getParameters();
Sehrish Khan
6

J'ai rencontré le problème lorsque j'utilisais ZBar pour la numérisation dans les onglets. Problème d'orientation de la caméra. En utilisant le code ci-dessous, j'ai pu résoudre le problème. Il ne s'agit pas de l'extrait de code complet, veuillez ne prendre que de l'aide.

 public void surfaceChanged(SurfaceHolder holder, int format, int width,
                               int height) {
     if (isPreviewRunning) {
            mCamera.stopPreview();
        }

 setCameraDisplayOrientation(mCamera);

        previewCamera();

    }



 public void previewCamera() {

        try {
            // Hard code camera surface rotation 90 degs to match Activity view
            // in portrait
            mCamera.setPreviewDisplay(mHolder);
            mCamera.setPreviewCallback(previewCallback);
            mCamera.startPreview();
            mCamera.autoFocus(autoFocusCallback);
            isPreviewRunning = true;
        } catch (Exception e) {
            Log.d("DBG", "Error starting camera preview: " + e.getMessage());
        }


    }


public void setCameraDisplayOrientation(android.hardware.Camera camera) {
        Camera.Parameters parameters = camera.getParameters();

        android.hardware.Camera.CameraInfo camInfo =
                new android.hardware.Camera.CameraInfo();
        android.hardware.Camera.getCameraInfo(getBackFacingCameraId(), camInfo);


        Display display = ((WindowManager) context.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
        int rotation = display.getRotation();
        int degrees = 0;
        switch (rotation) {
            case Surface.ROTATION_0:
                degrees = 0;
                break;
            case Surface.ROTATION_90:
                degrees = 90;
                break;
            case Surface.ROTATION_180:
                degrees = 180;
                break;
            case Surface.ROTATION_270:
                degrees = 270;
                break;
        }

        int result;
        if (camInfo.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
            result = (camInfo.orientation + degrees) % 360;
            result = (360 - result) % 360;  // compensate the mirror
        } else {  // back-facing
            result = (camInfo.orientation - degrees + 360) % 360;
        }
        camera.setDisplayOrientation(result);
    }




    private int getBackFacingCameraId() {
        int cameraId = -1;
        // Search for the front facing camera
        int numberOfCameras = Camera.getNumberOfCameras();
        for (int i = 0; i < numberOfCameras; i++) {
            Camera.CameraInfo info = new Camera.CameraInfo();
            Camera.getCameraInfo(i, info);
            if (info.facing == Camera.CameraInfo.CAMERA_FACING_BACK) {

                cameraId = i;
                break;
            }
        }
        return cameraId;
    }
DeepakPanwar
la source
5

J'ai finalement résolu ce problème à l'aide de l'application appareil photo de Google. Il obtient l'orientation du téléphone à l'aide d'un capteur, puis définit la balise EXIF ​​de manière appropriée. Le JPEG qui sort de l'appareil photo n'est pas orienté automatiquement.

En outre, l'aperçu de l'appareil photo ne fonctionne correctement qu'en mode paysage. Si vous avez besoin que votre disposition d'activité soit orientée en portrait, vous devrez le faire manuellement en utilisant la valeur du capteur d'orientation.

Abhinav
la source
2
hé, comment obtenir l'orientation de la caméra à l'aide du capteur d'orientation ?? s'il vous plaît partager votre code ...
Rishi
@Rishi voir ce lien stackoverflow.com/questions/9055460/…
PiyushMishra
4
Merci pour l'aide, mais mon problème est lorsque je prends une photo sur des téléphones symsung en mode portrait à ce moment-là, la photo est affichée avec une rotation de 90 degrés sur mon écran. Donc, j'essaie d'obtenir l'orientation de la caméra, donc, je fais pivoter la photo de 90 degrés pour le mode portrait
Rishi
4

Ce problème a été résolu il y a longtemps mais j'ai rencontré quelques difficultés pour assembler toutes les pièces donc voici ma solution finale, j'espère que cela aidera les autres:

public void startPreview() {
        try {
            Log.i(TAG, "starting preview: " + started);

            // ....
            Camera.CameraInfo camInfo = new Camera.CameraInfo();
            Camera.getCameraInfo(cameraIndex, camInfo);
            int cameraRotationOffset = camInfo.orientation;
            // ...

            Camera.Parameters parameters = camera.getParameters();
            List<Camera.Size> previewSizes = parameters.getSupportedPreviewSizes();
            Camera.Size previewSize = null;
            float closestRatio = Float.MAX_VALUE;

            int targetPreviewWidth = isLandscape() ? getWidth() : getHeight();
            int targetPreviewHeight = isLandscape() ? getHeight() : getWidth();
            float targetRatio = targetPreviewWidth / (float) targetPreviewHeight;

            Log.v(TAG, "target size: " + targetPreviewWidth + " / " + targetPreviewHeight + " ratio:" + targetRatio);
            for (Camera.Size candidateSize : previewSizes) {
                float whRatio = candidateSize.width / (float) candidateSize.height;
                if (previewSize == null || Math.abs(targetRatio - whRatio) < Math.abs(targetRatio - closestRatio)) {
                    closestRatio = whRatio;
                    previewSize = candidateSize;
                }
            }

            int rotation = getWindowManager().getDefaultDisplay().getRotation();
            int degrees = 0;
            switch (rotation) {
            case Surface.ROTATION_0:
                degrees = 0;
                break; // Natural orientation
            case Surface.ROTATION_90:
                degrees = 90;
                break; // Landscape left
            case Surface.ROTATION_180:
                degrees = 180;
                break;// Upside down
            case Surface.ROTATION_270:
                degrees = 270;
                break;// Landscape right
            }
            int displayRotation;
            if (isFrontFacingCam) {
                displayRotation = (cameraRotationOffset + degrees) % 360;
                displayRotation = (360 - displayRotation) % 360; // compensate
                                                                    // the
                                                                    // mirror
            } else { // back-facing
                displayRotation = (cameraRotationOffset - degrees + 360) % 360;
            }

            Log.v(TAG, "rotation cam / phone = displayRotation: " + cameraRotationOffset + " / " + degrees + " = "
                    + displayRotation);

            this.camera.setDisplayOrientation(displayRotation);

            int rotate;
            if (isFrontFacingCam) {
                rotate = (360 + cameraRotationOffset + degrees) % 360;
            } else {
                rotate = (360 + cameraRotationOffset - degrees) % 360;
            }

            Log.v(TAG, "screenshot rotation: " + cameraRotationOffset + " / " + degrees + " = " + rotate);

            Log.v(TAG, "preview size: " + previewSize.width + " / " + previewSize.height);
            parameters.setPreviewSize(previewSize.width, previewSize.height);
            parameters.setRotation(rotate);
            camera.setParameters(parameters);
            camera.setPreviewDisplay(mHolder);
            camera.startPreview();

            Log.d(TAG, "preview started");

            started = true;
        } catch (IOException e) {
            Log.d(TAG, "Error setting camera preview: " + e.getMessage());
        }
    }
Louis GRIGNON
la source
«J'espère que cela aidera les autres» Non, ce ne sera pas le cas. Votre morceau de code est sorti de son contexte. Par exemple, d'où vient la «isFrontFacingCam»?
seanpj
4
Je pense que c'est un autre sujet en fait, ce n'est vraiment rien comparé au problème initial. Vous pouvez l'obtenir à partir de android.hardware.Camera.CameraInfo.facing == CameraInfo.CAMERA_FACING_FRONT. Désolé pour ça.
Louis GRIGNON
4

découvrez cette solution

 public static void setCameraDisplayOrientation(Activity activity,
                                                   int cameraId, android.hardware.Camera camera) {
        android.hardware.Camera.CameraInfo info =
                new android.hardware.Camera.CameraInfo();
        android.hardware.Camera.getCameraInfo(cameraId, info);
        int rotation = activity.getWindowManager().getDefaultDisplay()
                .getRotation();
        int degrees = 0;
        switch (rotation) {
            case Surface.ROTATION_0: degrees = 0; break;
            case Surface.ROTATION_90: degrees = 90; break;
            case Surface.ROTATION_180: degrees = 180; break;
            case Surface.ROTATION_270: degrees = 270; break;
        }

        int result;
        if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
            result = (info.orientation + degrees) % 360;
            result = (360 - result) % 360;  // compensate the mirror
        } else {  // back-facing
            result = (info.orientation - degrees + 360) % 360;
        }
        camera.setDisplayOrientation(result);
    }
Mudassir Khan
la source
Merci d'avoir manipulé la caméra frontale. Cela a résolu mon problème
Louis CAD
vous êtes les bienvenus @LouisCAD heureux de voir que ma solution résout votre problème.
Mudassir Khan