Monday, November 15, 2010

La Identidad de Euler

Se dice que si se tuvieran que escoger los números más populares, sin duda alguna entre los primeros escogidos estarían π, e, i, 1 y 0.
Hay una bella identidad que los relaciona, seguramente conocida por más de alguno de ustedes, yo la ví en el documental universo matemático:

ei π + 1 = 0

La identidad anterior es conocida como la Identidad de Euler.

Para corroborar su autenticidad sólo debemos recurrir a la famosa Fórmula de Euler:

ei θ = cos (θ) + i sen(θ)



Donde se verifica fácilmente que si θ = π se obtiene:

ei π = cos (π) + i sen(π) = -1



Pues cos(π) = -1 y sin(π) = 0

Sunday, September 26, 2010

Tip Octave

Cuando se carga una imagen en memoria usando imread se almacena como una matriz uint8, lo que significa que cada valor de la matriz es un número positivo de 1 byte, si se quiere trabajar con esos números hay que tener cuidado pues tomarán un valor máximo de 255.


octave:> img = imread("imagen.jpg");
octave:> img(1,1)
octave:> ans = 44
octave:> img(1,1)*1000
octave:> ans=255
octave:> %para evitar esto hay que hacer un casting
octave:> double(img(1,1))*1000
octave:> ans=44000
octave:>


Escribí esto aquí para que no se me olvide, pues me causó un gran dolor de cabeza mientras estaba probando un algoritmo de interpolación para un trabajo de la universidad.

Monday, September 20, 2010

Efecto para ser invisible, para variar en OpenCV

El efecto mostrado en el post anterior es muy fácil de programar:
1.- Sacar una foto al fondo
2.- Reemplazar todos los pixeles color piel por su pixel correspondiente en la imagen tomada en 1.
Para detectar la piel humana en el espacio de colores RGB:

char isSkinPixel(IplImage *img, int x, int y) {
unsigned char R,G,B;
B = img->imageData[y*img->widthStep+x*img->nChannels];
G = img->imageData[y*img->widthStep+x*img->nChannels+1];
R = img->imageData[y*img->widthStep+x*img->nChannels+2];
if( R > 95 && G > 40 && B > 20 && ( max(R,G,B) - min(R,G,B) ) > 15 && abs( (int)(R-G) ) > 15 && R>G && R>B ) {
return 1;
}
return 0; 
}

La condición que se usa para detectar el color piel la ví en un paper, pero no lo pude encontrar para dejarlo aquí.

La detección de piel humana funciona bien sólo si estamos capturando imágenes en un lugar con la luminosidad adecuada, pues si esta muy oscuro o muy iluminado el color de la piel se pierde. Además hay falsos positivos si el fondo no es el adecuado.

Código fuente del programa : invisible (necesita la biblioteca OpenCV)

Tuesday, August 24, 2010

Pastilla para hacerse invisible

Hoy al fin probé la famosa pastilla para hacerse invisible, es impresionante la rapidez con que hace efecto. Os dejo un video, haber si alguien puede explicarme cómo es que funciona esta revolucionaria mezcla de ingredientes exóticos.

Monday, August 2, 2010

Implementación de k-means en OpenCV



k-means o k-medias es un algoritmo de agrupamiento (clustering), lo que significa que es usado para dividir un conjunto de datos en grupos de tal forma que los datos pertenecientes a un grupo tengan propiedades en común. También se dice que este algoritmo es de aprendizaje no supervisado, pues es capaz de clasificar datos sin tener un supervisor que durante la etapa de entrenamiento le indique la clase a la que pertenece cada dato.
Se puede usar este algoritmo para segmentar una imagen por color, clasificando los pixeles que tienen colores parecidos en un mismo grupo.

k-means funciona de la siguiente manera:

  • Elige aleatoriamente k centros (uno por cada grupo)
  • Se etiqueta cada dato como perteneciente al grupo cuyo centro es más cercano
  • Se calcula el centro promedio de cada grupo y se repite el paso anterior.
  • El algoritmo se detiene cuando el promedio de cada centro de grupo no cambia o se alcanza el número máximo de iteraciones.

