Cómo tomar fotos y controlar la configuración de la cámara

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

Image Capture es una API para capturar imágenes fijas y configurar la configuración de hardware de la cámara. Esta API está disponible en Chrome 59 para Android y computadoras. También publicamos una biblioteca de polyfill de ImageCapture.

La API permite controlar las funciones de la cámara, como el zoom, el brillo, el contraste, el ISO y el balance de blancos. Lo mejor de todo es que la Captura de imágenes te permite acceder a las capacidades de resolución completa de cualquier cámara o cámara web del dispositivo disponible. Las técnicas anteriores para tomar fotos en la Web usaban instantáneas de video, que tienen una resolución más baja que la disponible para las imágenes fijas.

Un objeto ImageCapture se construye con un MediaStreamTrack como fuente. La API tiene dos métodos de captura, takePhoto() y grabFrame(), y formas de recuperar las capacidades y la configuración de la cámara, y de cambiar esa configuración.

Construcción

La API de Image Capture obtiene acceso a una cámara a través de un MediaStreamTrack obtenido 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);
}

Puedes probar este código desde la consola de DevTools.

Captar

La captura se puede realizar de dos maneras: en formato completo y en instantánea rápida. takePhoto() muestra un Blob, el resultado de una sola exposición fotográfica, que el navegador puede descargar, almacenar o mostrar en un elemento <img>. Este método usa la resolución más alta disponible de la cámara fotográfica. Por ejemplo:

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() muestra un objeto ImageBitmap, una instantánea de video en vivo, que se podría dibujar (por ejemplo) en un <canvas> y, luego, procesar posteriormente para cambiar de forma selectiva los valores de color. Ten en cuenta que ImageBitmap solo tendrá la resolución de la fuente de video, que suele ser inferior a las capacidades de imagen fija de la cámara. Por ejemplo:

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));

Funciones y configuración

Existen varias formas de manipular la configuración de captura, según si los cambios se reflejarán en MediaStreamTrack o solo se pueden ver después de takePhoto(). Por ejemplo, un cambio en el nivel de zoom se propaga de inmediato a MediaStreamTrack, mientras que la reducción de ojos rojos, cuando se configura, solo se aplica cuando se toma la foto.

Las capacidades y la configuración de la cámara "en vivo" se manipulan a través de la vista previa MediaStreamTrack: MediaStreamTrack.getCapabilities() muestra un diccionario MediaTrackCapabilities con las capacidades compatibles concretas y los rangos o valores permitidos, p.ej., el rango de zoom compatible o los modos de balance de blancos permitidos. En consecuencia, MediaStreamTrack.getSettings() muestra un MediaTrackSettings con la configuración actual concreta. El zoom, el brillo y el modo linterna pertenecen a esta categoría, por ejemplo:

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;
}

Las capacidades y la configuración de la cámara "no en vivo" se manipulan a través del objeto ImageCapture: ImageCapture.getPhotoCapabilities() muestra un objeto PhotoCapabilities que proporciona acceso a las capacidades de la cámara disponibles "no en vivo". En consecuencia, a partir de Chrome 61, ImageCapture.getPhotoSettings() muestra un objeto PhotoSettings con la configuración actual concreta. La resolución de la foto, la reducción de ojos rojos y el modo de flash (excepto la linterna) pertenecen a esta sección, por ejemplo:

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));

Configurando

La configuración de la cámara "en vivo" se puede configurar a través de las restricciones applyConstraints() de la vista previa de MediaStreamTrack, por ejemplo:

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

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

La configuración de la cámara "no en vivo" se configura con el diccionario opcional PhotoSettings de takePhoto(), por ejemplo:

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));

Funciones de la cámara

Si ejecutas el código anterior, notarás una diferencia en las dimensiones entre los resultados de grabFrame() y takePhoto().

El método takePhoto() brinda acceso a la resolución máxima de la cámara.

grabFrame() solo toma el siguiente VideoFrame disponible en MediaStreamTrack dentro del proceso del renderizador, mientras que takePhoto() interrumpe MediaStream, reconfigura la cámara, toma la foto (por lo general, en un formato comprimido, de ahí el Blob) y, luego, reanuda MediaStreamTrack. En esencia, esto significa que takePhoto() otorga acceso a todas las capacidades de resolución de imágenes fijas de la cámara. Anteriormente, solo era posible "tomar una foto" llamando a drawImage() en un elemento canvas y usando un video como fuente (como se muestra aquí).

Puedes encontrar más información en la sección README.md.

En esta demostración, las dimensiones de <canvas> se establecen en la resolución de la transmisión de video, mientras que el tamaño natural de <img> es la resolución máxima de imagen fija de la cámara. Por supuesto, CSS se usa para establecer el tamaño de visualización de ambos.

Se puede obtener y configurar la gama completa de resoluciones de cámara disponibles para imágenes fijas con los valores MediaSettingsRange de PhotoCapabilities.imageHeight y imageWidth. Ten en cuenta que las restricciones de ancho y altura mínimas y máximas para getUserMedia() son para videos, que (como se analizó) pueden ser diferentes de las capacidades de la cámara para imágenes fijas. En otras palabras, es posible que no puedas acceder a las capacidades de resolución completa de tu dispositivo cuando guardes desde getUserMedia() en un lienzo. En la demo de restricción de resolución de WebRTC, se muestra cómo configurar restricciones getUserMedia() para la resolución.

¿Algo más?

  • La API de Shape Detection funciona bien con Image Capture: se puede llamar a grabFrame() de forma reiterada para enviar ImageBitmap a un FaceDetector o BarcodeDetector. Obtén más información sobre la API en la entrada de blog de Paul Kinlan.

  • Se puede acceder al flash de la cámara (luz del dispositivo) a través de FillLightMode en PhotoCapabilities, pero el modo linterna (flash encendido constantemente) se encuentra en MediaTrackCapabilities.

Demostraciones y muestras de código

Asistencia

  • Chrome 59 en Android y computadoras
  • Chrome Canary en Android y computadoras de escritorio anteriores a la versión 59 con las funciones de Plataforma web experimental habilitadas

Más información