[C#] detectar imagen y obtener coordenadas

Iniciado por nevachana, 1 Febrero 2015, 19:47 PM

0 Miembros y 1 Visitante están viendo este tema.

nevachana

Hola ^^ me gustaría hacer un programa que detecte una imagen en la pantalla y obtener las coordenadas x e y pero no sé como compararlas y obtener las coordenadas.
Una ayudita? xD

Eleкtro

#1
Buenas

1) El título de un tema debe describir el problema.
Porfavor, lee las normas del subforo de programación.




Cita de: nevachana en  1 Febrero 2015, 19:47 PMUna ayudita? xD

¿Donde está el código que demuestra tus progresos?, en este lugar no hacemos trabajos, ayudamos a resolver dudas específicas o aportamos orientación.

Hay varias librerías, algunas gratis y otras de pago, que ofrecen algoritmos profesionales de técnicas ImageSearch o PixelSearch, como por ejemplo AForge.Net
http://www.aforgenet.com/framework/downloads.html

Si quieres desarrollar tu propio algoritmo de forma tradicional no te lo recomiendo, por el simple hecho de que jamás podrás optimizarlo hasta tal punto, pero de todas formas puedes ver un ejemplo escrito en VB.Net en la class PixelUtil que desarrollé:
Librería de Snippets !! (Compartan aquí sus snippets)

Este ejemplo utilizando Aforge, es una función de uso genérico que busca una imagen en otra imagen, y devuelve un objeto con las coordenadas y otra información relevante:

