Descarga ProyectoIntegrador_GermanMas

Anuncio
Universidad Nacional de San Juan
Facultad de Ingeniería
Detección y Medición de
Arandelas mediante una cámara
Trabajo Integrador
• Complementos de Informática
• Temas Específicos de Control I (Visión)
Más, Germán Emilio – 23860
1
Año 2015
Objetivo
El objetivo de este trabajo integrador es detectar y medir arandelas de forma automática,
utilizando una cámara web y un sistema de iluminación posterior o por contraste (backlight). Los
datos obtenidos son recopilados para su posterior uso.
Sistema de Iluminación
La técnica de iluminación por contraste se utiliza situando el objeto entre la fuente de luz y
la cámara. De esta forma se puede reconocer la silueta del objeto por contraste y realizar
mediciones muy precisas, aunque no permite reconocer los detalles superficiales de las piezas a
inspeccionar.
La fuente de luz utilizada consiste en 9 LED blancos dispuestos en una matriz de 3x3,
alimentados con 12V. Los LED proveen una iluminación direccional y tienen la ventaja de utilizar
corriente continua, pues la corriente alterna genera variaciones imperceptibles de intensidad, que
puede llegar a ser capturada por la cámara.
A continuación se puede observar el arreglo construido.
Imagen 1 – Sistema de Iluminación construido
Imagen 2 – Matriz de LEDs
2
Software
El programa carga una imagen desde un archivo o cámara y la procesa para detectar una
arandela. La información de cada arandela es recopilada utilizando un vector. Esto permite manejar
la información de forma ordenada.
El software fue programado utilizando C++ y OpenCV 2.4.9 en el IDE Eclipse Luna. A
continuación se explican y fundamentan partes del código, que puede verse completo en el
Apéndice 1.
Clases
Se implementan dos clases: Arandela y Procesador, explicadas a continuación.
Arandela
La clase proviene del objeto real a medir. Las medidas importantes de una arandela son el
diámetro interior, el diámetro exterior y la altura. La altura se asume constante, ya que no puede
medirse con el sistema utilizado.
Imagen 3 – Medidas útiles de una arandela
Como parámetros privados de la clase encontramos aquellas medidas físicas que describen a
una arandela:
private:
//Parametros
float mDiametroInterior;
float mDiametroExterior;
float mAltura;
float mArea;
float mVolumen;
time_t mTiempo;
El área y volumen son medidas secundarias que derivan de los diámetros y la altura. Se
calculan porque pueden servir para alguna aplicación más específica (como calcular el peso, por
ejemplo). El tiempo hace referencia al tiempo en el cual la arandela fue detectada.
Los métodos de esta clase incluyen distintos constructores, destructor, acceso a los
parámetros y sobrecarga de operadores.
Se agrega también una variable estática para contar la cantidad de arandelas detectadas.
3
public:
//Variables estáticas
static unsigned int s_CantidadArandelas;
//Constructores
Arandela(void);
Arandela(float, float); //Diametros Interior y Exterior
//Destructores
~Arandela(void);
//Gets
const float GetDiametroInterior(void) const;
const float GetDiametroExterior(void) const;
const float GetAltura(void) const;
const float GetArea(void) const;
const float GetVolumen(void) const;
const double GetTiempo(void) const;
//Sets
void SetDiametroInterior(float);
void SetDiametroExterior(float);
void SetAltura(float);
void SetTiempo(void);
//Sobrecarga de Operadores
Arandela& operator= (const Arandela&);
friend std::ostream& operator<<(std::ostream&, const Arandela&);
Procesador
Esta clase es más abstracta que la anterior, ya que no proviene de un objeto físico. Tiene
como objetivo realizar el procesamiento de imágenes y detección de arandelas de forma
encapsulada.
Posee dos parámetros matriciales sobre los que trabaja y tres parámetros numéricos que
pueden ser modificados para la calibración del procesamiento.
private:
//Parametros
Mat mImagen;
Mat mMascara;
int mValorThreshold;
int mAreaMin;
int mAreaMax;
La imagen es enmascarada y procesada para detectar la arandela. Los valores numéricos
intervienen en el procesamiento.
Los métodos de esta clase incluyen constructores, destructor, acceso a los parámetros,
muestra y procesamiento.
public:
//Constructores
Procesador(void);
Procesador(Mat, Mat);
//Destructores
~Procesador();
//Get
const Mat GetImagen(void) const;
const Mat GetMascara(void) const;
4
const int GetValorThreshold(void) const;
const int GetAreaMin(void) const;
const int GetAreaMax(void) const;
//Set
void SetImagen(string); //Via archivo
void SetImagen(int); //Via camara
void SetMascara(string); //Via archivo
void SetMascara(int); //Via camara
void SetValorThreshold(int);
void SetAreaMin(int);
void SetAreaMax(int);
//Otros
void MuestraImagen(string);
void MuestraMascara(string);
const Arandela DetectarArandela(void) const;
Se puede observar una sobrecarga en los métodos SetImagen y SetMascara. Cuando el
argumento es una cadena de caracteres, la imagen se lee desde un archivo y cuando es un entero, es
tomada desde la cámara (indicada por su número).
El corazón de la clase Procesador es sin duda el método DetectarArandela, que se
explicará a continuación.
Detección
La clase Procesador trabaja con la imagen y la máscara. La imagen, es la foto tomada desde
la cámara y la máscara es una imagen binaria utilizada para determinar la región de interés.
Analicemos primero la máscara. Para calcularla, se toma una foto del iluminador sin
ninguna arandela y se procesa.
void Procesador::SetMascara(int cam)
{
Mat img;
VideoCapture Camara(cam);
if(!Camara.isOpened())
throw(3); //ERROR_CAMARA_NO_ENCONTRADA
Camara.read(img);
Hasta aquí se toma la imagen, que será contenida en la matriz img. Ahora comienza su
procesamiento.
El primer paso es convertir la imagen a escala de grises, para eliminar la información que
pueda existir de color.
cvtColor(img, mMascara, CV_BGR2GRAY);
El paso siguiente consiste en binarizar la imagen. Para ello se utiliza la función de OpenCV
threshold. Los valores de intensidad menores a mValorThreshold son puestos en 0 y los mayores
son llevados a 255.
threshold(mMascara, mMascara, mValorThreshold, 255, THRESH_BINARY);
En este caso particular, debido a las características constructivas del iluminador, se realiza
5
una doble erosión para reducir un poco el tamaño del círculo.
erode(mMascara, mMascara, Mat(), Point(-1, -1), 1, 1, 1);
erode(mMascara, mMascara, Mat(), Point(-1, -1), 1, 1, 1);
}
Una vez calculada la máscara, ya podemos realizar el procesamiento de la imagen para
detectar una arandela.
const Arandela Procesador::DetectarArandela() const
{
Mat imgMsk;
float diametroExterior = 0;
float diametroInterior = 0;
Declaramos e inicializamos algunas variables auxiliares.
//Enmascara (imagen con mascara)
if(mImagen.empty() || mMascara.empty())
throw(2); //ERROR_MATRIZ_VACIA
Para enmascarar correctamente, ni la imagen ni la máscara deben estar vacía.
mImagen.copyTo(imgMsk, mMascara);
Esta línea realiza el enmascaramiento. Copia cada elemento de mImagen hacia imgMsk, pero
sólo aquellos donde la máscara mMascara tenga valor igual a 1.
cvtColor(imgMsk,imgMsk,CV_BGR2GRAY); //Convierte imgMsk a grayscale
threshold(imgMsk, imgMsk, mValorThreshold, 255, THRESH_BINARY);
//Erosiona y dilata 2 veces para disminuir el ruido
erode(imgMsk, imgMsk, Mat(), Point(-1, -1), 1, 1, 1);
erode(imgMsk, imgMsk, Mat(), Point(-1, -1), 1, 1, 1);
dilate(imgMsk, imgMsk, Mat(), Point(-1, -1), 1, 1, 1);
dilate(imgMsk, imgMsk, Mat(), Point(-1, -1), 1, 1, 1);
Luego el proceso es similar al visto anteriormente con una salvedad: Se realiza una doble
dilatación luego de una doble erosión. Esto actúa como un filtro al ruido binario.
//Contornos
vector<vector<Point> > contours; //Vector de Contornos
vector<Vec4i> hierarchy; //Vector de Jerarquía de Contornos
findContours(imgMsk, contours, hierarchy, CV_RETR_CCOMP,
CV_CHAIN_APPROX_SIMPLE, Point(0,0));
Esta última línea encuentra los contornos de imgMsk que son guardados en contours. Los
contornos son un conjunto de puntos continuos desarrollados a lo largo de un borde. Se utilizan
para la detección, reconocimiento y análisis de formas. Para una detección mas precisa, es mejor
utilizar una imagen binaria. La jerarquía de los contornos, hierarchy, indica la relación de los
contornos: En qué nivel están, si están dentro de un contorno o si tienen otros contornos dentro.
if(contours.size() == 0)
throw(5); //ERROR_CONTORNOS_NO_DETECTADOS
Si no se encuentra ningún contorno, no hay arandela a detectar.
6
vector<Moments> mu(contours.size());
//Vector de Momentos
Se define un vector de momentos de la imagen. El momento más utilizado es el área,
mu[i].m00. Con él analizaremos los contornos obtenidos. Esta función es análoga a
contourArea(contours[i]).
//Verifica todos los contornos
for(unsigned i = 0; i<contours.size(); i++)
{
//Si el contorno es significativo, calcula los valores
if(contourArea(contours[i])>mAreaMin &&
contourArea(contours[i])<mAreaMax)
{
A veces se detectan contornos de más, debido al ruido u otros factores no deseados. Con el
área del contorno, podemos determinar el rango de aquellos que nos interesan. La circunferencia
interna de una arandela tiene un área no menor al parámetro mAreaMin y la circunferencia de la
iluminación tiene un área algo mayor a mAreaMax. Los contornos cuyo área esté dentro de éstos
límites, es una arandela.
//Obtiene los Momentos
mu[i] = moments(contours[i], false);
//Calcula los Diámetros con el área (mu[i].m00)
if(2*sqrt(mu[i].m00/CV_PI) > diametroExterior)
{
diametroInterior = diametroExterior;
diametroExterior = round(2*sqrt(mu[i].m00/CV_PI));
}
else
{
diametroInterior = round(2*sqrt(mu[i].m00/CV_PI));
}
Como leemos los contornos progresivamente con un lazo for, determinamos cuál es el
contorno de la circunferencia exterior o interior.
}
} //Fin for
if((diametroInterior == 0) && (diametroExterior == 0))
throw(6); //ERROR_ARANDELA_NO_DETECTADA
Si las variables auxiliares no cambian de valor, significa que no se detectó ninguna arandela.
}
Arandela::s_CantidadArandelas += 1;
return (Arandela(diametroInterior, diametroExterior));
Al terminar la detección, aumenta el número de arandelas y devuelve el objeto de tipo
Arandela detectado.
Las imágenes siguientes fueron obtenidas para ilustrar el proceso de detección
7
Paso 1 – Imagen tomada
Paso 2 – Máscara
Paso 4 – Threshold
Paso 5 – Contornos
8
En el programa se implementa un pequeño menú para que el usuario pueda utilizar las
distintas funciones. Se realiza primero una carga de las imágenes tomadas en el directorio. Luego,
con las opciones, se cargan desde la cámara.
german@HPG42:~/Documentos/cpp/Arandelas/Debug$ ./Arandelas
+--------------------------------------------------+
|
DETECCION Y MEDICION DE ARANDELAS POR IMAGEN
|
|
Por German Emilio Mas
|
+--------------------------------------------------+
- Cargando Imagenes del directorio /home/german/IMG/A/
- Carga finalizada
Que desea hacer?
1)
Generar una nueva mascara
2)
Tomar una nueva imagen
3)
Ver los ultimos datos adquiridos
4)
Ver la lista de arandelas
5)
Ordenar lista según Diametro Interior
6)
Ordenar lista según Diametro Exterior
7)
Ordenar lista según Tiempo de Lectura
8)
Salir
Ingrese un numero:
Al ingresar el comando del menú, se realiza la acción correspondiente.
9
Apendice I – Código
main.cpp
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include "arandela.h"
#include "procesador.h"
#include <iostream>
#include <vector>
#include <algorithm>
using namespace cv;
using namespace std;
//Comparacion para ordenar por Diametro Interior
bool comparaDiametroInterior(const Arandela& a1, const Arandela& a2)
{
return (a1.GetDiametroInterior()<a2.GetDiametroInterior());
}
//Comparacion para ordenar por Diametro Exterior
bool comparaDiametroExterior(const Arandela& a1, const Arandela& a2)
{
return (a1.GetDiametroExterior()<a2.GetDiametroExterior());
}
//Comparacion para ordenar por Tiempo de Lectura
bool comparaTiempo(const Arandela& a1, const Arandela& a2)
{
return (a1.GetTiempo()<a2.GetTiempo());
}
int main(int argc, char** argv)
{
enum
{
ERROR_PARAMETRO_NEGATIVO,
ERROR_DIAMETRO_INTERIOR_MAYOR,
ERROR_MATRIZ_VACIA,
ERROR_CAMARA_NO_ENCONTRADA,
ERROR_AREA_LIMITE_INFERIOR_MAYOR,
ERROR_CONTORNOS_NO_DETECTADOS,
ERROR_ARANDELA_NO_DETECTADA,
ERROR_VECTOR_VACIO
};
Procesador proc; //Objeto que procesa las imagenes
stringstream conv; //Para conversion de int a string
string filename; //Imagen a precargar
int comandoMenu; //Indica la accion a realizar
vector<Arandela> vecArandelas; //Vector de Arandelas Leidas
10
vector<Arandela>::const_iterator cii; //Iterador del vector
cout
cout
cout
cout
cout
<<
<<
<<
<<
<<
"+--------------------------------------------------+"
"|
DETECCION Y MEDICION DE ARANDELAS POR IMAGEN
|"
"|
Por German Emilio Mas
|"
"+--------------------------------------------------+"
endl;
<<
<<
<<
<<
endl;
endl;
endl;
endl;
//PRECARGA DE IMAGENES POR ARCHIVO
try
{
proc.SetMascara("/home/german/IMG/BKG/1.jpg"); //Carga la Mascara
cout << "- Cargando Imagenes del directorio /home/german/IMG/A/" << endl;
for(unsigned i=1; i<56; i++)
{
conv << i; //Para convertir i en string
filename = "/home/german/IMG/A/"+conv.str()+".jpg"; //Nombre del
Archivo a Cargar
conv.str(""); //Limpia el stringstream
proc.SetImagen(filename); //Lee el archivo
vecArandelas.push_back(proc.DetectarArandela()); //Cargo Lista
}
cout << "- Carga finalizada" << endl;
//Menu
while(true)
{
cout << "\nQue desea hacer?" << endl;
cout << "1)\tGenerar una nueva mascara" << endl;
cout << "2)\tTomar una nueva imagen" << endl;
cout << "3)\tVer los ultimos datos adquiridos" << endl;
cout << "4)\tVer la lista de arandelas" << endl;
cout << "5)\tOrdenar lista según Diametro Interior" << endl;
cout << "6)\tOrdenar lista según Diametro Exterior" << endl;
cout << "7)\tOrdenar lista según Tiempo de Lectura" << endl;
cout << "8)\tSalir" << endl << endl;
cout << "Ingrese un numero: ";
cin >> comandoMenu;
switch(comandoMenu)
{
case 1:
proc.SetMascara(1);
break;
case 2:
proc.SetImagen(1);
vecArandelas.push_back(proc.DetectarArandela()); //Cargo Vector
break;
case 3:
if(vecArandelas.empty())
throw(7); //ERROR_VECTOR_VACIO
sort(vecArandelas.begin(), vecArandelas.end(), comparaTiempo);
cout << vecArandelas.back() << endl;
break;
11
case 4:
if(vecArandelas.empty())
throw(7); //ERROR_VECTOR_VACIO
cout << "D.Int\tD.Ext\th\tTiempo" << endl;
for(cii=vecArandelas.begin(); cii!=vecArandelas.end(); cii++)
cout << *cii;
break;
case 5:
sort(vecArandelas.begin(), vecArandelas.end(),
comparaDiametroInterior);
break;
case 6:
sort(vecArandelas.begin(), vecArandelas.end(),
comparaDiametroExterior);
break;
case 7:
sort(vecArandelas.begin(), vecArandelas.end(), comparaTiempo);
break;
case 8:
cout << "\n--- Fin del programa ---" << endl << endl;
return 0;
break;
default:
cout << "\n--- Comando no reconocido. Por favor, ingrese el
numero indicado. ---" << endl;
break;
} //Fin switch
} //Fin while
} //Fin try
catch(int e)
{
switch(e)
{
case ERROR_PARAMETRO_NEGATIVO:
cerr << "ERROR. Los parámetros de las arandelas no pueden ser
negativos." << endl;
break;
case ERROR_DIAMETRO_INTERIOR_MAYOR:
cerr << "ERROR. El diametro interior no puede ser mayor al
exterior." << endl;
break;
case ERROR_MATRIZ_VACIA:
cerr << "ERROR. La matriz no puede estar vacía." << endl;
break;
case ERROR_CAMARA_NO_ENCONTRADA:
cerr << "ERROR. No se encontró la cámara." << endl;
break;
case ERROR_AREA_LIMITE_INFERIOR_MAYOR:
12
cerr << "ERROR. El límite inferior del área no puede ser
mayor al límite superior." << endl;
break;
case ERROR_CONTORNOS_NO_DETECTADOS:
cerr << "ERROR. No se detectaron contornos." << endl;
break;
case ERROR_ARANDELA_NO_DETECTADA:
cerr << "ERROR. No se detectó ninguna arandela." << endl;
break;
case ERROR_VECTOR_VACIO:
cerr << "ERROR. El vector de Arandelas está vacío." << endl;
break;
} //Fin Switch
} //Fin Catch
return (0);
}
arandelas.h
#ifndef ARANDELA_H_
#define ARANDELA_H_
#include <iostream>
#include <time.h>
using namespace std;
class Arandela
{
private:
//Parametros
float mDiametroInterior;
float mDiametroExterior;
float mAltura;
float mArea;
float mVolumen;
time_t mTiempo;
public:
//Variables estáticas
static unsigned int s_CantidadArandelas;
//Constructores
Arandela(void);
Arandela(float, float); //Diametros Interior y Exterior
//Destructores
~Arandela(void);
//Gets
const float GetDiametroInterior(void) const;
const float GetDiametroExterior(void) const;
const float GetAltura(void) const;
const float GetArea(void) const;
const float GetVolumen(void) const;
const double GetTiempo(void) const;
//Sets
void SetDiametroInterior(float);
void SetDiametroExterior(float);
13
void SetAltura(float);
void SetTiempo(void);
//Sobrecarga de Operadores
Arandela& operator= (const Arandela&);
friend std::ostream& operator<<(std::ostream&, const Arandela&);
};
#endif // ARANDELA_H_
arandela.cpp
#include "arandela.h"
#include <iostream>
#include <time.h>
using namespace std;
//Variables estáticas
unsigned int Arandela::s_CantidadArandelas = 0;
//Constructores
Arandela::Arandela()
{
mDiametroInterior = 0;
mDiametroExterior = 0;
mAltura = 1;
mArea = 0;
mVolumen = 0;
time(&mTiempo);
}
Arandela::Arandela(float d1, float d2)
{
if((d1 < 0) || (d2 < 0))
throw(0); //ERROR_PARAMETRO_NEGATIVO
if(d1 >= d2)
throw(1); //ERROR_DIAMETRO_INTERIOR_MAYOR
mDiametroInterior = d1;
mDiametroExterior = d2;
mAltura = 1;
mArea = (d2*d2-d1*d1)*3.1415*0.25;
mVolumen = mArea*mAltura;
time(&mTiempo);
}
//Destructores
Arandela::~Arandela() { }
//Gets
const float Arandela::GetDiametroInterior() const {
return(mDiametroInterior); }
const float Arandela::GetDiametroExterior() const {
return(mDiametroExterior); }
const float Arandela::GetAltura() const { return(mAltura); }
const float Arandela::GetArea() const { return(mArea); }
const float Arandela::GetVolumen() const { return(mVolumen); }
const double Arandela::GetTiempo() const { return(mTiempo); }
14
//Sets
void Arandela::SetDiametroInterior(float d1)
{
if(d1 < 0)
throw(0); //ERROR_PARAMETRO_NEGATIVO
if(d1 >= mDiametroExterior)
throw(1); //ERROR_DIAMETRO_INTERIOR_MAYOR
mDiametroInterior = d1;
mArea = (mDiametroExterior*mDiametroExterior-d1*d1)*3.1415*0.25;
mVolumen = mArea*mAltura;
}
void Arandela::SetDiametroExterior(float d2)
{
if(d2 < 0)
throw(0); //ERROR_PARAMETRO_NEGATIVO
if(d2 <= mDiametroInterior)
throw(1); //ERROR_DIAMETRO_INTERIOR_MAYOR
mDiametroExterior = d2;
mArea = (d2*d2-mDiametroInterior*mDiametroInterior)*3.1415*0.25;
mVolumen = mArea*mAltura;
}
void Arandela::SetAltura(float h)
{
if(h < 0)
throw(0); //ERROR_PARAMETRO_NEGATIVO
mAltura = h;
mVolumen = mArea*h;
}
void Arandela::SetTiempo() { time(&mTiempo); }
//Sobrecarga de Operadores
Arandela& Arandela::operator= (const Arandela& ara)
{
mDiametroInterior = ara.mDiametroInterior;
mDiametroExterior = ara.mDiametroExterior;
mAltura = ara.mAltura;
mArea = ara.mArea;
mVolumen = ara.mVolumen;
mTiempo = ara.mTiempo;
return (*this);
}
std::ostream& operator<<(std::ostream& salida, const Arandela& ara)
{
salida << ara.mDiametroInterior << "\t"
<< ara.mDiametroExterior << "\t"
<< ara.mAltura << "\t"
<< ctime(&ara.mTiempo);
return(salida);
}
procesador.h
#ifndef PROCESADOR_H_
#define PROCESADOR_H_
15
#include <iostream>
#include "arandela.h"
using namespace std;
using namespace cv;
class Procesador
{
private:
//Parametros
Mat mImagen;
Mat mMascara;
int mValorThreshold;
int mAreaMin; //Limite Inferior para verificar Contornos
int mAreaMax; //Limite Superior para verificar Contornos
public:
//Constructores
Procesador(void);
Procesador(Mat, Mat);
//Destructores
~Procesador();
//Get
const Mat GetImagen(void) const;
const Mat GetMascara(void) const;
const int GetValorThreshold(void) const;
const int GetAreaMin(void) const;
const int GetAreaMax(void) const;
//Set
void SetImagen(string); //Via archivo
void SetImagen(int); //Via camara
void SetMascara(string); //Via archivo
void SetMascara(int); //Via camara
void SetValorThreshold(int);
void SetAreaMin(int);
void SetAreaMax(int);
//Otros
void MuestraImagen(string);
void MuestraMascara(string);
const Arandela DetectarArandela(void) const;
};
#endif // PROCESADOR_H_
procesador.cpp
#include
#include
#include
#include
#include
#include
"opencv2/highgui/highgui.hpp"
"opencv2/imgproc/imgproc.hpp"
"procesador.h"
"arandela.h"
<iostream>
<string>
using namespace std;
using namespace cv;
//Constructores
Procesador::Procesador()
16
{
}
mImagen = Mat::zeros(1,1,CV_8UC3);
mMascara = Mat::zeros(1,1,CV_8UC3);
mValorThreshold = 30;
mAreaMin = 120;
mAreaMax = 69000;
Procesador::Procesador(Mat img, Mat msk)
{
if(img.empty() || msk.empty())
throw(2); //ERROR_MATRIZ_VACIA
mImagen = img.clone();
mMascara = msk.clone();
mValorThreshold = 30;
mAreaMin = 120;
mAreaMax = 69000;
}
//Destructores
Procesador::~Procesador()
{
mImagen.release();
mMascara.release();
}
//Gets
const Mat
const Mat
const int
const int
const int
Procesador::GetImagen() const { return mImagen.clone(); }
Procesador::GetMascara() const { return mMascara.clone(); }
Procesador::GetValorThreshold() const { return mValorThreshold; }
Procesador::GetAreaMin() const { return mAreaMin; }
Procesador::GetAreaMax() const { return mAreaMax; }
//Sets
void Procesador::SetImagen(string filename)
{
mImagen = imread(filename);
if(mImagen.empty())
throw(2); //ERROR_MATRIZ_VACIA
}
void Procesador::SetImagen(int cam)
{
VideoCapture Camara(cam);
if(!Camara.isOpened())
throw(3); //ERROR_CAMARA_NO_ENCONTRADA
Camara.read(mImagen);
}
void Procesador::SetMascara(string filename)
{
Mat img;
img = imread(filename);
if(img.empty())
throw(2); //ERROR_MATRIZ_VACIA
cvtColor(img, mMascara, CV_BGR2GRAY);
threshold(mMascara, mMascara, mValorThreshold, 255, THRESH_BINARY);
erode(mMascara, mMascara, Mat(), Point(-1, -1), 1, 1, 1);
//Erosiona la
máscara
17
erode(mMascara, mMascara, Mat(), Point(-1, -1), 1, 1, 1);
máscara
}
//Erosiona la
void Procesador::SetMascara(int cam)
{
Mat img;
VideoCapture Camara(cam);
if(!Camara.isOpened())
throw(3); //ERROR_CAMARA_NO_ENCONTRADA
Camara.read(img);
cvtColor(img, mMascara, CV_BGR2GRAY);
threshold(mMascara, mMascara, mValorThreshold, 255, THRESH_BINARY);
erode(mMascara, mMascara, Mat(), Point(-1, -1), 1, 1, 1);
//Erosiona la
máscara
erode(mMascara, mMascara, Mat(), Point(-1, -1), 1, 1, 1);
//Erosiona la
máscara
}
void Procesador::SetValorThreshold(int valor)
{
if(valor < 0)
throw(0); //ERROR_PARAMETRO_NEGATIVO
mValorThreshold = valor;
}
void Procesador::SetAreaMin(int valor)
{
if(valor < 0)
throw(0); //ERROR_PARAMETRO_NEGATIVO
if(valor >= mAreaMax)
throw(4); //ERROR_AREA_LIMITE_INFERIOR_MAYOR
mAreaMin = valor;
}
void Procesador::SetAreaMax(int valor)
{
if(valor < 0)
throw(0); //ERROR_PARAMETRO_NEGATIVO
if(valor <= mAreaMin)
throw(4); //ERROR_AREA_LIMITE_INFERIOR_MAYOR
mAreaMax = valor;
}
//Otros
void Procesador::MuestraImagen(string ventana)
{
if(mImagen.empty())
throw(2); //ERROR_MATRIZ_VACIA
imshow(ventana, mImagen);
cout << "\n--- Pulse una tecla para continuar ---" << endl;
waitKey();
}
void Procesador::MuestraMascara(string ventana)
{
if(mMascara.empty())
throw(2); //ERROR_MATRIZ_VACIA
imshow(ventana, mMascara);
18
}
cout << "\n--- Pulse una tecla para continuar ---" << endl;
waitKey();
const Arandela Procesador::DetectarArandela() const
{
Mat imgMsk;
float diametroExterior = 0;
float diametroInterior = 0;
//Enmascara (imagen con mascara)
if(mImagen.empty() || mMascara.empty())
throw(2); //ERROR_MATRIZ_VACIA
mImagen.copyTo(imgMsk, mMascara);
cvtColor(imgMsk,imgMsk,CV_BGR2GRAY); //Convierte imgMsk a grayscale
threshold(imgMsk, imgMsk, mValorThreshold, 255, THRESH_BINARY);
//Threshold
//Erosiona y dilata 2 veces para disminuir el ruido
erode(imgMsk, imgMsk, Mat(), Point(-1, -1), 1, 1, 1);
erode(imgMsk, imgMsk, Mat(), Point(-1, -1), 1, 1, 1);
dilate(imgMsk, imgMsk, Mat(), Point(-1, -1), 1, 1, 1);
dilate(imgMsk, imgMsk, Mat(), Point(-1, -1), 1, 1, 1);
//Contornos
vector<vector<Point> > contours; //Vector de Contornos
vector<Vec4i> hierarchy; //Vector de Jerarquía de Contornos
findContours(imgMsk, contours, hierarchy, CV_RETR_CCOMP,
CV_CHAIN_APPROX_SIMPLE, Point(0,0));
if(contours.size() == 0)
throw(5); //ERROR_CONTORNOS_NO_DETECTADOS
vector<Moments> mu(contours.size());
//Vector de Momentos
//Verifica todos los contornos
for(unsigned i = 0; i<contours.size(); i++)
{
//Si el contorno es significativo, calcula los valores
if(contourArea(contours[i])>mAreaMin &&
contourArea(contours[i])<mAreaMax)
{
//Obtiene los Momentos
mu[i] = moments(contours[i], false);
//Calcula los Diámetros con el área (mu[i].m00)
if(2*sqrt(mu[i].m00/CV_PI) > diametroExterior)
{
diametroInterior = diametroExterior;
diametroExterior = round(2*sqrt(mu[i].m00/CV_PI));
}
else
{
diametroInterior = round(2*sqrt(mu[i].m00/CV_PI));
}
}
} //Fin for
if((diametroInterior == 0) && (diametroExterior == 0))
throw(6); //ERROR_ARANDELA_NO_DETECTADA
Arandela::s_CantidadArandelas += 1;
return (Arandela(diametroInterior, diametroExterior));
}
19
20
Descargar