Comparer contenus d'images en C#

Signaler
Messages postés
8
Date d'inscription
vendredi 22 mai 2009
Statut
Membre
Dernière intervention
29 mai 2009
-
Messages postés
8
Date d'inscription
vendredi 22 mai 2009
Statut
Membre
Dernière intervention
29 mai 2009
-
Bonjour à tous,
je ne sais pas si j'ai placé ce topic dans le bon thème mais j'ai un petit problème sur lequel j'ai planché toute la journée sans trouver de solution.

Mon objectif serait de tester si une image contenue dans un fichier .bmp est visible à l'écran. J'ai donc eu l'idée de faire une capture d'écran et d'instancier cette image dans un objet Bitmap qui sera le modèle. Ayant le chemin de l'image à tester, je l'ai aussi instanciée dans un objet Bitmap. J'ai enfin converti ces deux objets en tableaux de bytes pour enfin les convetir en string.
J'utilise alors la fonction string.contains(string) pour savoir si l'image de la capture d'écran contient l'image à comparer. Seulement à chaque fois que je teste il me dit qu'il ne la trouve pas alors que l'image apparaît bien à l'écran ...

Voici le code que j'ai utilisé, si quelqu'un sait comment procéder ou s'il existe une technique beaucoup plus facile, je lui en serais très reconnaissant.

Code :
                    //Bitmap correspondant à la capture d'écran
                    Bitmap screen;
                    //Bitmap correspondant à l'image à tester
                    Bitmap image;
                  
                    //Ici, param1 est le string comportant l'adresse de l'image à tester
                    image = new Bitmap(param1);

                    // simuler le PrintScreen enrichi
                    SendKeys.SendWait("%{PRTSC}");
                    // récupérer l'image obtenue dans le Presse-Papier
                    screen = ((Bitmap)(Clipboard.GetDataObject().GetData("Bitmap")));

                    //On déclare les tableaux de bytes
                    Byte[] bytScreen = { new byte() };
                    Byte[] bytImage = { new byte() };
                  
                    //On instancie l'image de la capture dans le tableau de bytes
                    try
                    {
                        MemoryStream mstScreen = new MemoryStream();
                        screen.Save(mstScreen, System.Drawing.Imaging.ImageFormat.Jpeg);
                        bytScreen = mstScreen.GetBuffer();
                    }
                    catch (Exception)
                    {
                        MessageBox.Show("Error");
                    }

                    //On instancie l'image à tester dans le tableau de bytes
                    try
                    {
                        MemoryStream mstImage = new MemoryStream();
                        image.Save(mstImage, System.Drawing.Imaging.ImageFormat.Jpeg);
                        bytImage = mstImage.GetBuffer();
                    }
                    catch (Exception)
                    {
                        MessageBox.Show("Error");
                    }
                   
                    string screenString = Convert.ToBase64String(bytScreen);
                    string imageString  = Convert.ToBase64String(bytImage);

                    //On finit par tester si l'image à tester est contenue dans la capture d'écran (ce qui n'est jamais le cas)
                    if (all.Contains(current))
                        MessageBox.Show("found");
                    else
                        MessageBox.Show("not found");

Merci beaucoup
Don't geek so much

13 réponses

Messages postés
3246
Date d'inscription
lundi 25 avril 2005
Statut
Modérateur
Dernière intervention
27 octobre 2012
37
Salut,

Tu ne dois pas forcer un PrintScreen et éffacer le clipboard de l utilisateur, on ne fait jamais ça.. regarde les fonctions Win32 du genre GetDesktopWindow, PrintWindow etc..

Pour comparer des images tu dois les locker en mémoire avec la méthode LockBits et comparer les pixels entre eux, surtout pas de conversion en string !
Messages postés
8
Date d'inscription
vendredi 22 mai 2009
Statut
Membre
Dernière intervention
29 mai 2009

Bonsoir et tout d'abord merci pour cette réponse rapide.
C'est vrai que j'ai pensé à l'histoire du clipboard, ce serait sale mais je voulais stoquer ce qu'il y avait avant et le remettre après mais c'est pas optimisé du tout.
Sinon concernant les fonctions Win32 je vais aller voir ça merci. Maintenant pour la méthode LockBits je ne sais pas du tout comment ça marche, est-ce que vous avez un petit exemple rapide du fonctionnement? Ca m'aiderait assez.
Et Merci encore!

