martes, 12 de abril de 2016

Animación con processing - Capítulo 1

Después no escribir en todo este tiempo, estoy regresando con algunos de los grupos que tengo para ver algo sobre graficación con Processing, la cual es una herramienta con ya más de 15 años desarrollada por Casey Reas y Bejamin Fry, dos investigadores y profesores altamente interesados en el aprendizaje de sus alumnos. Para más información pueden visitar el sitio de processing.

En esta sección emplearemos dos archivos de imagen, con los cuales se irá desarrollando parte de está práctica introductoria. Primero describiremos las siguientes variables:

int x=0;  // servirá para indicar la posición de nuestro personaje en pantalla en el eje de las x's.
PImage p; // Contiene la imagen principal con todas las secuencias de nuestro personaje
PImage fondo; // Es el lugar donde se moverá el personaje
PImage m[]; // aquí se guardará de momento en un arreglo cada secuencia del personaje
int n; // servirá para indicar el índice en el arreglo   
int y; // indica la altura para la posición del personaje en pantalla

Un método importante es el setup(), este método se ejecuta al inicio y corre una sola vez, aquí se inicializa el "SKETCH".

public void setup(){
    size(1000,500);
    fondo = loadImage("calle.png"); // nombre del recurso imagen
    m = new PImage[8];
    p = loadImage("felipillo.png");   // nombre del recurso imagen
    m[0] = p.get(28,0,75,140);  //47
    m[1] = p.get(126,14,88,140);  // 
    m[2] = p.get(214,0,108,140);
    m[3] = p.get(334,-4,100,140);
    m[4] = p.get(450,-2,100,140);
    m[5] = p.get(551,12,100,140);
    m[6] = p.get(652,0,100,140);
    m[7] = p.get(766,-2,98,140);
    n=0;
    y = 300;
    frameRate(8);                           // Velocidad de cuadros a desplegar, a menor valor más lento
}

Una vez que se han cargado las imágenes es posible iniciar el Sketch, para esto existe un método que se llama "draw()", el cual es un método ciclo, siempre se ejecuta de forma ciclica a menos que sea detenido desde el mismo método.

public void draw(){
    image(fondo,0,-150);  // Se depliega primero el fondo de pantalla
    image(m[n],x,y);        // Después se depliega la imagen
     if(x > width)
        x=0;

}

El orden que se despliegan las imágenes obedece a que es como un efecto grafiti, la imagen que se vaya poniendo posteriormente estará sobre las demás imágenes previamente colocadas.

Finalmente hay que tener control sobre nuestro personaje al que llamaremos "Martincillo", esto se hará usando la tecla de flecha derecha del teclado.

public void keyPressed(){
       if(keyCode == RIGHT){
           n++;
           x += 8;
           if(n==8)
              n=0;
       }
}

Este método directamente monitorea si alguna tecla ha sido presionada, si es así usando el atributo "keyCode" se identifica de acuerdo al valor de la tecla presionada en este caso "RIGHT", si es así se incremente la posición en el eje de las x's y se incrementa el índice del arreglo para traer la imagen siguiente de la secuencia.



Finalmente tendrá algo como en la imagen mostrada, si usa la tecla de flecha derecha irá viendo la secuencia de animación de Martincillo.

Los recursos fueron tomados de la web, pero si se ha entendido el proceso, pueden crear sus propias imágenes para el proyecto:

Referencia 1  Personaje
Referencia 2  Fondo


¡Saludos y buena programación, hasta la próxima!






viernes, 27 de marzo de 2015

PROMEDIO DE IMÁGENES

En algunas aplicaciones es necesario obtener un promedio sobre ciertas imágenes. Esto con el fin de sacar datos estadísticos o de otra índole para alguna prueba o propósito específico. Para poder llevar a cabo esto es necesario que las imágenes a las cuales se les calculará su valor promedio sean del mismo tamaño en píxeles a lo ancho y alto de la imagen.


     Para este ejemplo, se usarán dos imágenes con el mismo tamaño en píxeles de alto y ancho: 224x270.
     Primeramente se deben crear matrices que sean capaces de almacenar estas imágenes empleando 3 canales debido a que son imágenes a color. La instrucción:
