Archivo para la categoría OpenCv

Playing with Qt and OpenCv

In this project I’m going to capture the image from a USB camera, using openCV, and showing all in a QT window.

Let’s started:

The first thing that we have to do, once we have created the project, is to edit the *.pro file, and add our OpenCv paths.

QT       += core gui
 TARGET = untitled
 TEMPLATE = app
 INCLUDEPATH += /home/opencv_source/trunk/opencv/include/opencv
 LIBS += -L/home/opencv_source/trunk/opencv/release/lib -lml -lcvaux -lhighgui -lcv -lcxcore
 SOURCES += capture.cpp\
 main.cpp\
 mainwindow.cpp

HEADERS  +=  capture.h\
 mainwindow.h

FORMS    += mainwindow.ui

I had a problem,  “undefined recerence to vtable” ,that I could fix placing on top capture.h and capture.cpp files.

Once we have ready our environment I’ll explain the content of the files:

– capture: Is the most important file, this class runs in other thread that the GUI, and all that it does is emitting a signal when has a new IplImage from the cam.

Create a new Thread in QT is so easy, all that we have to do is derivate from QThread and implement the virtual method void run()

class Captura : public QThread
 {
 Q_OBJECT              //We need this macro for use Signals mechanism
 public:
 void run();
 signals:
 void newImgAvailable(QImage*);
 };

void Capture::run()

{
 CvCapture * cap = cvCaptureFromCAM(0);
 if(cap == NULL)
 {
 cout << "There were troubles .. " << endl;
 }
 else
 {
 cout << "Ok" << endl;
 IplImage* imgDisplay;
 while(1){
 imgDisplay = cvQueryFrame(cap);
 QImage* image = IplImage2QImage(imgDisplay);
 emit newImgAvailable(image);
 QThread::msleep(10);
 }
 }
 }
<pre>

– In mainwindow we are going to create the instance of capture, start it, and connect with our own slots wich will set the image in a QLabel using setPixMap property.

The header:

class MainWindow : public QMainWindow
 {
 Q_OBJECT

public:
 explicit MainWindow(QWidget *parent = 0);
 ~MainWindow();

public slots:
 void getImg(QImage*);
 private:
 Ui::MainWindow *ui;
 };

and the cpp:

MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent),  ui(new Ui::MainWindow)
 {
 ui->setupUi(this);
 Capture* cap = new Capture();               //Create the Thread wich deals with opencv
 connect(cap, SIGNAL(newImgAvailable(QImage*)),this, SLOT(getImg(QImage*)));
 cap->start();  //starting the thread...
 }

void MainWindow::getImg(QImage* img)   //This code will update the image
 {
 ui->label->setPixmap(QPixmap::fromImage(*img));
 delete img;
 }

To avoid memory leaks we have to bear in mind that getImg has to delete the QImage.

Important: We mustn’t release the IplImage* in the loop, because this  is made internally and it would be a mistake

Anuncios

1 comentario

Porque no emplear cvSet2D. Why we shouldn’t use cvSet2D

Trás implementar la clase para el cambio de espacio de color he querido comprobar cuanta diferencia había entre los distintos métodos:

JETMAP: ha tardado: 9.7 milisegundos
JETMAPFAST: ha tardado: 1.555 milisegundos
HsvMap: ha tardado: 9.702 milisegundos
HsvMapFast: ha tardado: 1.556 milisegundos
HotMap: ha tardado: 9.735 milisegundos
HotMapFast: ha tardado: 1.555 milisegundos
CoolMap: ha tardado: 9.727 milisegundos
CoolMapFast: ha tardado: 1.556 milisegundos
SpringMap: ha tardado: 9.726 milisegundos
SpringMapFast: ha tardado: 1.569 milisegundos
SummerMap: ha tardado: 9.672 milisegundos
SummerMapFast: ha tardado: 1.568 milisegundos
AutumnMap: ha tardado: 9.789 milisegundos
AutumnMapFast: ha tardado: 1.554 milisegundos
WinterMap: ha tardado: 10.007 milisegundos
WinterMapFast: ha tardado: 1.555 milisegundos
GrayMap: ha tardado: 9.924 milisegundos
GrayMapFast: ha tardado: 1.554 milisegundos
BoneMap: ha tardado: 9.714 milisegundos
BoneMapFast: ha tardado: 1.555 milisegundos
CopperMap: ha tardado: 9.961 milisegundos
CopperMapFast: ha tardado: 1.564 milisegundos
PinkMap: ha tardado: 9.681 milisegundos
PinkMapFast: ha tardado: 1.556 milisegundos
LinesMap: ha tardado: 9.96 milisegundos
LinesMapFast: ha tardado: 1.568 milisegundos

Trás ver la evidente ventaja, a continuación expongo como sería el método rápido:

int alto    = entrada->height;
int ancho   = entrada->width;
int step    = entrada->widthStep;
int canales = entrada->nChannels;
uchar* data      = (uchar *)entrada->imageData;

for(int i=0; i < alto;i++)
for(int j=0; j <ancho;j++)
for(int k=0; k < canales;k++)
data[i*step+j*canales+k]= pink[data[i*step+j*canales+k]*3 +2-k];

