¿Cómo configurar OpenCV con MinGW?

Bien, es momento de un poco de robótica. En los siguientes días estaré publicando sobre programación con Arduino, ¿Suena interesante?

Bien. Esta vez, y antes de adentrarnos al Machine Learning o cualquiera de esas cosas que tal vez no cubra en lo inmediato, configuraremos una simple cámara dentro de una aplicación escrita en C++.

Si no conoces OpenCV bueno… Es una biblioteca de procesamiento de imágenes súper rápida y compatible con una infinidad de sistemas operativos, es capaz de realizar, también, procesamiento inteligente de imágenes, éstas pueden venir de diferentes entradas, Arduino como será en estos casos, pero eso vendrá en un post futuro.

Esto es meramente demostrativo. En la práctica real NO suele utilizarse Dev-C++ para programar esta clase de programas pero si recién comienzas te ahorrarás mucho y podrás programar de inmediato. El setup recomendado es CMake + Visual Studio (del 2019 para acá).

Si, el título tal vez es clickbait, pero en el fondo Dev-C++ trabaja con MinGW, por lo que esto también te funcionará incluso si trabajas con herramientas como Cygwin.

Necesitarás las bibliotecas de OpenCV 3.4.1(si, también llamadas por vicio librerías)

Las puedes encontrar directamente desde este vínculo

O también las puedes clonar con git desde el repositorio de huihut

git clone -b OpenCV-3.4.1 https://github.com/huihut/OpenCV-MinGW-Build.git

Si ya instalaste Dev-C++ y creaste un proyecto lo demás es sencillo, tan sólo necesitas crear un proyecto y configurar un par de cosas.

Este será el contenido de main.cpp:

#include <stdio.h>
#include <iostream>
#include "opencv2/core/core.hpp"
#include "opencv2/features2d/features2d.hpp"
#include "opencv2/highgui/highgui.hpp"

int main(int argc, char* argv[])
{
    int method = 0;
    cv::VideoCapture cap(0); // open the video capture for reading
    if ( !cap.isOpened() ) // if not success, exit program
    {
        std::cout << "Cannot open the video file" << std::endl;
        return -1;
    }

    cv::namedWindow("MyVideo",CV_WINDOW_AUTOSIZE);
    while(1)
    {
        cv::Mat frame;
        bool bSuccess = cap.read(frame);
        cv::imshow("MyVideo", frame);
        if(cv::waitKey(30) == 27)
        {
            std::cout << "esc key is pressed by user" << std::endl;
            break;
        }
    }
    return 0;
}

Tendrás que agregar los Include Directories de la siguiente forma (menú Project->Project Options…)

Luego tendrás que linkear las bibliotecas correspondientes:

Para eso tendrás que referenciar presionando en el botón «Add library or object», navegar a la carpeta opencv\x64\mingw\lib (te recomiendo usar una ruta para opencv en C:\Binaries o C:\Dev, según tu gusto).

Finalmente copia todo el contenido de opencv\x64\mingw\bin a tu carpeta del proyecto Dev-C++, algo así:

Si haz seguido bien los pasos todo saldrá bien. Tan sólo selecciona la opción del menú Execute -> Compile y luego Execute -> Run.

Tendrás una ventana con la salida de imagen de tu cámara:

Y ya está.

¿Qué es glTF y cómo usarlo?

glTF es en 3D un formato llamado GL Transmission Format, y sirve nada más y nada menos que para representar modelos en tercera dimensión.

Para usar glTF tenemos a la disposición bibliotecas de soporte para C#, C++, JavaScript, Java y media docena más de lenguajes de programación. Esto es útil saberlo ya que mientras no uses un lenguaje exótico siempre podrás tener la ventaja de utilizar el formato.

