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