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
new Movie(this, "epilogue.mp4");
video =
// reproduce video una vez
play();
video.// reproduce video en loop:
// video.loop();
}
void draw(){
// revisa si hay una nueva frame de video:
if( video.available() ){
// lee la frame hacia video
read();
video.// 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
new Movie(this, "epilogue.mp4");
video =
// reproduce video una vez
play();
video.// 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
read();
v.// 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:
.play()
.pause()
.stop()
.loop()
y.noLoop()
.jump( t )
: para saltar a cierto momentot
en el video (en segundos).speed( s )
: cambia la velocidad relativa de reproducción: 1.0 es normal, 2.0 es el doble de rápido, 0.5 es la mitad, etc..frameRate( f )
: cambia el frameRate de reproducción del video.time()
: indica en qué ubicación temporal del video se encuentra la reproducción.duration()
: regresa la duración del video en segundos
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
new Movie(this, "epilogue.mp4");
video =
// crea los objetos de clase PImage para recortes
createImage(width/2, height, RGB);
mitad1 = createImage(width/2, height, RGB);
mitad2 =
// reproduce video en loop:
loop();
video.
}
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
read();
v.// redimensiona frame al tamaño del canvas
resize(width, height);
v.// obtén la mitad izquierda
get(0, 0, v.width/2, v.height);
mitad1 = v.// obtén la mitad derecha
get(width/2, 0, v.width/2, v.height);
mitad2 = v. }
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
new Capture(this, 640, 480, camaras[0]);
cam = // enciende la cámara
start();
cam.
}
void draw(){
// revisa si hay una nueva frame de video
if ( cam.available() ){
// lee la nueva frame de la cámara
read();
cam.// 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.
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
new Capture(this, 640, 480, camaras[0]);
cam = // enciende la cámara
start();
cam.
}
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
read();
c.// 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
createImage(width/2, height, RGB);
mitad1 = createImage(width/2, height, RGB);
mitad2 =
// 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
new Capture(this, 640, 480, camaras[0]);
cam = // enciende la cámara
start();
cam.
}
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
read();
c.// redimensiona frame al tamaño del canvas
//c.resize(width, height);
// obtén la mitad izquierda
get(0, 0, c.width/2, c.height);
mitad1 = c.// obtén la mitad derecha
get(width/2, 0, c.width/2, c.height);
mitad2 = c. }