VB.Net:
Código (vbnet) [Seleccionar]
   ' Find Image
   ' ( By Elektro )
   '
   ' Usage Examples:
   '
   'Private Sub Test() Handles MyBase.Shown
   '
   '    ' A Desktop Screenshot, in 1920x1080 px. resolution.
   '    Dim desktopScreenshoot As New Bitmap("C:\Desktop.png")
   '
   '    ' A cutted piece of the screenshot, in 50x50 px. resolution.
   '    Dim partOfDesktopToFind As New Bitmap("C:\PartOfDesktop.png")
   '
   '    ' Find the part of the image in the desktop, with the specified similarity.
   '    For Each matching As AForge.Imaging.TemplateMatch In
   '
   '        FindImage(baseImage:=desktopScreenshoot, imageToFind:=partOfDesktopToFind, similarity:=80.5R) ' 80,5% similarity.
   '
   '        Dim sb As New System.Text.StringBuilder
   '
   '        sb.AppendFormat("Top-Left Corner Coordinates: {0}", matching.Rectangle.Location.ToString())
   '        sb.AppendLine()
   '        sb.AppendFormat("similarity Image Percentage: {0}%", (matching.similarity * 100.0F).ToString("00.00"))
   '
   '        MessageBox.Show(sb.ToString)
   '
   '    Next matching
   '
   'End Sub
   '
   ''' <summary>
   ''' Finds a part of an image inside other image and returns the top-left corner coordinates and it's similarity percent.
   ''' </summary>
   ''' <param name="baseImage">
   ''' Indicates the base image.
   ''' </param>
   ''' <param name="imageToFind">
   ''' Indicates the image to find in the base image.
   ''' </param>
   ''' <param name="similarity">
   ''' Indicates the similarity percentage to compare the images.
   ''' A value of '100' means identical image.
   ''' Note: High percentage values with big images could take several minutes to finish.
   ''' </param>
   ''' <returns>AForge.Imaging.TemplateMatch().</returns>
   Private Function FindImage(ByVal baseImage As Bitmap,
                              ByVal imageToFind As Bitmap,
                              ByVal similarity As Double) As AForge.Imaging.TemplateMatch()

       Dim currentSimilarity As Single

       ' Translate the readable similarity percent value to Single value.
       Select Case similarity

           Case Is < 0.1R, Is > 100.0R ' Value is out of range.
               Throw New Exception(String.Format("similarity value of '{0}' is out of range, range is from '0.1' to '100.0'",
                                                 CStr(similarity)))

           Case Is = 100.0R ' Identical image comparission.
               currentSimilarity = 1.0F

           Case Else ' Image comparission with specific similarity.
               currentSimilarity = Convert.ToSingle(similarity) / 100.0F

       End Select

       ' Set the similarity threshold to find all matching images with specified similarity.
       Dim tm As New AForge.Imaging.ExhaustiveTemplateMatching(currentSimilarity)

       ' Return all the found matching images,
       ' it contains the top-left corner coordinates of each one
       ' and matchings are sortered by it's similarity percent.
       Return tm.ProcessImage(baseImage, imageToFind)

   End Function


C#:
EDITO: El Snippet está traducido incorrectamente.

Código (csharp) [Seleccionar]

// Find Image
// ( By Elektro )

/// <summary>
/// Finds a part of an image inside other image and returns the top-left corner coordinates and it's similarity percent.
/// </summary>
/// <param name="baseImage">
/// Indicates the base image.
/// </param>
/// <param name="imageToFind">
/// Indicates the image to find in the base image.
/// </param>
/// <param name="similarity">
/// Indicates the similarity percentage to compare the images.
/// A value of '100' means identical image.
/// Note: High percentage values with big images could take several minutes to finish.
/// </param>
/// <returns>AForge.Imaging.TemplateMatch().</returns>
private AForge.Imaging.TemplateMatch[] FindImage(Bitmap baseImage, Bitmap imageToFind, double similarity)
{

float currentSimilarity = 0;

// Translate the readable similarity percent value to Single value.
switch (similarity) {

case 100.0: // Identical image comparission.
currentSimilarity = 1f;
break;

default: // Image comparission with specific similarity.
currentSimilarity = Convert.ToSingle(similarity) / 100f;
break;
}

// Set the similarity threshold to find all matching images with specified similarity.
AForge.Imaging.ExhaustiveTemplateMatching tm = new AForge.Imaging.ExhaustiveTemplateMatching(currentSimilarity);

// Return all the found matching images,
// it contains the top-left corner coordinates of each one
// and matchings are sortered by it's similarity percent.
return tm.ProcessImage(baseImage, imageToFind);

}

//=======================================================
//Service provided by Telerik (www.telerik.com)
//=======================================================


Saludos.








nevachana

#2
Cita de: Eleкtro en  1 Febrero 2015, 20:06 PM
Buenas

1) El título de un tema debe describir el problema.
Porfavor, lee las normas del subforo de programación.




¿Donde está el código que demuestra tus progresos?, en este lugar no hacemos trabajos, ayudamos a resolver dudas específicas o aportamos orientación.

Hay varias librerías, algunas gratis y otras de pago, que ofrecen algoritmos profesionales de técnicas ImageSearch o PixelSearch, como por ejemplo AForge.Net
http://www.aforgenet.com/framework/downloads.html

Si quieres desarrollar tu propio algoritmo de forma tradicional no te lo recomiendo, por el simple hecho de que jamás podrás optimizarlo hasta tal punto, pero de todas formas puedes ver un ejemplo escrito en VB.Net en la class PixelUtil que desarrollé:
Librería de Snippets !! (Compartan aquí sus snippets)

Este ejemplo utilizando Aforge, es una función de uso genérico que busca una imagen en otra imagen, y devuelve un objeto con las coordenadas y otra información relevante:

VB.Net:
Código (vbnet) [Seleccionar]
   ' Find Image
   ' ( By Elektro )
   '
   ' Usage Examples:
   '
   'Private Sub Test() Handles MyBase.Shown
   '
   '    ' A Desktop Screenshot, in 1920x1080 px. resolution.
   '    Dim desktopScreenshoot As New Bitmap("C:\Desktop.png")
   '
   '    ' A cutted piece of the screenshot, in 50x50 px. resolution.
   '    Dim partOfDesktopToFind As New Bitmap("C:\PartOfDesktop.png")
   '
   '    ' Find the part of the image in the desktop, with the specified similarity.
   '    For Each matching As AForge.Imaging.TemplateMatch In
   '
   '        FindImage(baseImage:=desktopScreenshoot, imageToFind:=partOfDesktopToFind, similarity:=80.5R) ' 80,5% similarity.
   '
   '        Dim sb As New System.Text.StringBuilder
   '
   '        sb.AppendFormat("Top-Left Corner Coordinates: {0}", matching.Rectangle.Location.ToString())
   '        sb.AppendLine()
   '        sb.AppendFormat("similarity Image Percentage: {0}%", (matching.similarity * 100.0F).ToString("00.00"))
   '
   '        MessageBox.Show(sb.ToString)
   '
   '    Next matching
   '
   'End Sub
   '
   ''' <summary>
   ''' Finds a part of an image inside other image and returns the top-left corner coordinates and it's similarity percent.
   ''' </summary>
   ''' <param name="baseImage">
   ''' Indicates the base image.
   ''' </param>
   ''' <param name="imageToFind">
   ''' Indicates the image to find in the base image.
   ''' </param>
   ''' <param name="similarity">
   ''' Indicates the similarity percentage to compare the images.
   ''' A value of '100' means identical image.
   ''' Note: High percentage values with big images could take several minutes to finish.
   ''' </param>
   ''' <returns>AForge.Imaging.TemplateMatch().</returns>
   Private Function FindImage(ByVal baseImage As Bitmap,
                              ByVal imageToFind As Bitmap,
                              ByVal similarity As Double) As AForge.Imaging.TemplateMatch()

       Dim currentSimilarity As Single

       ' Translate the readable similarity percent value to Single value.
       Select Case similarity

           Case Is < 0.1R, Is > 100.0R ' Value is out of range.
               Throw New Exception(String.Format("similarity value of '{0}' is out of range, range is from '0.1' to '100.0'",
                                                 CStr(similarity)))

           Case Is = 100.0R ' Identical image comparission.
               currentSimilarity = 1.0F

           Case Else ' Image comparission with specific similarity.
               currentSimilarity = Convert.ToSingle(similarity) / 100.0F

       End Select

       ' Set the similarity threshold to find all matching images with specified similarity.
       Dim tm As New AForge.Imaging.ExhaustiveTemplateMatching(currentSimilarity)

       ' Return all the found matching images,
       ' it contains the top-left corner coordinates of each one
       ' and matchings are sortered by it's similarity percent.
       Return tm.ProcessImage(baseImage, imageToFind)

   End Function


