Prendre des photos et contrôler les paramètres de l'appareil photo

Miguel Casas-Sanchez
François Beaufort
François Beaufort

Image Capture est une API permettant de capturer des images fixes et de configurer les paramètres matériels de l'appareil photo. Cette API est disponible dans Chrome 59 sur Android et sur ordinateur. Nous avons également publié une bibliothèque de polyfill ImageCapture.

L'API permet de contrôler les fonctionnalités de l'appareil photo, telles que le zoom, la luminosité, le contraste, les paramètres ISO et la balance des blancs. Mieux encore, Image Capture vous permet d'accéder à toutes les fonctionnalités de résolution de n'importe quelle caméra ou webcam disponible sur votre appareil. Les techniques précédentes de prise de photo sur le Web utilisaient des instantanés vidéo, dont la résolution est inférieure à celle des images fixes.

Un objet ImageCapture est construit avec un MediaStreamTrack comme source. L'API dispose ensuite de deux méthodes de capture takePhoto() et grabFrame(), ainsi que de moyens de récupérer les fonctionnalités et les paramètres de la caméra, et de les modifier.

Travaux

L'API Image Capture accède à une caméra via un MediaStreamTrack obtenu à partir de getUserMedia():

navigator.mediaDevices.getUserMedia({video: true})
    .then(gotMedia)
    .catch(error => console.error('getUserMedia() error:', error));

function gotMedia(mediaStream) {
    const mediaStreamTrack = mediaStream.getVideoTracks()[0];
    const imageCapture = new ImageCapture(mediaStreamTrack);
    console.log(imageCapture);
}

Vous pouvez tester ce code à partir de la console DevTools.

Filmez

Vous pouvez prendre une photo de deux manières: en plein écran et en mode instantané rapide. takePhoto() renvoie un Blob, résultat d'une seule exposition photographique, qui peut être téléchargé, stocké par le navigateur ou affiché dans un élément <img>. Cette méthode utilise la résolution d'appareil photo la plus élevée disponible. Exemple :

const img = document.querySelector('img');
// ...
imageCapture.takePhoto()
    .then(blob => {
    img.src = URL.createObjectURL(blob);
    img.onload = () => { URL.revokeObjectURL(this.src); }
    })
    .catch(error => console.error('takePhoto() error:', error));

grabFrame() renvoie un objet ImageBitmap, un instantané de la vidéo en direct, qui peut (par exemple) être dessiné sur un <canvas>, puis post-traité pour modifier sélectivement les valeurs de couleur. Notez que ImageBitmap n'a que la résolution de la source vidéo, qui est généralement inférieure aux capacités d'image fixe de la caméra. Exemple :

const canvas = document.querySelector('canvas');
// ...
imageCapture.grabFrame()
    .then(imageBitmap => {
    canvas.width = imageBitmap.width;
    canvas.height = imageBitmap.height;
    canvas.getContext('2d').drawImage(imageBitmap, 0, 0);
    })
    .catch(error => console.error('grabFrame() error:', error));

Fonctionnalités et paramètres

Il existe plusieurs façons de manipuler les paramètres de capture, selon que les modifications sont reflétées dans MediaStreamTrack ou ne sont visibles qu'après takePhoto(). Par exemple, une modification du niveau zoom est immédiatement propagée à MediaStreamTrack, tandis que la réduction des yeux rouges, lorsqu'elle est définie, n'est appliquée que lorsque la photo est prise.

Les fonctionnalités et les paramètres de l'appareil photo "en direct" sont manipulés via l'aperçu MediaStreamTrack: MediaStreamTrack.getCapabilities() renvoie un dictionnaire MediaTrackCapabilities avec les fonctionnalités prises en charge et les plages ou valeurs autorisées, par exemple la plage de zoom prise en charge ou les modes de balance des blancs autorisés. En conséquence, MediaStreamTrack.getSettings() renvoie un MediaTrackSettings avec les paramètres actuels concrets. Le zoom, la luminosité et le mode lampe de poche appartiennent à cette catégorie, par exemple:

var zoomSlider = document.querySelector('input[type=range]');
// ...
const capabilities = mediaStreamTrack.getCapabilities();
const settings = mediaStreamTrack.getSettings();
if (capabilities.zoom) {
    zoomSlider.min = capabilities.zoom.min;
    zoomSlider.max = capabilities.zoom.max;
    zoomSlider.step = capabilities.zoom.step;
    zoomSlider.value = settings.zoom;
}