After develop the class for space color changes, I wanted check the differences between both methods:

JETMAP: takes: 9.7 miliseconds
JETMAPFAST: takes: 1.555 miliseconds
HsvMap: takes: 9.702 miliseconds
HsvMapFast: takes: 1.556 miliseconds
HotMap: takes: 9.735 miliseconds
HotMapFast: takes: 1.555 miliseconds
CoolMap: takes: 9.727 miliseconds
CoolMapFast: takes: 1.556 miliseconds
SpringMap: takes: 9.726 miliseconds
SpringMapFast: takes: 1.569 miliseconds
SummerMap: takes: 9.672 miliseconds
SummerMapFast: takes: 1.568 miliseconds
AutumnMap: takes: 9.789 miliseconds
AutumnMapFast: takes: 1.554 miliseconds
WinterMap: takes: 10.007 miliseconds
WinterMapFast: takes: 1.555 miliseconds
GrayMap: takes: 9.924 miliseconds
GrayMapFast: takes: 1.554 miliseconds
BoneMap: takes: 9.714 miliseconds
BoneMapFast: takes: 1.555 miliseconds
CopperMap: takes: 9.961 miliseconds
CopperMapFast: takes: 1.564 miliseconds
PinkMap: takes: 9.681 miliseconds
PinkMapFast: takes: 1.556 miliseconds
LinesMap: takes: 9.96 miliseconds
LinesMapFast: takes: 1.568 miliseconds

Once we see the advantages of using pointers, I write the code for the fastest way:

int alto    = entrada->height;
int ancho   = entrada->width;
int step    = entrada->widthStep;
int canales = entrada->nChannels;
uchar* data      = (uchar *)entrada->imageData;

for(int i=0; i < alto;i++)
for(int j=0; j <ancho;j++)
for(int k=0; k < canales;k++)
data[i*step+j*canales+k]= pink[data[i*step+j*canales+k]*3 +2-k];

1 comentario

Protegido: Supresión de fondos

Este contenido está protegido por contraseña. Para verlo introduce tu contraseña a continuación:

1 comentario

Sistema de detección de clases de objetos utilizando modelos de partes deformables

En este proyecto se ha implementado un detector de clases genéricas de objetos utilizando una aproximación piramidal

y detectores de gradientes orientados (HOG).

Está desarrollado en C++ y hace uso de modelos ya entrenados existentes en workspaces de MATLAB.

A continuación unas imágenes del detector funcionando y un video.

Deja un comentario

Protegido: Visor de consumo de memoria

Este contenido está protegido por contraseña. Para verlo introduce tu contraseña a continuación:

Deja un comentario

Detección de bordes

La detección de bordes se basa en los cambios de intensidad entre un pixel y su vecindario.
Para ello empleamos un filtro de matriz. Las matrices más usadas corresponden a la matriz de Laplace y su aproximación
Una matriz de Laplace, tiene la siguiente forma:

1 1 1
1 -8 1
1 1 1

Ahora tenemos que tener en cuenta que el color blanco se corresponde a 25 y el ne  gro a 0, y tenemos algo tal que:

255 255 255 Que lo multiplicamos por la matriz 255 255 255
255 255 255 255 -2040 255
0 0 0 0 0 0

Una vez hayamos hecho esto, sumamos todos los valores, y hayamos su valor absoluto, 765 en este caso.
El cual umbralizaremos (lo normal es entre 20 y 30) y diremos que el punto denota un borde

La info la he extraido y resumido a partir de aquí:
http://vidaartificial.com/index.php?title=Una_introduccion_a_la_Vision_Artificial_%28Generation5.org%29

#include "program.h"
#include <fstream>
#include <vector>

using namespace std;

/***********************************************
        Detector de Bordes:
                                1   1   1
Aplicamos una mascara tal que:  1  -8   1
                                1   1   1

sumamos los puntos y si esta por encima de un umbral
ese punto nos mola.

OpenCv usa un puntero, lo cual nos complica un poco la cosa
mi idea es, defino la matriz del mismo tamañao, y la inicializo
y entonces ya puedo acceder dentro de un bucle for
***********************************************/