C#:
EDITO: El Snippet está traducido incorrectamente.

Código (csharp) [Seleccionar]

// Find Image
// ( By Elektro )

/// <summary>
/// Finds a part of an image inside other image and returns the top-left corner coordinates and it's similarity percent.
/// </summary>
/// <param name="baseImage">
/// Indicates the base image.
/// </param>
/// <param name="imageToFind">
/// Indicates the image to find in the base image.
/// </param>
/// <param name="similarity">
/// Indicates the similarity percentage to compare the images.
/// A value of '100' means identical image.
/// Note: High percentage values with big images could take several minutes to finish.
/// </param>
/// <returns>AForge.Imaging.TemplateMatch().</returns>
private AForge.Imaging.TemplateMatch[] FindImage(Bitmap baseImage, Bitmap imageToFind, double similarity)
{

float currentSimilarity = 0;

// Translate the readable similarity percent value to Single value.
switch (similarity) {

case 100.0: // Identical image comparission.
currentSimilarity = 1f;
break;

default: // Image comparission with specific similarity.
currentSimilarity = Convert.ToSingle(similarity) / 100f;
break;
}

// Set the similarity threshold to find all matching images with specified similarity.
AForge.Imaging.ExhaustiveTemplateMatching tm = new AForge.Imaging.ExhaustiveTemplateMatching(currentSimilarity);

// Return all the found matching images,
// it contains the top-left corner coordinates of each one
// and matchings are sortered by it's similarity percent.
return tm.ProcessImage(baseImage, imageToFind);

}

//=======================================================
//Service provided by Telerik (www.telerik.com)
//=======================================================


Saludos.
La prueba:
Código (csharp) [Seleccionar]
   public static void Start()
       {
           while (true)
           {
               for (int i = 0; i < totalCoords; i++)
               {
                   a.MouseClick("Left", X[i], Y[i], 1, 1);
                   Thread.Sleep(seconds * 1000);
                   a.MouseClick("Left", CloseX, CloseY, 1, 1);
                   Thread.Sleep(1900);
                   if (i == totalCoords - 1)
                       i = 0;
               }
           }
       }

       private void Form1_Load(object sender, EventArgs e)
       {
           this.KeyPreview = true;          
       }

       private void button2_Click(object sender, EventArgs e)
       {
           this.KeyDown += new KeyEventHandler(Form1_KeyDown);
           textBox4.Text = "";
       }
       void Form1_KeyDown(object sender, KeyEventArgs e)
       {
           if (e.KeyCode.ToString() == "F3")
           {
               textBox4.Text = textBox4.Text + Cursor.Position.X.ToString()+"@"+Cursor.Position.Y + "\r\n";
           }
       }
       void cerrar(object sender, KeyEventArgs e)
       {
           if (e.KeyCode.ToString() == "F1")
           {
               textBox1.Text = Cursor.Position.X.ToString();
               textBox2.Text = Cursor.Position.Y.ToString();
           }
       }

       private void button3_Click(object sender, EventArgs e)
       {
           this.KeyDown += new KeyEventHandler(cerrar);
       }
   }
}

Ya sé que no es limpio es mi primera aplicación en autoit,pero la idea que me has dado serviría para buscarlo por toda la pantalla?