El centro de cada grupo y las distancias quedan definidos por el problema.
En el caso de las imagenes RGB se define el centro como una tripleta que contiene un valor para cada color:rojo, verde y azul. La distancia que se usará en este caso es la euclídea.
Sólo hay que imaginar los colores como una coordenada en el espacio 3D y la distancia entre dos puntos como el largo de la recta que los une.

El resultado del algoritmo dependerá de los centros iniciales, si estos son escogidos aleatoriamente probablemente se obtendrán resultados diferentes en cada ejecución.

OpenCV trae implementado este algoritmo, sin embargo yo programé mi propia implementación porque el algoritmo es sencillo y me pareció interesante hacerlo.
Un poco de código:

//calculo de centroides y matriz de clasificacion
for(it=0; it < iterations;it++) {
for(i=0;i<size;i++) {
red[i] = 0;
green[i] = 0;
blue[i] = 0;
count[i] = 0;
}
for(j=0; j < image->height; j++) {
for(i=0; i < image->width; i++) {
tcolor = getColor(image,i,j);
dist = distance(centroid[0], tcolor );
label = 0;
for(k=1; k<size; k++) {
temp = distance(centroid[k], tcolor );
if( temp < dist) {
dist = temp;
label = k;
}
}
++count[label];
red[label] += tcolor.r;
green[label] += tcolor.g;
blue[label] += tcolor.b;
if(it == (iterations-1)) {
labeled[i][j] = (uchar)label;
}
}
}
//actualizar centroides
for(i=0;i<size;i++) {
if(count[i]==0) continue; //no actualizar centroide si no se hay colores que promediar
centroid[i].r = (uchar)(red[i]/count[i]);
centroid[i].g = (uchar)(green[i]/count[i]);
centroid[i].b = (uchar)(blue[i]/count[i]);
}

}
//dejar con color de centroide mas cercano a cada pixel de la imagen a mostrar
for(i=0; i < image->width; i++) {
for(j=0; j < image->height; j++) {
image2->imageData[j*image->widthStep + i*image->nChannels + 2] = centroid[labeled[i][j]].r;
image2->imageData[j*image->widthStep + i*image->nChannels + 1] = centroid[labeled[i][j]].g;
image2->imageData[j*image->widthStep + i*image->nChannels ] = centroid[labeled[i][j]].b;
}
}

centroid[] : contiene un color por cada clase
size: cantidad de clases
labeled[][]: almacena los resultados de la clasificación, un número de 0 a (size-1) que relaciona cada pixel con su clase.
red[],green[],blue[],cout[]: variables usadas para calcular el color promedio

En el código anterior, dados los centroides de cada clase se procede a identificar cada pixel con la clase cuyo color se encuentre más cercano a su color.
Posteriormente se actualizan los centroides de cada clase, para ello se promedian los colores de los pixeles que fueron etiquetados como pertenecientes a la clase.
El proceso se repite hasta completar la cantidad de iteraciones especificada en la variable iterations.
Código fuente completo: kmeans-2010.tar.gz

Resultados al aplicar a la imagen superior izquierda el algoritmo con 5 clases y 90 iteraciones:

Tuesday, May 18, 2010

Filtro de Sobel en OpenCV

Una forma más elaborada de detectar bordes es el filtro de Sobel, dado que usa una mayor cantidad de píxeles de la vecindad para calcular el borde.

#include <opencv/highgui.h>
#include <opencv/cv.h>

void sobel(IplImage *img) {
int i,j,indice;
uchar *pixels = (uchar*)(img->imageData);
uchar *new_pixels = (uchar*)malloc(img->widthStep*img->height*img->nChannels*sizeof(uchar));

uchar sup;
uchar sup_izq;
uchar sup_der;
uchar inf;
uchar inf_izq;
uchar inf_der;
uchar izq;
uchar der;
int A;
int B;
double SUM;
//el origen esta en esquina superior izquierda del pantalla
for( i=1 ; i < (img->width-1) ; i++) {
for( j=1 ; j < (img->height-1) ; j++) {
indice = j*img->widthStep + i*img->nChannels;
sup = pixels[indice - img->widthStep]; //pixel superior al actual
inf = pixels[indice + img->widthStep]; //pixel inferior al actual
izq = pixels[indice - img->nChannels]; //pixel izq al actual
der = pixels[indice + img->nChannels]; //pixel der al actual
sup_izq = pixels[indice - img->nChannels - img->widthStep];
sup_der = pixels[indice + img->nChannels - img->widthStep];
inf_izq = pixels[indice - img->nChannels + img->widthStep];
inf_der = pixels[indice + img->nChannels + img->widthStep];
A = sup_izq + 2*sup + sup_der - inf_izq - 2*inf - inf_der;
B = sup_izq + 2*izq + inf_izq - sup_der - 2*der - inf_der;
SUM = A*A + B*B;
new_pixels[indice] = 255-(uchar)sqrt(SUM);

}
}
memcpy(pixels, new_pixels, img->widthStep*img->height*img->nChannels*sizeof(uchar));
free(new_pixels);

}

