jardínBit

Video: Estructuras básicas

Utilizando la biblioteca (library) de Video, Processing nos permite manipular video pregrabado con Movie y/o obtenido en vivo desde cámara web con Capture.

La biblioteca maneja toda la lógica para abrir y reproducir videos en el caso de Movie, y para manejar a la cámara web en el caso de Capture.

Además, cada frame de video nos la entrega como una imagen (PImage) con la cual podemos hacer cualquier manipulación como cuando trabajamos con imágenes estáticas:

1 Preparación

1.1 Instalación de la biblioteca

Para instalar la biblioteca de Video, ve al menú Sketch -> Importar biblioteca… -> Añadir biblioteca. En la ventana que se abre, busca Video y selecciona la opción que dice Video | GStreamer-based video library for Processing y tiene como autora a The Processing Foundation.

Haz click en esa opción, y presiona el botón Install para que la biblioteca se descargue e instale.

Este proceso solo hay que realizarlo una vez.

1.2 Importar biblioteca

Para utilizar la biblioteca en nuestro sketch, podemos ir al menú Sketch -> Importar biblioteca… -> Video; esto agregará la siguiente línea de código:

import processing.video.*;

Si lo prefieres puedes escribir esa línea de código directamente, en lugar de usar los menús.

1.3 Uso de videos

Para poder utilizar videos pregrabados con Movie, es necesario tener nuestro sketch de código guardado.

En la carpeta donde se encuentra el archivo .pde de nuestro sketch hay que crear una carpeta llamada data, y ahí es donde hemos de colocar los videos que queremos utilizar.

Por ejemplo, si guardamos nuestro sketch dentro de la carpeta sketchbook con el nombre estudioVideos, hay que colocar nuestros videos en la carpeta sketchbook/estudioVideos/data.

1.3.1 Consideraciones

Toma en cuenta que para fines de sketching, y dependiendo de tu equipo computacional, puede convenir trabajar provisionalmente con archivos de video de baja resolución y/o calidad. Esto permitirá que tu sketch cargue más rápido al ejecutarlo.

También, podría suceder que si tu programa realiza operaciones muy intensivas computacionalmente en cada frame, el frame rate podría reducirse, en especial manipulando video en vivo.

Si la prioridad es la ejecución en vivo puedes bajar la resolución y/o calidad, y si la prioridad es el resultado del procesamiento entonces puedes guardar cada frame con saveFrame("frames/frame-####.png"); y después ensamblarlas en un video.

1.4 Uso de cámara web

El uso de cámara web con Capture funciona sin problema en GNU/Linux y Windows.

Para versiones recientes de Mac OS X, al parecer hay que realizar el siguiente procedimiento:

Video Library 2.0 - MacOS Mojave / Catalina Issues

(por confirmar)

2 Movie: Videos pregrabados

La clase Movie maneja videos pregrabados. Con ella podemos cargar videos, iniciar o detener su avance, obtener datos temporales, etc.

2.1 Estructura básica

Este sketch muestra la estructura básica para utilizarla:

// estructura básica para cargar y reproducir video

// agrega 'library' de Video
import processing.video.*;

// variable de clase Movie
Movie video;

void setup(){
  size(640, 360);
  // crea objeto de clase Movie, cargando video
  video = new Movie(this, "epilogue.mp4");
  
  // reproduce video una vez
  video.play();
  // reproduce video en loop:
  // video.loop();
}

void draw(){
 // revisa si hay una nueva frame de video:
 if( video.available() ){
  // lee la frame hacia video
  video.read(); 
  // realiza aquí dentro operaciones como redimensiones, recortes y máscaras
 }
 
 // dibuja la frame de video:
 image(video, 0, 0);
}

Nota las similitudes y diferencias con el uso de PImage.

La principal diferencia aquí es el condicional con el método .available(), que carga en nuestro programa cada frame de video únicamente cuando está listo. Esto se debe a que el frameRate de reproducción de video y el frameRate de ejecución del sketch puede variar o desfasarse un poco.

2.2 Evento movieEvent( )

En lugar del condicional podemos usar la función movieEvent( ) que se llama cada vez que hay alguna actualización en la lectura del video.

El sketch anterior quedaría de la siguiente forma:

// estructura básica para cargar video con movieEvent()

// agrega 'library' de Video
import processing.video.*;

// variable de clase Movie
Movie video;

void setup(){
  size(640, 360);
  // crea objeto de clase Movie, cargando video
  video = new Movie(this, "epilogue.mp4");
  
  // reproduce video una vez
  video.play();
  // reproduce video en loop:
  // video.loop();
}

void draw(){
 // dibuja la frame de video:
 image(video, 0, 0);
}

void movieEvent(Movie v){
 // lee la frame de video
 v.read(); 
 // realiza aquí dentro operaciones como redimensiones, recortes y máscaras
}

2.3 Métodos

La clase Movie tiene más métodos para controlar la reproducción del video:

Encuentra más detalles sobre ellos en la referencia de la biblioteca:

Video  Libraries  Processing.org

2.4 Ejemplos

2.4.1 Recortes

Este ejemplo recorta e intercambia de lugar las mitades horizontales del video cargado.

Nota cómo los recortes se realizan justo después de que se lee cada frame del video (.read()), y en el ciclo draw() únicamente se dibujan.

// este ejemplo recorta e intercambia las mitades horizontales del video

// agrega 'library' de Video
import processing.video.*;

// variable de clase Movie
Movie video;

// variables para los recortes
PImage mitad1, mitad2;