cv::Mat imagenresultante = cv::Mat::zeros(alto, ancho, CV_32FC3);
Dice que la imagen a color será en 3 canales y se manejará con notación decimal, dado que hay que recordar que el máximo valor que puede tomar un píxel es 255, es decir si suma 250+200 le dará como resultado 255, si se pasa a flotante dará 450 y al dividir sobre 2 dará 225 el cual es el valor promedio del píxel.
   Por eso es importante que al final antes de desplegar la imagen resultante, convierta el valor nuevamente a entero sin signo de 8 bits. Un píxel puede variar su valor desde 0 a 255 para cada uno de los 3 canales de la imagen a color.
    La variable count, sirve para indicar el total de imágenes de las que se tomará el valor promedio, es importante que observe que tenga el mismo número de imágenes a las especificadas por la variable para evitar cualquier excepción en tiempo de ejecución.
    Para hacer el cambio a 8 bits sin signo, se aplica el comando:
imagenresultante.convertTo(imagenresultante,CV_8UC3);
      Dentro del ciclo for lo que resta es simplemente sumar imagen tras imagen y al final dividir entre el total cuyo valor está en la variable count.
      A continuación se le lista el código para poder llevar a cabo esta tarea:
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/opencv.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
using namespace cv;
using namespace std;

int main()
{
    cv::Mat frame, tmp;
    const int count=2;
    const int ancho =224;
    const int alto = 270;
    cv::Mat imagenresultante = cv::Mat::zeros(alto, ancho, CV_32FC3);
    char nombrearchivo[50];
    for(int i=1; i<= count ; i++){
        sprintf(nombrearchivo,"cara%d.jpg",i);
        frame = imread(nombrearchivo, CV_LOAD_IMAGE_COLOR);
        frame.convertTo(tmp,CV_32FC3);
        imagenresultante += tmp;
        cout << "i = "<<i <<endl;
    }
    imagenresultante *= (1.0/count);

    imagenresultante.convertTo(imagenresultante,CV_8UC3);
    imshow("",imagenresultante);

    waitKey(0);
    return 0;
}

    Finalmente el resultado será algo similar a:


      Puede probar ahora usando más imágenes que tenga para ver resultados interesantes.



jueves, 12 de marzo de 2015

IDENTIFICACIÓN DE CONTORNO DE IMÁGENES

Introducción:
       La obtención de contorno también se le conoce como seguimiento de borde. El trazado de contorno es una técnica que se aplica a imágenes digitales para extraer sus bordes. La pregunta importante al analizar la imagen es: ¿Qué es un borde? Si sabemos que la imagen digital es un grupo de pixeles en un cuadro donde cada pixel tiene cierto valor. Para el caso básico una imagen puede ser transformada a escala de grises y con un valor de umbral a dos tonos (Blanco y Negro), segmentación de dos niveles, o imagen binarizada.
      El borde de un patrón dado 'P' es el conjunto de pixeles borde de P. Existen dos tipos de pixeles de borde:
      1) Pixeles de 4-bordes
          Un pixel negro se considera de 4-bordes si comparte al menos unos de sus bordes con un pixel blanco.
      2) Pixeles de 8-bordes
          Un pixel negro se considera de 8-bordes si comparte al menos uno de sus bordes con un pixel blanco.
    Un punto digno de remarcar es que no es suficiente con identificar los pixeles borde de un patrón  para extraer el contorno. Lo que se requiere es una secuencia ordenada de pixeles frontera que puedan extraer la forma general del patrón.

¿Por qué queremos obtener el contorno de un objeto en una imagen?
Respuestas:
     a) Para conocer su forma.
     b) Para clasificación de objetos.
Obtener el borde de una imagen nos da información suficiente para clasificarla conforme a su forma, Por ejemplo si se quiere identificar monedas sobre una mesa, un contorno tipo círculo nos diría en que parte de la mesa se encuentran.