int main() {
CvCapture *cap = cvCreateCameraCapture(0);
IplImage *img;
IplImage *imgGray;
int notCreated=1;
while(1) {
img = cvQueryFrame(cap);
cvNamedWindow("hahaha",CV_WINDOW_AUTOSIZE);
if(notCreated) {
imgGray = cvCreateImage( cvSize(img->width,img->height), IPL_DEPTH_8U, 1 );
notCreated = 0;
}
cvCvtColor(img, imgGray, CV_RGB2GRAY);
sobel(imgGray);
cvShowImage("hahaha",imgGray);
char key = cvWaitKey(40);
if(key == 27)
break;
}
cvReleaseImage(&img);
cvReleaseImage(&imgGray);
cvReleaseCapture(&cap);
cvDestroyWindow("hahaha");
return 0;
}

Sunday, April 18, 2010

Efecto fotocopia en tiempo real

He estado jugando un poco con la librería de visión artificial de Intel, OpenCV. Programé el mismo efecto fotocopia que postie tiempo atrás, pero esta vez aplicandolo al video capturado por la webcam en tiempo real.
Esto no es difícil de lograr, sin embargo es interesante ver los resultados.

Aca el código:


#include <opencv/highgui.h>
#include <opencv/cv.h>

void fotocopia(IplImage *img, int difMax) {
int i,j,difh,difw,indice;
for( i=0 ; i < (img->width-1) ; i++) {
for( j=0 ; j < (img->height-1) ; j++) {
indice = j*img->widthStep + i*img->nChannels;
/*diferencia brillo en pixeles de una misma columna
widthStep es el tamaño de una fila en bytes,
ya que la imagen esta en un arreglo unidimensional.
Al sumar widthStep estamos pasando a la siguiente fila
*/
difh = ( (uchar*)(img->imageData) )[indice] - ( (uchar*)(img->imageData) )[indice+img->widthStep];
if( difh < 0) {
difh = difh * -1;
}

//diferencia brillo en pixeles de una misma fila

difw = ( (uchar*)(img->imageData) )[indice] - ( (uchar*)(img->imageData) )[indice+1];
if( difw < 0) {
difw = difw * -1;
}

//si hay mucha diferencia, es un contorno, hay que pintar el pixel negro
if ( (difh + difw) > difMax) {
( (uchar*)(img->imageData) )[indice] = 0 ;
} else {
( (uchar*)(img->imageData) )[indice] = 255 ;
}
}
}
}

int main() {
CvCapture *cap = cvCreateCameraCapture(0);
IplImage *img;
IplImage *imgGray;
cvNamedWindow("hahaha",CV_WINDOW_AUTOSIZE);
img = cvQueryFrame(cap);
imgGray = cvCreateImage( cvSize(img->width,img->height), IPL_DEPTH_8U, 1 );
while(1) {
img = cvQueryFrame(cap);
cvCvtColor(img, imgGray, CV_RGB2GRAY);
fotocopia(imgGray,5);
cvShowImage("hahaha",imgGray);
char key = cvWaitKey(40);
if(key == 27)
break;
}
cvReleaseCapture(&cap);
cvReleaseImage(&img);
cvReleaseImage(&imgGray);
cvDestroyWindow("hahaha");
}

Para compilar:

gcc `pkg-config --cflags opencv --libs opencv` fotocopia.c -o fotocopia

Aca un video de cómo se queda el efecto:

Thursday, April 1, 2010

tarjeta de video Intel GMA X4500HD Ubuntu

Problema:

Cuando abría alguna aplicación que hiciera uso de OpenGL esta veía con manchas negras o se pegaba y al ejecutar dmesg me aparecía lo siguiente:

[ 210.430986] [drm:i915_gem_object_pin_and_relocate] *ERROR* Relocation beyond target object bounds: obj f08cb9c0 target 23 delta 4096 size 4096.
[ 210.430992] [drm:i915_gem_execbuffer] *ERROR* Failed to pin buffers -22
[ 210.445817] [drm:i915_gem_object_pin_and_relocate] *ERROR* Relocation beyond target object bounds: obj f0a39180 target 23 delta 4096 size 4096.
[ 210.445827] [drm:i915_gem_execbuffer] *ERROR* Failed to pin buffers -22

....

Usando Ubuntu 9.10 the Karmic Koala con el paquete xserver-xorg-video-intel versión:
2:2.9.1~git-0ubuntu0tormod

Solución :

apt-get install libgl1-mesa-dri
Con lo cual se actualizaron los siguientes paquetes:
libdrm-intel1 a la versión 2.4.15~git-0ubuntu0tormod~jaunty
libgl1-mesa-dri a la versión 7.7.0~git20091114.57f40b18-0ubuntu0sarvatt~jaunty

La solución la encontré aquí.

Thursday, February 18, 2010

Editando imagenes con Octave

Me encanta el filtro fotocopia que trae el Gimp para dejar una foto como un dibujo, me dí cuenta de que es muy intuitivo hacer este efecto en Octave. Para ello sólo hace falta usar la función gradiente que toma una matriz y calcula la diferencia que hay entre dos elementos contiguos en cada fila y en cada columna.
Por ejemplo:

M =
1 2
3 4

[diffX, diffY] = gradient(M)

diffX =
1 1
1 1

diffY =
2 2
2 2

Se aprecia que diffX es la diferencia entre los elementos de las filas y diffY es la diferencia entre los elementos de las columnas.

Aplicando este concepto a las imagenes en escala de grises :

function fotocopia(imagen, salida)
im = imread(imagen);
[diffX,diffY] = gradient(im);
fotocopia = (diffX.^2 + diffY.^2).^(1/2);
fotocopia = fotocopia * 3;
fotocopia = 255 - fotocopia;
imwrite(fotocopia,salida);
endfunction

El operador . es para que se haga la operación a cada elemento de la matriz (elevar al cuadrado elemento por elemento)
La multiplicación por 3 es para realzar los bordes
La resta es para que el fondo quede blanco y el contorno oscuro (255 = blanco, 0 = negro).

fotocopia("plum.jpg","fotocopia.jpg");






Tuesday, February 16, 2010

Recuerdo




El año 2005 programé un juego de naves en C con la librería ncurses, usando caracteres ASCII.
Aprendí muchas cosas y sentí gran satisfacción al ver mi jueguito terminado. No creo que el código sea un ejemplo, pero fue para mí una pequeña victoria.
Os dejo el link para descargar el código y el ejecutable.
PD: Las balas no se veen porque estan mal programadas, su velocidad depende del procesador.

Monday, January 11, 2010

Puntero void*

Un puntero void* es un puntero genérico que se puede usar para hacer funciones que pueden recibir y/o devolver distintos tipos de datos según sea el caso. O sea que usando este tipo de puntero puedes hacer por ejemplo una función que trabaje con enteros si se le pasan punteros a enteros o con una estructura si se le pasan punteros a la estructura.
En el siguiente ejemplo se hace un casting a otro tipo de puntero para que así el compilador sepa cuantos bytes debe sumar a la dirección base, en este caso el casting es a char* por lo tanto el compilador sumará tamElem*k*sizeof(char) bytes a la dirección base.
Esta función recibe de argumento la función de comparación y busca en un arreglo el argumento llamado valor, en caso de hallarlo retorna el indice del elemento en el arreglo, si no encuentra al elemento en el arreglo retorna -1.



int busquedaLineal(void *valor, void *dirBase, int tamElem, int cantElem, int (*comparacion) (void*,void*)) {
int k;
for(k=0; k < cantElem; k++) {
void *elemActual = (char*)dirBase + tamElem*k;
if( comparacion(valor,elemActual) == 0 ) {
return k;
}
}
return -1;
}