es.davy.ai

Preguntas y respuestas de programación confiables

¿Tienes una pregunta?

Si tienes alguna pregunta, puedes hacerla a continuación o ingresar lo que estás buscando.

EmguCV y múltiples resultados con MatchTemplate

Estoy usando EmguCV para encontrar una plantilla en una imagen con el siguiente código:

CvInvoke.MatchTemplate(imagen, plantilla, resultado,
Emgu.CV.CvEnum.TemplateMatchingType.CcoeffNormed);

double minVal = Double.MaxValue;
double maxVal = Double.MinValue;

var minLoc = new Point();
var maxLoc = new Point();

CvInvoke.MinMaxLoc(resultado, ref minVal, ref maxVal, ref minLoc, ref maxLoc);

if (maxVal > umbral)
{
// Hacer algo
}

Esto funciona muy bien para obtener la mejor coincidencia.

Pero para algunos casos, sé que hay múltiples coincidencias y me gustaría ver todas las coincidencias para luego agruparlas con “groupRectangles”.

¿Cómo puedo cambiar mi código para iterar a través de todas las coincidencias?

  • Actualización

Lo resolví con el siguiente código, sin entender bien lo que está sucediendo, ensamblado a partir de otros pequeños fragmentos que encontré. Pero parece muy torpe, además, parece estar obteniendo muchos falsos positivos, lo que no parecía suceder cuando se usaba el código de una única coincidencia. Es decir, si una plantilla no está en la imagen, el código de una única coincidencia no lo encuentra, pero este código está encontrando muchas posibles coincidencias que están completamente equivocadas. Encuentra las coincidencias correctas cuando están en la pantalla, pero con muchas otras cosas equivocadas.

CvInvoke.MatchTemplate(imagen, plantilla, resultado,
Emgu.CV.CvEnum.TemplateMatchingType.CcoeffNormed);

// Normalizar la imagen de resultado en una matriz de flotantes (¿umbrales?)
Mat umbrales = new Mat();
CvInvoke.Normalize(resultado, umbrales, 0, 1, Emgu.CV.CvEnum.NormType.MinMax);

var rectangulos = new List<Rectangle>();
var tamaño = new Size(plantilla.Width, plantilla.Height);

// Convertirlo en una matriz multidimensional para poder iterar a través de ella
// (¿es realmente necesario, no hay algo nativo en EmguCV para esto?)
var datosUmbrales = umbrales.GetData();
for (int y = 0; y < datosUmbrales.GetLength(0); y++)
{
for (int x = 0; x < datosUmbrales.GetLength(1); x++)
{
var valor = (float)datosUmbrales.GetValue(y, x);

    if (valor &gt; umbral)
    {
        rectangulos.Add(new Rectangle(x, y, tamaño.Width, tamaño.Height));
    }
}

}

var rectangulosAgrupados = new VectorOfRect(rectangulos.ToArray());
CvInvoke.GroupRectangles(rectangulosAgrupados, 1);
var matrizRectangulosAgrupados = rectangulosAgrupados.ToArray();

foreach (var rectangulo in matrizRectangulosAgrupados)
{
respuesta.Add(new TemplateMatch
{
Posición = new Point(rectangulo.Left, rectangulo.Top),
Puntuación = -1, // Para varias coincidencias, ya no puedo encontrar la puntuación, porque fueron agrupadas
Tamaño = tamaño,
Válido = true,
});
}

Tags:  , ,

Answer

  1. Avatar for davy.ai

    Para iterar a través de todas las coincidencias, puedes utilizar la matriz de resultado normalizada de MatchTemplate e iterar a través de todos sus datos, comprobando los valores contra un umbral para determinar posibles coincidencias. Aquí tienes un código de ejemplo que debería funcionar:

    CvInvoke.MatchTemplate(image, template, result, Emgu.CV.CvEnum.TemplateMatchingType.CcoeffNormed);
    
    // Normaliza la imagen de resultado en una matriz de flotantes
    Mat normalizedResult = new Mat();
    CvInvoke.Normalize(result, normalizedResult, 0, 1, Emgu.CV.CvEnum.NormType.MinMax);
    
    var matches = new List<templatematch>();
    var size = new Size(template.Width, template.Height);
    
    // Itera a través del resultado normalizado y comprueba los valores contra un umbral
    var resultData = normalizedResult.GetData();
    for (int y = 0; y < resultData.GetLength(0); y++)
    {
        for (int x = 0; x < resultData.GetLength(1); x++)
        {
            var value = (float)resultData.GetValue(y, x);
    
            if (value > threshold)
            {
                var match = new TemplateMatch
                {
                    Position = new Point(x, y),
                    Score = value,
                    Size = size,
                    Valid = true
                };
                matches.Add(match);
            }
        }
    }
    
    // Agrupa las coincidencias si es necesario
    if (groupRectangles)
    {
        var rectangles = matches.Select(m => new Rectangle(m.Position, m.Size)).ToArray();
        var groupedRectangles = new VectorOfRect(rectangles);
        CvInvoke.GroupRectangles(groupedRectangles, 1);
        var groupedRectanglesArray = groupedRectangles.ToArray();
    
        // Convierte los rectángulos agrupados de nuevo en TemplateMatches
        matches.Clear();
        foreach (var rect in groupedRectanglesArray)
        {
            var match = new TemplateMatch
            {
                Position = new Point(rect.Left, rect.Top),
                Score = -1,  // Los rectángulos agrupados ya no tienen una puntuación
                Size = size,
                Valid = true
            };
            matches.Add(match);
        }
    }
    
    // Haz algo con las coincidencias
    foreach (var match in matches)
    {
        // ...
    }
    

    Este código normaliza la matriz de resultado en una matriz de flotantes, itera a través de todos sus datos y comprueba los valores contra un umbral. Si un valor está por encima del umbral, se crea una nueva TemplateMatch y se añade a una lista. Si groupRectangles es verdadero, las coincidencias se agrupan con GroupRectangles y se convierten de nuevo en objetos TemplateMatch. Finalmente, el código itera a través de todas las coincidencias (agrupadas o no) y hace algo con ellas.

Comments are closed.