Esto de aquí es un programilla en java que a partir del vídeo captado por una webcam, detecta si hay o no movimiento en el video y con qué dirección e intensidad. Realmente el concepto es muuuy sencillo: Cogemos dos frames del video, (normalmente el último obtenido y uno que hayamos obtenido hace un pequeño lapso de tiempo, que llamaremos dT), los restamos, y de la imagen obtenida ignoramos los píxeles cuya intensidad esté por debajo de un umbral determinado. Si queda algún píxel con luminosidad > 0 en la imagen, significa que ha habido movimiento durante dT, y la imagen resultante (que llamaré imagen de movimiento) nos indica dónde se ha producido dicho mivimiento y cómo. Para aproximar la dirección y velocidad del movimiento, tomamos dos imágenes de detección producidas en dos intervalos de tiempo distintos pero cercanos, por ejemplo dT y dT2. Obtenemos la posición media de los píxeles con luminosidad > 0 que contienen ambas imágenes, y ambos puntos obtenidos nos definen un vector cuyo módulo es la intensidad del movimiento y cuya dirección es la dirección del movimiento. Por supesto es una aproximación bastante cutre, pero es rápida de calcular. Veamos el código de la primera parte (detección de movimiento en un intervalo):
private boolean distintos(Color c1,Color c2){
float tred =40;
float tgreen = 40;
float tblue = 40;
if ((Math.abs(c1.getRed()-c2.getRed())>tred) ||
(Math.abs(c1.getGreen()-c2.getGreen())>tgreen) ||
(Math.abs(c1.getBlue()-c2.getBlue())>tblue))
return true;
else
return false;
}
private BufferedImage movimiento(BufferedImage x1,BufferedImage x2) {
BufferedImage temp = new BufferedImage(x1.getWidth(this), x1.getHeight(this),
BufferedImage.TYPE_INT_ARGB);
if (x2 == null) return temp;
for(int loop1 = 0; loop1 < x1.getWidth(this); loop1++) {
for(int loop2 = 0; loop2 < x1.getHeight(this); loop2++) {
Color pixel1 = new Color(x1.getRGB(loop1,loop2));
Color pixel2 = new Color(x2.getRGB(loop1,loop2));
if(distintos(pixel1,pixel2)) {
temp.setRGB(loop1,loop2,Color.red.getRGB());
}
else {
temp.setRGB(loop1,loop2,Color.black.getRGB());
}
}
}
return temp;
}
Explicación: La función movimiento() toma dos imágenes, que normalmente serán el frame actual y uno tomado hace poco tiempo, y nos devuelve una imagen indicando los cambios de un frame al otro. Para cada pixel de ambas imágenes, comparamos si son distintos usando la función del mismo nombre. Si lo son, es que en ese pixel ha habido movimiento (lo marcamos en rojo). Si no, lo marcamos en negro.)
La función distintos() simplemente da un margen a la hora de comparar dos pixeles, para que no tengan que ser *exactamente* iguales para ser considerados iguales. Bien, como veis es bastante sencillo. Si para obtener información acerca del movimiento usamos dos frames normales, para obtener dirección y velocidad del movimiento usaremos dos frames de movimiento:
private float[] direccion_velocidad(BufferedImage x1,BufferedImage x2) {
float[] zero = {0,0};
if (x2 == null||x1 == null) return zero;
float mx1,my1,mx2,my2;
mx1 = 0;mx2 = 0;my1 = 0; my2 = 0;
int m1 = 0;
int m2 = 0;
int total = x1.getWidth(this)*x1.getHeight(this);
for(int loop1 = 0; loop1 < x1.getWidth(this); loop1++) {
for(int loop2 = 0; loop2 < x1.getHeight(this); loop2++) {
Color pixel1 = new Color(x1.getRGB(loop1,loop2));
Color pixel2 = new Color(x2.getRGB(loop1,loop2));
if(pixel1.equals(on) && pixel2.equals(off)) {
mx1 += loop1;
my1 += loop2;
m1++;
}
if(pixel2.equals(on) && pixel1.equals(off)) {
mx2 += loop1;
my2 += loop2;
m2++;
}
}
}
if (m1 > 80 && m2 > 80){
mx1 = mx1/m1;
my1 = my1/m1;
mx2 = mx2/m2;
my2 = my2/m2;
float[] res = {mx1-mx2,my1-my2};
return res;
}
else return zero;
}
Cogemos dos imágenes, y calculamos el centroide de cada una de las imágenes, sumando las posiciones de todos los pixeles rojos que contiene cada una y dividiendo entre el número de píxeles rojos. Podemos comprobar que el número de pixeles pase de un cierto límte, para no tener en cuenta frames con pequeños movimientos residuales.
Y ya está. Lo único que he omitido es el código para obtener y almacenar las imágenes de la webcam a través de JMF, pero es bastante sencillo y ya hay muchos ejemplos de ello. Una posible mejora a esto sería añadir un algoritmo de detección de "blobs" para identificar diversas fuentes de movimiento, y así podríamos calcular para cada una el área de la imagen que cubre, su velocidad y su dirección de movimiento, en lugar de hacerlo para toda la imagen sin distinguir objetos. También, si dispuesiéramos de un trípode motorizado para la cámara, podríamos hacer que la cámara siguiese automáticamente los objetos que se están moviendo (no habría más que ajustar el trípode dependiendo del desplazamiento del centroide de las imágenes de movimiento con respecto al centro de la imagen), con lo que tendríamos una estupenda cámara de vigilancia.