web-dev-qa-db-fra.com

Camera2 comprendre les orientations du capteur et de l'appareil

Je suis tombé sur un problème en essayant d'implémenter une fonction tactile pour se concentrer en utilisant Android Camera2 .

La théorie est simple:

  • obtenir la position du tap dans la surface d'aperçu
  • mappez-le aux dimensions du capteur ou de la zone de recadrage du capteur (en cas de zoom) en veillant à inverser les dimensions si nécessaire
  • appliquer un changement de base pour finir dans la même base que le capteur
  • faire un MeteringRectangle du résultat et l'utiliser dans un nouveau CaptureRequest

Il existe un certain nombre d'exemples qui montrent comment traiter le premier et le dernier point, mais pas beaucoup qui traitent du deuxième et du troisième d'une manière compréhensible. Les documents et les exemples ne sont pas vraiment clairs et cela peut être vraiment déroutant.

Et c'est parti...


Le CameraCharacteristics.SENSOR_ORIENTATION Est décrit comme

Angle dans le sens des aiguilles d'une montre selon lequel l'image de sortie doit être tournée pour être verticale sur l'écran de l'appareil dans son orientation d'origine.

Sachant que le système de coordonnées du capteur est défini avec (0,0) étant le pixel supérieur gauche dans le tableau de pixels actif, je le lis comme étant l'angle nécessaire pour faire pivoter l'image capturée dans le système de coordonnées du capteur jusqu'à la position qui ferait l'image semble droite dans l'orientation native. Donc, si le haut du capteur fait face au côté droit d'un téléphone avec une orientation portrait native, le SENSOR_ORIENTATION Sera de 90 °. Sensor Orientation


L'orientation de l'affichage obtenue via mActivity.getWindowManager().getDefaultDisplay().getRotation(); est documentée comme suit:

Renvoie la rotation de l'écran à partir de son orientation "naturelle". La valeur renvoyée peut être Surface.ROTATION_0 (pas de rotation), Surface.ROTATION_90, Surface.ROTATION_180 ou Surface.ROTATION_270. Par exemple, si un appareil possède un écran naturellement haut et que l'utilisateur l'a tourné sur le côté pour passer en orientation paysage, la valeur renvoyée ici peut être Surface.ROTATION_90 ou Surface.ROTATION_270 selon la direction dans laquelle il a été tourné. L'angle est la rotation des graphiques dessinés sur l'écran, qui est la direction opposée de la rotation physique de l'appareil. Par exemple, si l'appareil est tourné de 90 degrés dans le sens antihoraire, pour compenser le rendu, il sera tourné de 90 degrés dans le sens horaire et donc la valeur renvoyée ici sera Surface.ROTATION_90.

Je trouve que cette définition est beaucoup plus claire que celle du capteur, il n'y a pas de place pour l'interprétation.


Maintenant, là où les choses commencent à devenir laides ...

J'ai décidé d'utiliser la méthode fournie dans l'exemple Camera2Raw pour obtenir la rotation de l'orientation du capteur à l'orientation de l'appareil.

/**
 * Rotation need to transform from the camera sensor orientation to the device's current
 * orientation.
 *
 * @param c                 the {@link CameraCharacteristics} to query for the camera sensor
 *                          orientation.
 * @param deviceOrientation the current device orientation relative to the native device
 *                          orientation.
 * @return the total rotation from the sensor orientation to the current device orientation.
 */
private static int sensorToDeviceRotation(CameraCharacteristics c, int deviceOrientation) {
    int sensorOrientation = c.get(CameraCharacteristics.SENSOR_ORIENTATION);

    // Get device orientation in degrees
    deviceOrientation = ORIENTATIONS.get(deviceOrientation);

    // Reverse device orientation for front-facing cameras
    if (c.get(CameraCharacteristics.LENS_FACING) == CameraCharacteristics.LENS_FACING_FRONT) {
        deviceOrientation = -deviceOrientation;
    }

    // Calculate desired JPEG orientation relative to camera orientation to make
    // the image upright relative to the device orientation
    return (sensorOrientation + deviceOrientation + 360) % 360;
}

Voici un tableau des différentes sorties pour une caméra orientée vers l'arrière et vers l'avant sur un téléphone avec une orientation native portrait. enter image description here

La première chose que j'ai remarquée est que si je considère les sorties comme décrit (rotation de l'orientation du capteur de la caméra à l'orientation actuelle de l'appareil), pour que cela ait du sens, je dois considérer que la rotation de sortie est dans le sens antihoraire (contrairement à l'orientation du capteur et de l'appareil) !! Par exemple, si nous prenons le capteur typique à 90 ° et l'orientation de l'appareil à 0 °, le résultat est de 90 ° et si je ne me trompe pas dans mon analyse, cela ne peut être que dans le sens antihoraire.

Sous l'hypothèse que ma compréhension des orientations du capteur et de l'appareil est correcte (pas certaine à ce sujet), alors quelque chose doit être faux avec les résultats des tableaux ci-dessus, car si vous regardez le capteur à 90 ° et le boîtier d'orientation de l'appareil à 90 ° , il ne peut pas être à 180 °, il doit être à 0 °. L'image suivante est une représentation visuelle de ma compréhension de tout cela pour un téléphone à capteur à 90 °. enter image description here

Je suis allé de l'avant et mis en œuvre mes changements de base dans R2 pour obtenir mon point de prise de la base de l'écran à la base du capteur et ajouté les décalages attendus.

J'ai observé que si je change les calculs à 180 ° et 0 °, alors mon toucher pour se concentrer fonctionne parfaitement. Les valeurs observées correctes pour la rotation du capteur vers l'orientation actuelle de l'appareil correspondent en fait au tableau de la caméra frontale.

Donc, mon instinct est que le sensorToDeviceRotation est imparfait et que la valeur de retour devrait être:

// Calculate desired JPEG orientation relative to camera orientation to make
// the image upright relative to the device orientation
return (sensorOrientation - deviceOrientation + 360) % 360;

Ce serait en fait plus logique en termes de calcul ...

Quelqu'un pourrait-il confirmer cela? ou ai-je mal compris quelque chose quelque part?

À votre santé

20
Dude

Oui, c'est un bug dans Camera2Raw; merci de l'avoir attrapé.

Si vous comparez l'exemple de code dans les documents de référence pour Camera # setDisplayOrientation , vous verrez le calcul que vous attendez:

...
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;
}
4
Eddy Talvala