OpenCV
           Esta biblioteca cuenta ya con los métodos necesarios para realizar está tarea. Como ejemplo, vea el siguiente código:

#include "opencv2/highgui/highgui.hpp"
#include "opencv2/opencv.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
using namespace cv;
using namespace std;

Mat color;
Mat gris;
RNG rng(12345);

int main()
{
    color =  imread("figuras-geometricas.jpg");

    // Mostramos la imagen con la que se va a trabajar
    namedWindow("IMAGEN COLOR",CV_WINDOW_AUTOSIZE);
    imshow("IMAGEN COLOR",color);

    // Se pasa la imagen de color a tono de grises para trabajar con ella
    cvtColor(color,gris,CV_RGB2GRAY);
    // Se binariza con un valor de umbral intermedio 128, para una mejor resolucion de las figuras
    threshold(gris,gris,128,255,CV_THRESH_BINARY);

    namedWindow("IMAGEN GRIS",CV_WINDOW_AUTOSIZE);
    imshow("IMAGEN GRIS",gris);

// Se prepara un vector para guardar los grupos de pixeles que conformarán el contorno.
    vector<vector<Point> > contornos;
    vector<Vec4i> resultado;

  findContours(gris,contornos,resultado,CV_RETR_TREE,CV_CHAIN_APPROX_SIMPLE,Point(0,0));

    /// Dibujar los  contornos
  Mat dibujo = Mat::zeros( gris.size(), CV_8UC3 );
  for( int i = 0; i< (uint16_t)contornos.size(); i++ )
     {
       Scalar color = Scalar( rng.uniform(0, 255), rng.uniform(0,255), rng.uniform(0,255) );
       drawContours( dibujo, contornos, i, color, 2, 8, resultado, 0, Point() );
     }

  /// Mostralos en una ventana
  namedWindow( "CONTORNOS", CV_WINDOW_AUTOSIZE );
  imshow( "CONTORNOS", dibujo );



    cvWaitKey(0); // Espera a que se presione una tecla

    // Salir de la aplicación
    cvDestroyAllWindows();
}

Al ejecutar el código observará en cada una de las ventanas el seguimiento en el tratamiento de la imagen para la obtención de su contorno:


Referencia: 
Imagen tomada para fines explicativos de:
http://arte.about.com/od/Que-es-el-arte/fl/Figuras-geometricas.htm





















miércoles, 22 de junio de 2011

MANEJO DE PUERTO PARALELO

PUERTO PARALELO
       (0x378)

   La salida de datos de una computadora a un hardware de propósito específico, se lleva a cabo por medio del uso de los puertos, estos pueden ser el paralelo, serial, o incluso vía inalámbrica. El más sencillo de usar, dado que no usa sincronía para el envío de datos es el puerto paralelo. Para ejemplificar esto usaremos Linux Mint, una distribución basada en Debian, y que gracias al uso del paquete apt, no facilita la configuración rápida de un ambiente de desarrollo básico.
    Primeramente, hay que entrar como usuario root, o emplear el comando sudo, se hará lo siguiente:

$sudo apt-get install build-essential

 De esta manera, se tendrá lo básico para hacer el desarrollo, primero empezaremos con el programa, esto se puede llevar a cabo con cualquier editor, en este caso emplearemos vi:
$vi ejemplo.c
Ya dentro del editor, se teclea lo siguiente:
#include <stdio.h>
#include <stdlib.h>
#include <sys/io.h>
#define puerto 0x378
int main(void){
   int i=0;
   unsigned int p = 0x02;
   
   if( ioperm(puerto,1,1)) {
      perror("Se ha detectado un error");
      exit(1);
   }
   for(i = 1 ; i < 20 ; i++ ) {
      outb(p,puerto);
      sleep(3);
      p = 0x00;
      outb(p,puerto);
      if ( i%2 != 0 )
         p = 0x00;
     else
        p = 0x02;
    }
    if ( ioperm(puerto,1,0)){
      perror("Se ha detectado un error ");
      exit(1);
   }
   exit(0);

 }