¿Por qué utilizar glTF?
Bueno, en primera porque su formato está desarrollado por el grupo Khronos, quienes se encargan de crear los fierros detrás de OpenGL. Luego además de ser de código abierto, junto con todas sus herramientas de implementación, tiene la ventaja de no ser privativo y no se limita a un solo conjunto de programas de modelado (como 3d Studio Max o similares), de hecho, puedes usar Blender para trabajar con él.

No te preocupes si utilizas una implementación diferente a OpenGL para su procesamiento. De hecho, si utilizas el horrendo y sobrevalorado motor Unity, ya te contaré por qué Unity tiene esas características desde mi perspectiva en algún otro post. En fin, si utilizas Unity existen plugins que te permiten cargar glTF sin problema alguno. Y si utilizas MonoGame podrás utilizarlo también sin problema utilizando SharpGLTF.

En lo personal, y por qué no hablo de algo diferente a C#, bueno, pues es que en realidad hoy en día los videojuegos que se crean con lenguajes diferentes, y vaya que es monopólico, suelen ser más complicados de programar o más extensos, créeme, por experiencia te lo digo.

Otros lenguajes como Lua son predilectos para programar videojuegos, aunque no lo sepas, Lua es bastante utilizado en proyectos de código cerrado, también es bueno que le eches un vistazo a proyectos como Love2d mientras estás por allí.

glTF tiene además la ventaja de poseer transformaciones de huesos o «Skin transforms» que no son más que movimiento de los vértices generados por animaciones, también posee texturas, normales y materiales basados en PBR, súper cómodos y extremadamente lucidos en su calidad gráfica.

Si ya tienes como extraer los huesos de tus modelos 3D, será suficiente con pasárselos al vertex shader de OpenGL con algo como:

// BoneTransforms simplemente es una matriz en el shader
// Además hay que especificar el tamaño máximo de los bones
// Este valor puede estar rondando los 200 
#define SKINNED_EFFECT_MAX_BONES   144
uniform mat4 BoneTransforms[SKINNED_EFFECT_MAX_BONES];

for (var i = 0; i < _effect.BoneTransforms.Length; i++)
{
    shaderProgram.SetUniform(_uniformInputBones[i], 
    _effect.BoneTransforms[i]);
}

Luego viene la parte del shader y permíteme decirte que esto me tomó al menos un par de semanas de darme cuenta, ya verás por qué.

El shader, cuando se verifica que el modelo tenga skin es tan simple como:

if (IsSkinned == 1.0) 
{
    vec4 skinningPosition = vec4(0.0, 0.0, 0.0, 0.0);
    float boneCount = 4;

    for (int i = 0; i < boneCount; i++)
    {
    skinningPosition += (BoneTransforms[int(VertexBlendIndex[i])] * vec4(VertexPosition, 1.0)) * VertexWeight[i];
    }

    FragmentPosition = vec3(Model * vec4(VertexPosition, 1.0f));
    FragmentNormal = mat3(transpose(inverse(Model))) * VertexNormal;;
    FragmentTexCoord = VertexTexCoord;

    gl_Position = WorldViewProj * skinningPosition;
}

Mientras que en el fragment shader tenemos:

vec4 color = vec4(1.0, 1.0, 1.0, 1.0);

if (HasTexture == 1.0) 
{
    vec2 flipTC = vec2(FragmentTexCoord.x + 0.5 / TextureWidth, FragmentTexCoord.y - 0.5 / TextureHeight);
    color = texture2D(Texture, flipTC);// * FragmentDiffuse;
}

OutputColour = color

Ten en cuenta que le tienes que pasar los tamaños de las texturas ya que OpenGL hace el sampling de pixeles en medio de la textura y no en el origen superior o inferior izquierdo como en otras herramientas gráficas.

Y eso es todo, no necesitas nada adicional para poder visualizar un modelo glTF.

Más delante nos adentraremos en el loader completo para OpenGL y C#, pero si estás teniendo problemas para elegir el formato y no sabes al final como lo vas a cargar, esta información tal vez pueda serte útil y quizá glTF sea uno de los formatos que quieras tener en tu lista de formatos soportados.