Don't geek so much
Messages postés
834
Date d'inscription
samedi 15 novembre 2008
Statut
Membre
Dernière intervention
14 janvier 2017
30
Bonjour,

Tu peux utiliser la methode GetPixel(), qui lock l'image en mémoire.

ex:
Bitmap image1=

new Bitmap( pictureBox1.Image);
Bitmap image2=
new Bitmap( pictureBox2.Image);

for (
int i=0; i < image1.Width; i++)

   for (
int j=0; j < image1.Height; j++)
   {

      if (image1.GetPixel(i,j) != image2.GetPixel(i,j))
      {
         MessageBox.Show("not found");

         return;
      }
   }
MessageBox.Show("found");

C# is amazing, enjoy it!
Messages postés
8
Date d'inscription
vendredi 22 mai 2009
Statut
Membre
Dernière intervention
29 mai 2009

Ah d'accord donc ce serait plus propre d'utiliser cette technique pour comparer deux images. Merci beaucoup Robert33.
Le problème pour moi est que je dois tester si une image apparaît à l'écran, ce qui veut dire qu'une image est contenue dans la capture d'écran: ce ne serait donc plus une comparaison littéralement parlant mais vérifier si une image en contient une autre. C'est pour cela que j'ai voulu utiliser la fonction Contains de string. Si quelqu'un a déjà utilisé un traitement similaire, cela m'aiderait beaucoup.

Don't geek so much
Messages postés
834
Date d'inscription
samedi 15 novembre 2008
Statut
Membre
Dernière intervention
14 janvier 2017
30
Bonsoir,

En effet c'est plus hardu, impossible de le faire par cette méthode.
Même en sérialisant les tableaux de bits de chaque image on ne pourrait arriver a savoir de maniere directe.

il faudrait donc serialiser les lignes de pixels une par une, puis regarder si les lignes de l'image sont contenues dans le fond d'écrann en verifiant que les lignes soient bien alignées pour former une image coherente.

C# is amazing, enjoy it!
Messages postés
8
Date d'inscription
vendredi 22 mai 2009
Statut
Membre
Dernière intervention
29 mai 2009

Merci pour la réponse.

D'accord quand tu dis sérialiser c'est-à-dire créer une liste de lignes de pixels? Par contre je n'ai pas compris quand tu as dit "en vérifiant que les lignes soient bien alignées pour former une image cohérente".

Don't geek so much
Messages postés
834
Date d'inscription
samedi 15 novembre 2008
Statut
Membre
Dernière intervention
14 janvier 2017
30
re;
oui, il faut creer une liste de ligne de pixels,
mais attention aux couleurs transparentes de l'image d'origine, car une fois posées sur le fond d'écran elles seront changées.

En fait il faudrait verifier que la position du début de chaque ligne est bien allignée avec celle d'avant,
mais en fait la probabilité que toutes les lignes appartiennent à l'image de fond sans être allignées est tellement faible qu'on peut la considerrer nulle.

C# is amazing, enjoy it!
Messages postés
8
Date d'inscription
vendredi 22 mai 2009
Statut
Membre
Dernière intervention
29 mai 2009

Re