Les fonctionnalités et les paramètres de la caméra "non en direct" sont manipulés via l'objet ImageCapture: ImageCapture.getPhotoCapabilities() renvoie un objet PhotoCapabilities qui permet d'accéder aux fonctionnalités de la caméra disponibles en mode "non en direct". Par conséquent, à partir de Chrome 61, ImageCapture.getPhotoSettings() renvoie un objet PhotoSettings avec les paramètres actuels concrets. La résolution photo, la réduction des yeux rouges et le mode flash (sauf la lampe de poche) font partie de cette section, par exemple:

var widthSlider = document.querySelector('input[type=range]');
// ...
imageCapture.getPhotoCapabilities()
    .then(function(photoCapabilities) {
    widthSlider.min = photoCapabilities.imageWidth.min;
    widthSlider.max = photoCapabilities.imageWidth.max;
    widthSlider.step = photoCapabilities.imageWidth.step;
    return imageCapture.getPhotoSettings();
    })
    .then(function(photoSettings) {
    widthSlider.value = photoSettings.imageWidth;
    })
    .catch(error => console.error('Error getting camera capabilities and settings:', error));

Configuration

Les paramètres de la caméra "En direct" peuvent être configurés via les contraintes applyConstraints() de l'aperçu MediaStreamTrack, par exemple:

var zoomSlider = document.querySelector('input[type=range]');

mediaStreamTrack.applyConstraints({ advanced: [{ zoom: zoomSlider.value }]})
    .catch(error => console.error('Uh, oh, applyConstraints() error:', error));

Les paramètres de la caméra "non en direct" sont configurés avec le dictionnaire facultatif PhotoSettings de takePhoto(), par exemple:

var widthSlider = document.querySelector('input[type=range]');
imageCapture.takePhoto({ imageWidth : widthSlider.value })
    .then(blob => {
    img.src = URL.createObjectURL(blob);
    img.onload = () => { URL.revokeObjectURL(this.src); }
    })
    .catch(error => console.error('Uh, oh, takePhoto() error:', error));

Fonctionnalités de l'appareil photo

Si vous exécutez le code ci-dessus, vous remarquerez une différence de dimensions entre les résultats grabFrame() et takePhoto().

La méthode takePhoto() permet d'accéder à la résolution maximale de la caméra.

grabFrame() prend simplement le VideoFrame suivant disponible dans le MediaStreamTrack dans le processus de rendu, tandis que takePhoto() interrompt le MediaStream, reconfigure l'appareil photo, prend la photo (généralement dans un format compressé, d'où le Blob), puis reprend le MediaStreamTrack. En substance, cela signifie que takePhoto() permet d'accéder à toutes les fonctionnalités de résolution d'image fixe de l'appareil photo. Auparavant, il n'était possible de "prendre une photo" qu'en appelant drawImage() sur un élément canvas, en utilisant une vidéo comme source (comme dans l'exemple ci-dessous).

Pour en savoir plus, consultez la section README.md.

Dans cette démonstration, les dimensions de <canvas> sont définies sur la résolution du flux vidéo, tandis que la taille naturelle de <img> correspond à la résolution maximale des images fixes de la caméra. Le CSS permet bien sûr de définir la taille d'affichage des deux.

Vous pouvez obtenir et définir toute la gamme de résolutions d'appareil photo disponibles pour les images fixes à l'aide des valeurs MediaSettingsRange pour PhotoCapabilities.imageHeight et imageWidth. Notez que les contraintes de largeur et de hauteur minimales et maximales pour getUserMedia() sont destinées aux vidéos, qui (comme indiqué) peuvent être différentes des fonctionnalités de l'appareil photo pour les images fixes. En d'autres termes, vous ne pourrez peut-être pas accéder à toutes les fonctionnalités de résolution de votre appareil lorsque vous enregistrerez depuis getUserMedia() vers un canevas. La démonstration de la contrainte de résolution WebRTC montre comment définir des contraintes getUserMedia() pour la résolution.

Autre chose ?

  • L'API Shape Detection fonctionne bien avec la capture d'image: grabFrame() peut être appelé à plusieurs reprises pour transmettre des ImageBitmap à un FaceDetector ou à un BarcodeDetector. Pour en savoir plus sur l'API, consultez l'article de blog de Paul Kinlan.

  • Le flash de l'appareil photo (lumière de l'appareil) est accessible via FillLightMode dans PhotoCapabilities, tandis que le mode lampe de poche (flash allumé en permanence) se trouve dans MediaTrackCapabilities.

Démos et exemples de code

Assistance

  • Chrome 59 sur Android et ordinateur.
  • Chrome Canary sur Android et ordinateur antérieur à la version 59 avec les fonctionnalités de la plate-forme Web expérimentale activées.

En savoir plus