Estamos estudiando las posibilidades de como nos puede ver nuestro robot. La idea inicial eran QRs pero la detección después de todas las optimizaciones a nivel de programación se quedaba en los 100 ms (no es mucho)
El problema principal es que a distancias superiores a 2 metros el QR debe ser muy grande, lo que lo convierte en poco práctico.
Después de un acalorado debate QR vs Geometrías y haciendo pruebas el sistema más eficiente es el de figuras geométricas. Dónde pasamos de los 100 ms a los 15ms y conseguimos distancias de varios metros.
Así que seguimos la máxima de la programación simplifica, vuelve a simplificar y finalmente lo vuelves a simplificar (mínimo 3 simplificaciones)
El código fuente se simplifica y el tiempo se mejora y la distancia aumenta , esto parece que será la opción elegida para que nuestro robot se comunique con el mundo.
#include <opencv2/opencv.hpp>
#include <iostream>
#include <chrono>
using namespace cv;
using namespace std;
using namespace std::chrono;
//Funcion para detectar las formas
void detectShapes(Mat& frame) {
Mat gray, blurred, thresholded;
// Convertir en gris
cvtColor(frame, gray, COLOR_BGR2GRAY);
// Hacer un blur para reducir el ruido
GaussianBlur(gray, blurred, Size(5, 5), 0);
// Aplicar la detección de las fronteras
Canny(blurred, thresholded, 50, 150);
vector<vector<Point>> contours;
vector<Vec4i> hierarchy;
// Buscar los contornos
findContours(thresholded, contours, hierarchy, RETR_TREE, CHAIN_APPROX_SIMPLE);
for (size_t i = 0; i < contours.size(); i++) {
// Aproximar los contornos contiguos
vector<Point> approx;
approxPolyDP(contours[i], approx, 0.02 * arcLength(contours[i], true), true);
Rect rect =boundingRect(approx);
// Saltar los tamaños pequeños y que no sean "convexos" posibles figuras cerradas
if (fabs(contourArea(contours[i])) < 1000 || !isContourConvex(approx))
continue; // Si no es muy ortodoxo usar continue pero aquí hoy lo permito.
if (approx.size() == 3) { //Comprobamos si es un triángulo
putText(frame, "Triangle", approx[0], FONT_HERSHEY_SIMPLEX, 0.5, Scalar(0, 255, 0), 2);
rectangle (frame, rect, Scalar (0,255,255),2);
} else if (approx.size() == 4) {
// Comprobar si es un rectángulo/cuadrado
Rect r = boundingRect(approx);
rectangle (frame, rect, Scalar (0,255,255),2);
float aspectRatio = (float)r.width / r.height;
if (aspectRatio >= 0.9 && aspectRatio <= 1.1) //si la relación entre los lados es pequeña es un cuadrado
putText(frame, "Square", approx[0], FONT_HERSHEY_SIMPLEX, 0.5, Scalar(255, 0, 0), 2);
else
putText(frame, "Rectangle", approx[0], FONT_HERSHEY_SIMPLEX, 0.5, Scalar(255, 0, 0), 2);
} else if (approx.size() > 4 && approx.size() <= 8) { //Si tenemos más de 4 es un poligono (dijimos simplificar)
putText(frame, "Polygon", approx[0], FONT_HERSHEY_SIMPLEX, 0.5, Scalar(0, 255, 255), 2);
rectangle (frame, rect, Scalar (0,255,255),2);
} else {
// El círculo es algo más complejo.
Point2f center;
float radius;
rectangle (frame, rect, Scalar (0,255,255),2);
minEnclosingCircle(contours[i], center, radius);
if (fabs(1 - ((float)radius / radius)) < 0.2)
putText(frame, "Circle", approx[0], FONT_HERSHEY_SIMPLEX, 0.5, Scalar(0, 0, 255), 2);
}
}
}
char buf[20];
int main() {
// Capturamos el vídeo.
VideoCapture cap(0);
// Comprobar si abre la web cam
if (!cap.isOpened()) {
cout << "Error: Could not open the camera" << endl;
return -1;
}
while (true) {
Mat frame;
// Capturar fotograma.
cap >> frame;
// Si no hay imagen cerramos
if (frame.empty()) {
cout << "Error: Could not read frame" << endl;
break;
}
auto start = high_resolution_clock::now();
// Detectar las formas.
detectShapes(frame);
auto stop = high_resolution_clock::now();
auto total =duration_cast<milliseconds>(stop -start);
// Mostrar el resultado
sprintf(buf,"%d",total.count());
putText(frame,buf,Point(10,10),FONT_HERSHEY_SIMPLEX,0.5,Scalar(255,0,255),2);
imshow("Detected Shapes", frame);
cout << total.count() << " ms" <<endl;
if (waitKey(30) == 'q') {
break;
}
}
cap.release();
destroyAllWindows();
return 0;
}