void setup(){
  size(640, 360);
  // crea objeto de clase Movie, cargando video
  video = new Movie(this, "epilogue.mp4");
  
  // crea los objetos de clase PImage para recortes
  mitad1 = createImage(width/2, height, RGB);
  mitad2 = createImage(width/2, height, RGB);
  
  // reproduce video en loop:
  video.loop();
}

void draw(){
 // dibuja las mitades en posiciones intercambiadas
 // mitad izquierda dibujada a la derecha:
 image(mitad1, width/2, 0);
 // mitad derecha dibujada a la izquierda
 image(mitad2, 0, 0);
}

void movieEvent(Movie v){
 // lee la frame de video
 v.read(); 
 // redimensiona frame al tamaño del canvas
 v.resize(width, height);
 // obtén la mitad izquierda 
 mitad1 = v.get(0, 0, v.width/2, v.height);
 // obtén la mitad derecha
 mitad2 = v.get(width/2, 0, v.width/2, v.height);
}

3 Capture: Cámara(s) web

La clase Capture maneja cámaras web. Con ella podemos escoger entre cámaras, asignar alguna resolución, encenderla o apagarla.

3.1 Estructura básica

Este sketch muestra la estructura básica para utilizarla. Es muy parecida al uso de Movie, con la diferencia de que en lugar de cargar un video, hay que escoger una cámara y asignarle una resolución.

// estructura básica para usar una cámara web

// importa 'library' de Video
import processing.video.*;

// variable de clase Capture
// para la cámara web
Capture cam;

void setup(){
 size(640, 480);
 
 // obtén lista de cámaras conectadas
 String[] camaras = Capture.list();
 // muestra la lista en la consola
 printArray( camaras );
 
 // crea objeto de clase Capture
 // utilizando la primera cámara
 // con resolución de 640x480
 cam = new Capture(this, 640, 480, camaras[0]);
 // enciende la cámara
 cam.start();
}

void draw(){
 // revisa si hay una nueva frame de video
 if ( cam.available() ){
   // lee la nueva frame de la cámara
   cam.read(); 
   // realiza aquí operaciones como redimensiones, recortes y máscaras
 }
 // dibuja la frame de la cámara
 image(cam, 0, 0); 
}

3.1.1 Espejo

Nota que el video no está “espejado”; se muestra literalmente lo que ve la cámara.

Para dibujar en espejo podemos utilizar estas líneas de código con transformaciones:

 scale(-1, 1); // invierte el eje X
 translate(-width, 0); // mueve esq. sup. izq. a la "derecha"
 image(cam, 0, 0);

Lo que sucede aquí es que primero el eje X se invierte. Luego se traslada el origen width pixeles a “la izquierda” (porque es negativo), pero como el eje X está invertido esto se traduce en trasladar el origen width pixeles a “la derecha” del canvas.

De esta forma, lo que era la esquina superior izquierda de la imagen ahora queda en la esquina superior derecha del canvas, todo en espejo.

[Transformaciones]

3.2 Evento captureEvent( )

Al igual que con movieEvent( ) (ver arriba), aquí un equivalente que se llama con cada nueva frame de la cámara web.

La estructura básica quedaría así:

// estructura básica para usar una cámara web con captureEvent

// importa 'library' de Video
import processing.video.*;

// variable de clase Capture
// para la cámara web
Capture cam;

void setup(){
 size(640, 480);
 
 // obtén lista de cámaras conectadas
 String[] camaras = Capture.list();
 // muestra la lista en la consola
 printArray( camaras );
 
 // crea objeto de clase Capture
 // utilizando la primera cámara
 // con resolución de 640x480
 cam = new Capture(this, 640, 480, camaras[0]);
 // enciende la cámara
 cam.start();
}

void draw(){
 // dibuja la frame de la cámara
 image(cam, 0, 0); 
}

void captureEvent( Capture c ){
 // lee la nueva frame de la cámara
 c.read(); 
 // realiza aquí operaciones como redimensiones, recortes y máscaras
}

3.3 Ejemplos

3.3.1 Recortes

Similar al ejemplo de arriba con Movie, este programa recorta e intercambia de lugar las mitades horizontales del video proveniente de la cámara web.

// este ejemplo recorta e intercambia las mitades horizontales del video de la cámara

// agrega 'library' de Video
import processing.video.*;

// variable de clase Capture
// para la cámara web
Capture cam;

// variables para los recortes
PImage mitad1, mitad2;

void setup(){
  size(640, 480);
  
  // crea los objetos de clase PImage para recortes
  mitad1 = createImage(width/2, height, RGB);
  mitad2 = createImage(width/2, height, RGB);

  // obtén lista de cámaras conectadas
  String[] camaras = Capture.list();
  // muestra la lista en la consola
  printArray( camaras );
 
  // crea objeto de clase Capture
  // utilizando la primera cámara
  // con resolución de 640x480
  cam = new Capture(this, 640, 480, camaras[0]);
  // enciende la cámara
  cam.start();
}

void draw(){
 // "espejea" video
 scale(-1, 1); // invierte el eje X
 translate(-width, 0); // mueve esq. sup. izq. a la "derecha"

 // dibuja las mitades en posiciones intercambiadas
 // mitad izquierda dibujada a la derecha:
 image(mitad1, width/2, 0);
 // mitad derecha dibujada a la izquierda
 image(mitad2, 0, 0);
}

void captureEvent(Capture c){
 // lee la frame de video de la cámara
 c.read(); 
 // redimensiona frame al tamaño del canvas
 //c.resize(width, height);
 // obtén la mitad izquierda 
 mitad1 = c.get(0, 0, c.width/2, c.height);
 // obtén la mitad derecha
 mitad2 = c.get(width/2, 0, c.width/2, c.height);
}