int main (int argc, char **argv)
{

 IplImage *entrada = cvLoadImage(argv[1]);              //Leemos la entrada

 int alto    = entrada->height;                         //guardamos las propiedades
 int ancho   = entrada->width;
 int canales = entrada->nChannels;

 vector <int> columna;                                   //en cada columna vamos metiendo los datos, y luego todos los metemos en una fila
 vector <vector <int> > fila;                            //Luego la fila se compone de elementos columnas
 vector <vector <vector <int> > > img_canales;           //y un conjunto de filasxcolumnas, forman un canal

for(int z = 0; z < canales; z++)
{
     for(int f = 0; f < alto ;f++)
     {
        for(int c = 0; c < ancho ; c++)              {               columna.push_back(c);              }         fila.push_back(columna);         columna.clear();      }      img_canales.push_back(fila);    fila.clear();     }     /*      AHORA QUEDA COPIAR LOS DATOS EN LA MATRIZ  El parametro widthStep, indica el número de bytes utilizados para representar cada fila de una imagen.  Una imagen de anchura 101 píxeles, de tipo byte con un widthStep igual a 104, codificará cada fila como una ristra de 104 bytes, de los cuales los 101 primeros se corresponderán con los píxeles de cada fila. ¿Y los 3 restantes, en este ejemplo? OpenCv los rellena con ‘/0′. En OpenCV, el campo widthStep siempre tiene que ser múltiplo de 4. Siempre. Así pues, una imagen (tanto en color como en escala de grises) de tipo byte con anchura de 102 píxeles tendrá un widthStep con valor 104, exactamente el mismo que para otra imagen diferente de anchura 103 ó 104. Esto es así porque la mínima cantidad de memoria que se puede reservar es de 4 bytes en algunos formatos de imagen. */  /*          este trozo de codigo invierte una imagen*/ int step      = entrada->widthStep;
uchar* data      = (uchar *)entrada->imageData;

  for(int i=0;i<alto;i++)
     for(int j=0;j<ancho;j++)
        for(int k=0;k<span 				data-mce-type="bookmark" 				id="mce_SELREST_start" 				data-mce-style="overflow:hidden;line-height:0" 				style="overflow:hidden;line-height:0" 			></span><canales;k++)
            data[i*step+j*canales+k]= 255-data[i*step+j*canales+k];

 cvNamedWindow("kk");
 cvShowImage("kk",entrada);
 cvWaitKey(0);

 return 0;
}

Deja un comentario

Visor de imágenes en OpenCv. Hilos en Linux.

Estoy realizando un visor de imágenes en OpenCv, y una de las cosas que quiero lograr es rapidez, que no tenga que esperar entra cambio y cambio de imagen. El problema surge en que la mayoría de fotos que tengo están entorno a las 2 – 3 Mb, por lo cual, lo que estoy implementando es cargar en paralelo a la representación de la primera imagen, un máximo de 4 más. Para ello empleo los hilos:

– De momento la técnica empleada consiste en cargar en memoria un archivo generado mediante una llamada al sistema, y que guarda los paths de todas las imágenes

system("ls *.JPG  >>  ~tmpfiles.txt");

A continuación creo un array de identificadores de hilos:

pthread_t hilos[4];  

También preparo los datos guardandolos en un array de estructuras que contienen el puntero a la IplImage, que será lo que se rellene en el hilo, y el path de una imágen, que es la entrada del hilo.

	pthread_create(&hilos[0], NULL, &thread, &datosi[0]);
	pthread_create(&hilos[1], NULL, &thread, &datosi[1]);
	pthread_create(&hilos[2], NULL, &thread, &datosi[2]);
	pthread_create(&hilos[3], NULL, &thread, &datosi[3]);

Hay que fijarse que a la función pthread_create, se le pasa, la dirección del identificador del hilo, la dirección de la función paralela, en este caso thread, y la dirección de la estructura de datos. Esto es así porque thread es una función de tipo, puntero a void, que recibe también un puntero a void como argumento

void *thread(void *direccion_datos)

Dentro de la función, para poder hacer uso de la estructura de datos debemos hacer un cast, primero definimos un puntero a una estructura de tipo datos_para_hilo (que contiene el IplImage* y el path, como dije antes) y a continuación hacemos el cast para que C++ sepa que tamaño hay que asignar.

	datos_para_hilo *mi_dato;
	mi_dato = (datos_para_hilo *) direccion_datos;

La forma de usar los datos es la típica, modificando los datos, en la estructura de la que hace uso el main:

	mi_dato->img = cvLoadImage (path);
	cout << "Ancho: " << mi_dato->img->width << endl;

Por último, antes de hacer uso de los datos, deberemos esperar a que cada hilo acabe, como tenía los identificadores de los hilos en un array, puedo hacer esto

for(int i = 0 ; i < 4 ; i++)
{
        //Esperamos la finalización de cada hilo
	pthread_join(hilos[i],NULL);                                  
 
       //Los hilos modificaron los datos de datosi    
 	cvShowImage (datosi[i].cadenas, datosi[i].img);	  
	char tecla = cvWaitKey (0);
       //Hay bastantes más opciones quitadas para el post
	switch(tecla)                    
	{
		case 27 : exit(0);
		          break;
	}
  	cvDestroyWindow(datosi[i].cadenas);	 
}

13/10/2009
He subido la versión de desarrollo 0.1 a mi repositorio.
Ya tengo la versión bastante avanzada en cuanto a los hilos, ya no falla al lanzar varios a la vez, y no tiene memory leaks, que era una cosa que si se producían..
Queda pendiente lanzar los hilos mientras se visualiza la imagen 2, porque de momento carga 4 imágenes por vez y se nota.

14/10/2009
Precargo 4 imágenes, y a continuacion la que se va visualizando voy sustituyendola por otro hilo en ejecución… ahora va mucho mejor que antes.
Carga todas las imágenes.
No falla al cargar imágenes que no existen (De hecho no las carga, hago un stats antes)
He comentado bien el código.

Deja un comentario