Eleкtro

#3
Cita de: nevachana en  2 Febrero 2015, 20:14 PMYa sé que no es limpio es mi primera aplicación en autoit
No te preocupes, aquí nadie es mejor que nadie y todos vienen a aprender.

Cita de: nevachana en  2 Febrero 2015, 20:14 PMpero la idea que me has dado serviría para buscarlo por toda la pantalla?
Hombre, por supuesto, solo debes obtener la imagen de la zona donde quieras buscar (puede ser una captura de la pantalla completa, claro), y si la imagen es demasiado grande redimensionar la imagen a unas dimensiones que te permitan realizar la búsqueda y comparación de imagen de una forma eficiente, rápida, pero sin aumentar el margen de falsos positivos, buscando esa relación (ej: 100x100 píxeles quizás, depende).

EDITO:
No me ha quedado claro que camino vas a tomar para llevarlo a cabo, pero bueno vuelvo a aconsejarte utilizar la librería de AForge.Net, aunque he visto algoritmos de pago mucho más rápidos que obtienen los mismos o mejores resultados, pero AForge es excelente para ser gratis.

Realizar un algoritmo de ese estilo es relativamente fácil, de forma básica, pero es un tema muy delicado ya que procesar/comparar una imagen, una imagen que sea grande, es una operación muy lenta (y si usas metodologías anticuadas lo es aun mucho más), si no optimizas tu propio algoritmo hasta el punto que puede llegar a optimizarlo un grupo de desarrolladores profesionales en el tema que están vendiendo un producto de ese estilo entonces te puede quedar un algoritmo muy lento o incompetente en otros sentidos, y yo no soy ningún gurú en la optimización de este tema, pero llegué a implementar funcionalidades GPU para aumentar la velocidad de procesamiento de imagen y aun así no me convenció mucho el resultado que obtuve, sigo prefiriendo la estabilidad de AForge.

Saludos








nevachana

Cita de: Eleкtro en  2 Febrero 2015, 20:36 PM
No te preocupes, aquí nadie es mejor que nadie y todos vienen a aprender.
Hombre, por supuesto, solo debes obtener la imagen de la zona donde quieras buscar (puede ser una captura de la pantalla completa, claro), y si la imagen es demasiado grande redimensionar la imagen a unas dimensiones que te permitan realizar la búsqueda y comparación de imagen de una forma eficiente, rápida, pero sin aumentar el margen de falsos positivos, buscando esa relación (ej: 100x100 píxeles quizás, depende).

EDITO:
No me ha quedado claro que camino vas a tomar para llevarlo a cabo, pero bueno vuelvo a aconsejarte utilizar la librería de AForge.Net, aunque he visto algoritmos de pago mucho más rápidos que obtienen los mismos o mejores resultados, pero AForge es excelente para ser gratis.

Realizar un algoritmo de ese estilo es relativamente fácil, de forma básica, pero es un tema muy delicado ya que procesar/comparar una imagen, una imagen que sea grande, es una operación muy lenta (y si usas metodologías anticuadas lo es aun mucho más), si no optimizas tu propio algoritmo hasta el punto que puede llegar a optimizarlo un grupo de desarrolladores profesionales en el tema que están vendiendo un producto de ese estilo entonces te puede quedar un algoritmo muy lento o incompetente en otros sentidos, y yo no soy ningún gurú en la optimización de este tema, pero llegué a implementar funcionalidades GPU para aumentar la velocidad de procesamiento de imagen y aun así no me convenció mucho el resultado que obtuve, sigo prefiriendo la estabilidad de AForge.

Saludos
ahora le hecho un vistazo ^^,y bueno no te dije lo que quería cambiar,a sí que te lo dejo ahora :P , estoy usando una lista de coordenadas.
a.MouseClick("Left", X, Y, 1, 1);
y bueno,resulta que donde quiero pulsar aparece de forma aleatoria en la pantalla,por lo que rara vez clicaba,y por eso necesitaba lo de las imágenes ^^

nevachana

Hola ^^ sabrías qué falla aquí?
Código (csharp) [Seleccionar]
    Bitmap bmpScreenshot = new Bitmap(Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height, PixelFormat.Format32bppArgb);
                Graphics gfxScreenshot = Graphics.FromImage(bmpScreenshot);
                gfxScreenshot.CopyFromScreen(Screen.PrimaryScreen.Bounds.X, Screen.PrimaryScreen.Bounds.Y, 0, 0, Screen.PrimaryScreen.Bounds.Size, CopyPixelOperation.SourceCopy);
                System.Drawing.Bitmap sourceImage = new Bitmap (@"C:\Users\PC\Documents\Visual Studio 2012\Projects\NeoBux\NeoBux\obj\Debug\Ball.bmp");           
                System.Drawing.Bitmap template = new Bitmap(bmpScreenshot);
                ExhaustiveTemplateMatching tm = new ExhaustiveTemplateMatching(0.921f);
                TemplateMatch[] matchings = tm.ProcessImage(sourceImage, template);//fallo aquí