en ce qui concerne les couleurs transparentes, je ne pense pas qu'il y en ait parce que ce serait la plupart du temps des images provenant d'un site web (en fait l'utilisateur utilise ma fonction pour attendre qu'une image apparaisse à l'écran, dès qu'elle apparaît, le programme continue).
Maintenant je ne sais pas comment faire pour faire la comparaison ligne par ligne parce que l'image de départ est plus petite que celle du PrintScreen : il y aura donc des problèmes de tailles.
Je pense qu'il faudrait se déplacer pixel par pixel dans l'image du PrintScreen et dès qu'on rencontre le 1er pixel de l'image on teste le pixel suivant jusqu-à la fin faisant attention de revenir à la ligne quand il le faut (et c'est là que je ne sais pas comment faire)

Don't geek so much
Messages postés
834
Date d'inscription
samedi 15 novembre 2008
Statut
Membre
Dernière intervention
14 janvier 2017
30
Bonjour
tu peux "sérialiser" les lignes de pixels sous forme de chaines de caracteres 'suite de code hexadécimal), puis utiliser la methode string.Contains().
j'ai essayer et ça marche, le plus long etant la sérialisation de l'image de l'écran, il faut utiliser du code non managé pour avoir une bonne performance.

C# is amazing, enjoy it!
Messages postés
8
Date d'inscription
vendredi 22 mai 2009
Statut
Membre
Dernière intervention
29 mai 2009

Bonjour et merci pour ta réponse.
Si cela marche de cette façon, je serais soulagé mais je ne comprends pas comment une chaine avec des retours à la ligne peut être contenue dans une autre (étant donné que l'image de départ est plus grande). Mais si tu dis que ça marche je te crois. Par contre, je n'ai pas appris explicitement en cours comment gérer une serialisation en code non managé et comme je suis en stage, je n'ai pas de prof pour m'informer là-dessus. Comment devrais-je construire mon objet liste?
Merci encore
Don't geek so much
Messages postés
834
Date d'inscription
samedi 15 novembre 2008
Statut
Membre
Dernière intervention
14 janvier 2017
30
Bonsoir,

Essaye ça, c'est un peut lent, mais ça marche

Bitmap image1 = newBitmap(pictureBox1.Image);
SendKeys.SendWait("%{PRTSC}");
Bitmap screen = ((Bitmap)(Clipboard.GetDataObject().GetData("Bitmap")));
//définition de 2 listes qui contiendront les lignes de pixels
string[] Image1Pixels = newstring[image1.Height];
string[] ScreenPixels = newstring[screen.Height];
//sérialisation de l'image à  comparer
// ligne par ligne stockée dans le tableau
for (int i = 0; i < image1.Width; i++)
   for (int j = 0; j < image1.Height; j++)
   {
      Image1Pixels[j] += image1.GetPixel(i, j).ToArgb().ToString("X") + "-";
   }
//Idem pour l'écran de fond
//TROP LENT, il faudrait utiliser du code non managé !!!
for (int i = 0; i < screen.Width; i++)
   for (int j = 0; j < screen.Height; j++)
   {
      ScreenPixels[j] += screen.GetPixel(i, j).ToArgb().ToString("X") + "-";
   }
// bon reste plus qu'à trouver la petite image dans la grande
// on parcour les lignes de l'image de fond
for (int i = 0; i < screen.Height; i++)
{
   //Est-ce que la ligne contine la premiere ligne de notre petite image ?
   if (ScreenPixels[i].Contains(Image1Pixels[0]))
   {
      // oui, mais est-ce un hasard ?
      bool found = true;
      // on regarde si les autre lignes de la petite
      // sont bien dans les lignes suivante de la grande
      for (int j = 0; j < image1.Height; j++)
      {
         if (!ScreenPixels[i + j].Contains(Image1Pixels[j]))
         {
            // mince, non un ligne manquante
            // bon on va regarder les autres
            found = false;
            break;
         }
      }
      if (found)
      {
         // YES!!
         MessageBox.Show("found");
         return;
      }
   }
}
// bof ...
MessageBox.Show("not found");

C# is amazing, enjoy it!
Messages postés
8
Date d'inscription
vendredi 22 mai 2009
Statut
Membre
Dernière intervention
29 mai 2009

Merci beaucoup Robert33, dès demain matin je reprends ce code pour l'essayer et si ça marche chez moi(et y a pas de raison pour que ça ne se fasse pas) alors là je dirai bravo!
En tout cas j'aime les commentaires, on a même l'impression d'être dans un film d'action tellement ça bouge

Don't geek so much
Messages postés
8
Date d'inscription
vendredi 22 mai 2009
Statut
Membre
Dernière intervention
29 mai 2009

Waw ça marche! par contre le problème comme tu l'as dit vient de la lenteur de l'exécution : chez moi ça dure environ 30 secondes de traitement (surtout que je vais faire ce traitement dans un while vu qu'on attend une image à l'écran) et je ne sais pas ce qu'est le code managé/non managé. Si il y a un moyen pour que cela marche instantanément le problème sera complètement résolu.

En tout cas merci beaucoup Robert33 pour l'aide

Don't geek so much