Esto se graba, y se compila, para después invocarse desde consola, de la siguiente forma:

$ gcc -O2 -o ejemplo ejemplo.c

Ya una vez compilado, lo podemos invocar de la siguiente forma:

$ ./ejemplo  <ENTER>

No hará nada visible para nosotros, ahora debemos de preparar la parte de hardware, para lo siguiente es necesario cuentes con el siguiente material:
- transistores NPN C1008 Y-913 o 2N3904
- misma cantidad de LED que de transistores
- resistencias de 220 ohms, igual cantidad que el número de LEDs
- 2 pares de resistencias de 120 ohms, por cada LED que se use.
- 1 tableta prototypeboard, y cables para conexión en proto.

El puerto paralelo tiene 8 bits de datos, mediante los cuales se envía la información.







viernes, 3 de junio de 2011

CAPTURA DE IMAGEN CON OPENCV

Objetivo
Captura de cuadro(s) de imagen mediante cámara web.

Introducción
Opencv, es una suite que nos ofrece un sin número de funciones dentro del API, para llevar a cabo operaciones de captura y procesamiento de imágenes, entre ellas la captura de imagen fija. En este documento, se muestran las funciones básicas para captura de imagen, así como se explica el uso de parámetros en el empleo de las mismas.
OpenCV emplea el comando CvCaptureFromCAM para accesar a la cámara web conectada vía USB a la computadora. El parámetro CV_CAP_ANY , nos facilita que la función tome la cámara web que este conectada en ese instante. El identificador pCapture toma un valor de TRUE si la cámara es hallada y un valor de FALSE si esta tiene algún problema o simplemente no hay cámara web
conectada. El comando cvQueryFrame, nos proporciona la captura de un cuadro de imagen de la cámara web , si la imagen se obtiene adecuadamente el identificador pVideoFrame arroja un valor de TRUE de otra forma es FALSE.
Para guardar la imagen capturada se emplea la función: cvSaveImage . Está espera 2 parámetros, el nombre de archivo y el cuadro de imagen capturado. Algunas ocasiones dependiendo de la forma de captura de la imagen, se debe agregar un tercer parámetro que indica el número índice de cámara web que se esta empleando, en nuestro caso -0- .
Finalmente cvReleaseCapture se encarga de cerrar el objeto de captura de video, y dejar libre recursos, se le da, de parámetro un objeto del tipo de clase CvCapture.
A continuación se da un ejemplo, de captura de cuadro de imagen, el cual está escrito de tal forma que pueda capturar dos cuadros de imagen.

#include <stdio.h>
#include "cv.h"
#include "highgui.h"
#include <string.h>
int main(int argc, char ** argv)
{
CvCapture * pCapture = 0;
IplImage * pVideoFrame = 0;
int i;
char filename[50];
// Inicializar video de captura
pCapture = cvCaptureFromCAM( CV_CAP_ANY );
if( !pCapture )
{
fprintf(stderr, "Ocurrio una falla al momento de iniciar la captura de video.\n");
return -1;
}
// Captura dos cuadros de imagen y escribelos como archivos
for(i=0; i<2; i++)
{
pVideoFrame = cvQueryFrame( pCapture );
if( !pVideoFrame )
{
fprintf(stderr, "Ocurrio una falla al obtener un cuadro de imagen\n");
}
// Guarda el cuadro de imagen del video capturado como un archivo imagen
sprintf(filename, "CuadroImagen%d.jpg", i+1);
if( !cvSaveImage(filename, pVideoFrame,0) )
{
fprintf(stderr, "Ocurrio un error al momento de guardar el archivo %s\n", filename);
}
// IMPORTANTE: NO quites o modifiques la imagen que se regresa.
// de cvQueryFrame() !
}
// Termina la captura de video y libera recursos
cvReleaseCapture( &pCapture );
return 0;
}