Eleкtro

#6
Cita de: nevachana en  6 Febrero 2015, 16:59 PMsabrías qué falla aquí?

Porfavor, cuando formules una pregunta sobre un error expecífico, asegúrate de MOSTRAR el mensaje de la excepción.

Muestra la información necesaria para intentar ayudarte, ese método tiene muchos overloads y puede dar muchos errores distintos, que podrías solucioanrlo viendo la documentación oficial.

De todas formas me imagino que estás teniendo una excepción del tipo UnsuportedImageFormatException, ya que para el objeto template estás utilizando el constructor del Bitmap que por defecto asigna un canal ARGB de 32bpp, y aparte de eso estas asignando manualmente en el otro bitmap el mismo canal y profundidad de bits, y ese tipo de formato no está soportado por el método en cuestión, simplemente usa el formato de pixel a Format24bppRgb.

Saludos








GonzaFz

Yo una vez hice (copie en parte) un algoritmo ImageSearch bastante eficiente como para una imagen 1024 x 768.
El algoritmo se basa en ir pixel por pixel comparando el primer pixel de la imagen X (imagen a buscar) con los pixeles de la imagen B (imagen base).
Por primera vez yo lo había hecho con GetPixel (funcion de la libreria grafica de C#) pero era muy lento. Buscando en internet encontré que a través de punteros podes acceder directamente a la memoria donde se encuentran almacenados cada pixel, haciendo que la velocidad aumente considerablemente.

Acá te dejo un link con la información necesaria:
http://bobpowell.net/lockingbits.aspx

Nunca probé AForge pero el metodo que yo te digo lo podes comprobar acá:
[youtube=640,360]https://www.youtube.com/watch?v=rF2G87b6qiQ[/youtube]

Quizas te parezca algo lento pero eso se debe a que la imagen la obtiene de un WebBrowser, si la sacas de algún archivo estático y comparás, va a ser muy veloz.

Obviamente se podria optimizar mucho mas, haciendo por ejemplo, lo que dice Elektro, dividir en partes de 100x100.

Saludos

nevachana

Cita de: Eleкtro en  7 Febrero 2015, 14:56 PM
Porfavor, cuando formules una pregunta sobre un error expecífico, asegúrate de MOSTRAR el mensaje de la excepción.

Muestra la información necesaria para intentar ayudarte, ese método tiene muchos overloads y puede dar muchos errores distintos, que podrías solucioanrlo viendo la documentación oficial.

De todas formas me imagino que estás teniendo una excepción del tipo UnsuportedImageFormatException, ya que para el objeto template estás utilizando el constructor del Bitmap que por defecto asigna un canal ARGB de 32bpp, y aparte de eso estas asignando manualmente en el otro bitmap el mismo canal y profundidad de bits, y ese tipo de formato no está soportado por el método en cuestión, simplemente usa el formato de pixel a Format24bppRgb.

Saludos
Me da esta exepción:
'AForge.Imaging.UnsupportedImageFormatException' en AForge.Imaging.dll

Información adicional: Unsupported pixel format of the source or template image.

Código (csharp) [Seleccionar]
  Bitmap bmpScreenshot = new Bitmap(Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height, PixelFormat.Format24bppRgb);
                Graphics gfxScreenshot = Graphics.FromImage(bmpScreenshot);
                gfxScreenshot.CopyFromScreen(Screen.PrimaryScreen.Bounds.X, Screen.PrimaryScreen.Bounds.Y, 0, 0, Screen.PrimaryScreen.Bounds.Size, CopyPixelOperation.SourceCopy);
                System.Drawing.Bitmap sourceImage = new Bitmap (@"C:\Users\PC\Documents\Visual Studio 2012\Projects\NeoBux\NeoBux\obj\Debug\Ball.bmp");           
                System.Drawing.Bitmap template = new Bitmap(bmpScreenshot);
                ExhaustiveTemplateMatching tm = new ExhaustiveTemplateMatching(0.921f);
                TemplateMatch[] matchings = tm.ProcessImage(sourceImage, template);//fallo en esta línea