Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

C# Discussion :

Image identique ou différente


Sujet :

C#

  1. #1
    Membre régulier
    Image identique ou différente
    Bonjour à tous,

    Je me crée une petite bibliothèque de comparaison d'image dans le cadre d'un projet. Une des class est censée vérifier que deux image sont identiques en taille, format, résolution, ...

    Pour la comparer pixels par pixels, j'utilise la fonction suivante qui trouve les pixels différent

    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    		protected static IList<Point> GetDiffPixels(Image img1, Image img2)
    		{
    			Bitmap bmp1 = (Bitmap)img1;
    			Bitmap bmp2 = (Bitmap)img2;
     
    			IEnumerable<int> widthRange = Enumerable.Range(0, img1.Width);
    			IEnumerable<int> heightRange = Enumerable.Range(0, img1.Height);
     
    			List<Point> result = widthRange
    				.SelectMany(x => heightRange, (x, y) => new Point(x, y))
    				.Select(point => new
    				        {
    				        	Point = point,
    				        	Pixel1 = bmp1.GetPixel(point.X, point.Y),
    				        	Pixel2 = bmp2.GetPixel(point.X, point.Y)
    				        })
    				.Where(pair => pair.Pixel1 != pair.Pixel2)
    				.Select(pair => pair.Point)
    				.ToList();
     
    			return result;
    		}


    En principe si le résultat est à 0 j'ai une image identique au pixels près.

    J'ai également fait un autre test sur base de deux images identiques au pixels près et au même format sur leur stream par le code suivant :

    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    		public static Stream ImageToStream(Image image)
    		{
    			ImageFormat imgFormat = ImgUtils.GetFormat(image);
     
    			MemoryStream ms = new MemoryStream();
    			image.Save(ms, imgFormat);
    			return  ms;
    		}
     
    		public static bool CompareStreamImage(Image img1, Image img2)
    		{
    			using( Stream stream1 = ImageToStream( img1 ) ) {
    				using(Stream stream2 = ImageToStream( img2 ) ) {
     
    					return CompareStream(stream1, stream2);
     
    				}}
     
    		}
     
    public static bool CompareStream(Stream a, Stream b)
    		{
    			if (a == null || b == null)
    				throw new ArgumentNullException(a == null ? "a" : "b");
     
    			if (a == b)
    				return true;
     
    			if (a.Length != b.Length) //TODO: comment faire la différence si longueur différente ou si pas la même comparaison après
    				return false;
     
    			for (int i = 0; i < a.Length; i++)
    			{
    				int aByte = a.ReadByte();
    				int bByte = b.ReadByte();
     
    				if (aByte.CompareTo(bByte) != 0)
    					return false;
    			}
     
    			return true;
    		}


    Et la le résultat est étonnant.

    En effet, la comparaison au pixels près est identique mais l'analyse du stream est différent au niveaux de la longeur des streams.

    Les questions sont donc

    l'image est-elle différente ou non?
    Qu'est ce qui peut faire que deux images identiques au pixels près peuvent avoir des streams différent?
    Sur quoi puis-je me baser pour m'assurer que deux images sont identiques si les méthodes ci-avant donnent des résultat différent?

    Merci de votre aide et en espérant avoir été clair sur le problème rencontré.

  2. #2
    Expert confirmé
    bonjour

    Il est preferable de comparer les images Byte à Byte (octet par octer).

    ton code revu :

    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
     
        public static bool CompareByteImage(Image img1, Image img2)
            {
               byte[] byteImg1 = ImageToByteArray(img1);
               byte[] byteImg2 = ImageToByteArray(img2);
     
                return CompareBytesImage(byteImg1, byteImg2);
            }
            public static byte[] ImageToByteArray(Image image)
            {
                ImageFormat imgFormat = ImgUtils.GetFormat(image);
     
                MemoryStream ms = new MemoryStream();
                image.Save(ms, imgFormat);
                return ms.ToArray();
            }
            public static bool CompareBytesImage(byte[] a, byte[] b)
            {
                if (a == null || b == null)
                    throw new ArgumentNullException(a == null ? "a" : "b");
     
                if (a == b)
                    return true;
     
                if (a.Length != b.Length) //TODO: comment faire la différence si longueur différente ou si pas la même comparaison après
                    return false;
     
                for (int i = 0; i < a.Length; i++)
                {
                    byte aByte = a[i];
                    byte bByte = b[i];
     
                    if (aByte.CompareTo(bByte) != 0)
                        return false;
                }
     
                return true;
            }


    bon code...

  3. #3
    Expert éminent sénior
    Citation Envoyé par MABROUKI Voir le message
    bonjour

    Il est preferable de comparer les images Byte à Byte (octet par octer).
    Cela ne changera pas le problème. Les méthodes byte à byte ou stream à stream retourneront le même résultat.

    Citation Envoyé par agparchitecture

    Qu'est ce qui peut faire que deux images identiques au pixels près peuvent avoir des streams différent?
    Tout simplement les métadonnées.

    Lorsque tu compares pixel par pixel, tu prends les données de l'image uniquement.

    Lorsque tu compares stream par stream, le stream contient l'image sous un format (PNG, JPEG, etc...). Ces formats contiennent les données de l'image (les pixels), mais aussi des métadonnées (par exemple date et heure). Tu peux donc avoir 2 images identiques au niveau des pixels, mais avec des métadonnées différentes. Ce qui explique que la comparaison pixel par pixel puisse te renvoyer un résultat différent de la comparaison stream par stream.

    Si tu veux comparer les images (le contenu), la bonne méthode est bien d'utiliser les pixels.
    François DORIN
    Consultant informatique : conception, modélisation, développement (C#/.Net et SQL Server)
    Site internet | Profils Viadéo & LinkedIn
    ---------
    Page de cours : fdorin.developpez.com
    ---------
    N'oubliez pas de consulter la FAQ C# ainsi que les cours et tutoriels

  4. #4
    Membre régulier
    Ok merci pour les explications:

    Donc si je veux savoir si les images sont identiques au vu de ce qu'elles affichent (contenus) je regarde les différences des pixels.

    Par contre si je veux savoir si elles sont parfaitement identiques (contenus + métadonnées) je regarde le flux.

    Merci

    @MABROUKI : Ton code me propose de passer par un tableau de bytes à la place d'un ReadBytes sur chaque boucle. Quelle en est la raison par rapport à mon code initial?

  5. #5
    Expert confirmé
    Citation Envoyé par agparchitecture Voir le message
    Ok merci pour les explications:

    Donc si je veux savoir si les images sont identiques au vu de ce qu'elles affichent (contenus) je regarde les différences des pixels.

    Par contre si je veux savoir si elles sont parfaitement identiques (contenus + métadonnées) je regarde le flux.

    Merci

    @MABROUKI : Ton code me propose de passer par un tableau de bytes à la place d'un ReadBytes sur chaque boucle. Quelle en est la raison par rapport à mon code initial?
    La raison en est la clarté .

    1/ Au regard de ce qu'elles affichent c'est allez vite en besogne. Tu veux dire pixel par pixel pour être plus précis .
    Même dans ce cas il faut en plus que les images aient mêmes Hauteur et Largeur sinon false.
    Ensuite pour comparer rapidement des tableaux de pixels le class BitmapData qui utilise l'interop est le moyen le plus rapide pour le faire.
    2/ si elles sont parfaitement identiques (contenus + métadonnées) ,la même condition Hauteur et Largeur s'impose.
    Comme tu n'as pas fourni le contexte ou but précis de cette comparaison ,la question posée reste vaste.

  6. #6
    Membre régulier
    En fait j'ai plusieurs images qui ont été ramenée depuis différent disque durs et je voulais supprimer les doublons. Fdupes sur linux fait ca très bien mais il en laissait beaucoup qui sont toujours identiques.

    J'ai donc regardé comment fdupes fonctionnait et il fait une comparaison sur le stream. j'ai donc regardé pour faire une comparaison sur les pixels et me suis rendu compte que les images était quand même identique.

    Ne me tracassant pas des métadata, je dois donc bien faire une comparaison pixels par pixels pour m'assurer que les images sont identiques et n'en conserver plus qu'une pour ne plus avoir de doublons.

  7. #7
    Expert confirmé
    bonjour
    Pour une comparaison par pixel exacte et pour complément les conditions à réunir sont:
    - les images doivent avoir mêmes Size(Hauteur et Largeur en pixel).
    - les images doivent avoir même Résolution
    - les images doivent avoir même PixelFormat(Format32bppRgb,Format32bppArgb,Indexed,etc...)
    - les images doivent avoir même RawFormat(bmp,jpg,png,etc...)

    bon code...

  8. #8
    Membre régulier
    Je viens de rouvrir le post car je me pose encore une question qui reste dans la logique de cette discutions. Si nécessaire je peux ouvrir une nouvelle question

    @MABROUKI : Effectivement, je n'ai pas mis tout le code mais je vérifie bien avant de comparer les pixels que les images sont bien du même type et peuvent être comparées
    Si nécessaire je peux ajouter la class que je suis en train de faire.

    Par contre en continuant le développement je me pose une question car le getpixels(x, y) ou l'utilisation de linq dans ma fonction est très lent sur les images un peut grosse.

    mais on a vu que la différence du stream venait des métadata.

    La question est donc si je clone mes deux images en supprimant les métadat des images est-ce que j'aurai le même Stream?

    En fait j'envisage de faire comme suit:

    1. je copie les images en supprimant les métadata
    2. je convertit les images sans métadat en stream
    3. je compare les streams en comptabilisant les différences. (mais est-ce que les stream seront bien identique???)
    4. Si ceux-ci sont identiques
    alors les images sont identiques
    sinon l’addition des différence me permet de caluler le pourcentage de similarité

    Est-ce que cet algorithme peut marcher correctement ou le fait de simplement supprimer les métadata d'image identique ne me garantira pas des stream identique?

    PS: j'ai vu qu'il existait également lockbit mais je ne veux pas forcément "travailler sur les images" mais juste les comparer sans préoccupation de métadata.

    Merci

###raw>template_hook.ano_emploi###