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 > 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,
});
}
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: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. SigroupRectangles
es verdadero, las coincidencias se agrupan conGroupRectangles
y se convierten de nuevo en objetosTemplateMatch
. Finalmente, el código itera a través de todas las coincidencias (agrupadas o no) y hace algo con ellas.