Vous ne trouvez pas de réponse à votre problème ? Alors posez la question dans le forum. Souvenez-vous qu'il n'y a jamais de question bête, mais rester dans l'ignorance parce que l'on n'ose pas poser une question, ça c'est une erreur !

TRAITEMENT D'IMAGE EN NOIR ET BLANC


Information sur la source

Catégorie :Graphique Source .NET ( DotNet ) Classé sous : image, noir, blanc, difusion, erreurs Niveau : Initié Date de création : 08/11/2006 Date de mise à jour : 09/11/2006 14:22:08 Vu : 12 744

Note :
Aucune note

Commentaire sur cette source (3)
Ajouter un commentaire et/ou une note

Description

Cliquez pour voir la capture en taille normale
Bonjours à tous

Voilà je doit travaillé sur des image et les convertir en noir & blanc (C'est un projet de génération de fax), il s'agit aussi de mon premier projet C# (le C/C++ est assez loin derriere moi mais je suis content de revenir a ce type de syntax aprés une longue période VB).

cette classe fais partie d'un projet bien plus vaste mais elle pourra aidé des paersonne qui auront a travailler sur des image en noir et blanc (sujet peu traiter sur les sites codes source a ce que j'ai vue), il y'a aussi Frédéric Mélantois qui traite du sujet du traitement d'image sur son blog et dans des articles (dernier : http://www.techheadbrothers.com/Articles.aspx?Id=8d3eb481-8a98-42a6-8033-e851c797aa60&p=1).
donc ca pourras peut-être soulever des questions/sugestions sur le sujet.

Enfin bref j'ai essayer d'optimiser mais y'a sans doute d'autres optimisation a faire.

C'est la première fois que je traite des image et dans l'ensemble je ne suis pas mécontant du résultat , mon application est fluide toutefois je ne suis pas satisfais de la gestion de la luminositer, j'ai essayer de trouver un moyen de ne pas "bruler" les parties claires mais on as trés vite une perte de constraste, si quelqu'un peut me donnée des méthodes pour réaliser ca de maniére plus propre je suis preneur(j'y mais juste la condition que l'algo s'applique sur le niveau de gris.

La classe supporte trois algo pour la transphormation niveau de gris -> Noir et blanc :
RANDOM (on compare le niveau de gris a une valeur aléatoire),
HALFTONE (on compare le niveau de gris a une valeur fixé),
FLOYD Steinberg (algo par difusion d'erreur) sans doute le plus intéréssant mais aussi le plus gourmand

il est a noté que l'algo Floyd a tendonce a créer des motif répétitif pour les surfaces de couleur uniforme et dégradé on pourat donc l'amélioré en ajoutant une petite dose d'aléatoire...


le traitement noir et blanc est volontairement séparer de la partie qui gére le passage en noir et blanc, en effet cette partie n'est recalculer que lorsque l'on modifie la taille de l'image ce qui est assez rare pour des pages de fax.


 

Source

  • //Structure de manipulation de tableaux RGB
  • public struct RGBPix
  • {
  • public byte R;
  • public byte G;
  • public byte B;
  • }
  • //Classe de manipulation et conversion d'image en noir et blanc
  • public class FaxImage
  • {
  • //Petite enumération des différent algo gérer par la classe
  • public enum BWAlgorithme
  • {
  • Floyd,
  • Haltone,
  • Random
  • }
  • //Immage d'origine
  • private Bitmap _OriginalImg = null;
  • //Pointer ver un tableau de byte contenant les données en niveaux de gris
  • private IntPtr _GreyDat = IntPtr.Zero;
  • //Image finale
  • private Bitmap _BWImg = null;
  • //variable de configuration internes
  • private BWAlgorithme _DitherAlgo = BWAlgorithme.Floyd;
  • private sbyte _Luminosity = 0;
  • private int _Height = 0;
  • private int _Width = 0;
  • //Constructeur On lui passe l'image d'origine, et la taille finale souhaiter
  • public FaxImage(System.Drawing.Bitmap Img,int Width,int Height)
  • {
  • this.Width = Width;
  • this.Height = Height;
  • this._OriginalImg = Img;
  • }
  • //Métode pour changer d'image d'origine
  • public void SetImage(Bitmap Image)
  • {
  • this._OriginalImg = Image;
  • if (this._GreyDat != IntPtr.Zero)
  • {
  • System.Runtime.InteropServices.Marshal.FreeHGlobal(this._GreyDat);
  • this._GreyDat = IntPtr.Zero;
  • }
  • }
  • //Propriété pour récupérer l'image en noir et blanc
  • public System.Drawing.Bitmap BWImage
  • {
  • get
  • {
  • //On recalcul l'image en niveau de gris si besoin.
  • if (this._GreyDat == IntPtr.Zero)
  • Make_GrayDat();
  • //On recalcul l'image noir et blanc si besoin
  • if (_BWImg==null)
  • switch (this._DitherAlgo)
  • {
  • case BWAlgorithme.Floyd:
  • Make_BWDat_Floyd();
  • break;
  • case BWAlgorithme.Haltone:
  • Make_BWDat_HalfTone();
  • break;
  • case BWAlgorithme.Random:
  • Make_BWDat_Random();
  • break;
  • }
  • //On retourne l'image
  • return this._BWImg;
  • }
  • }
  • Propriéter de modification de la luminositer de l'image final
  • public sbyte Luminosity
  • {
  • get
  • {
  • return this._Luminosity;
  • }
  • set
  • {
  • if (this._Luminosity != value)
  • {
  • this._Luminosity = value;
  • if (this._BWImg != null)
  • {
  • this._BWImg.Dispose();
  • this._BWImg = null;
  • }
  • }
  • }
  • }
  • //Propriéter pour indiquer l'algo a utiliser lors de la conversion
  • public BWAlgorithme DitherAlgo
  • {
  • get
  • {
  • return this._DitherAlgo;
  • }
  • set
  • {
  • if (this._DitherAlgo != value)
  • {
  • this._DitherAlgo = value;
  • if (this._BWImg != null)
  • {
  • this._BWImg.Dispose();
  • this._BWImg = null;
  • }
  • }
  • }
  • }
  • //Propriéter de la hauteur final de l'image
  • public int Height
  • {
  • get
  • {
  • return this._Height;
  • }
  • set
  • {
  • if (value < 1) value = 1;
  • if (this._Height != value)
  • {
  • this._Height = value;
  • if (this._GreyDat != IntPtr.Zero)
  • {
  • System.Runtime.InteropServices.Marshal.FreeHGlobal(this._GreyDat);
  • this._GreyDat = IntPtr.Zero;
  • }
  • }
  • }
  • }
  • //Propriéter de la largeur final de l'image
  • public int Width
  • {
  • get
  • {
  • return this._Width;
  • }
  • set
  • {
  • if (value < 1) value = 1;
  • if (this._Width != value)
  • {
  • this._Width = value;
  • if (this._GreyDat != IntPtr.Zero)
  • {
  • System.Runtime.InteropServices.Marshal.FreeHGlobal(this._GreyDat);
  • this._GreyDat = IntPtr.Zero;
  • }
  • }
  • }
  • }
  • private void Make_BWDat_Floyd()
  • {
  • //Initialisation de l'image
  • if (this._BWImg != null)
  • this._BWImg.Dispose();
  • this._BWImg=new Bitmap(this._Width, this._Height, PixelFormat.Format1bppIndexed);
  • BitmapData BWDat = this._BWImg.LockBits(new Rectangle(0, 0, this._Width, this._Height), ImageLockMode.WriteOnly, PixelFormat.Format1bppIndexed);
  • //Allocation de la mémoire pour les erreurs
  • IntPtr Erreurs = System.Runtime.InteropServices.Marshal.AllocHGlobal(((this._Height + 1) * this._Width + 1) * sizeof(int));
  • //Calcule des donnée noir et blanc
  • unsafe
  • {
  • byte CurBitMsk;
  • int PointVal;
  • int ErrToDispatch;
  • byte* CurGrey = (byte*)this._GreyDat.ToPointer();
  • int* CurErreur = (int*)Erreurs.ToPointer();
  • int* CurErreur7 = &(CurErreur[1]);
  • int* CurErreur1 = &(CurErreur[this._Width - 1]);
  • int* CurErreur5 = &(CurErreur[this._Width]);
  • int* CurErreur3 = &(CurErreur[this._Width + 1]);
  • //initialisation des premiéres erreurs (les autres seront initialiser à la volée).
  • for (int* LastErreur = &(((int*)Erreurs.ToPointer())[this._Width]); CurErreur <= LastErreur; CurErreur++)
  • *CurErreur = 0;
  • CurErreur = (int*)Erreurs.ToPointer();
  • Random Rnd = new Random();
  • float LuminosityRatio = (float)((float)255/(float)(255 - Math.Abs(this._Luminosity)));
  • byte LuminosityOffset = (this._Luminosity < 0) ? (byte)0 : (byte)this._Luminosity;
  • //Table de précacule de la luminosité
  • byte* LuminosityTab = stackalloc byte [255];
  • for (int grayValue = 0; grayValue < 255; grayValue++)
  • LuminosityTab[grayValue] = (byte)(grayValue / LuminosityRatio + LuminosityOffset);
  • for (int y = 0; y < this._Height; y++)
  • {
  • CurBitMsk = 128;// 1 << 7;
  • for (
  • byte* CurBWs = &(((byte*)BWDat.Scan0.ToPointer())[BWDat.Stride * y]), //On ointe vers le premier octe de la ligne en cours.
  • afterLastGrey = &(((byte*)this._GreyDat.ToPointer())[(y+1)*this.Width]); //Borne de la ligne des niveaux de gris
  • //On parcourt la ligne courante
  • CurGrey < afterLastGrey;
  • //incrémentation des pointeurs
  • CurGrey++,
  • CurErreur++,
  • CurErreur7++,
  • CurErreur1++,
  • CurErreur5++,
  • CurErreur3++
  • )
  • {
  • //Calcul de la valeur du point
  • PointVal = LuminosityTab[*CurGrey] + *CurErreur + Rnd.Next(-25, 25);
  • if(PointVal > 128)
  • {
  • //Enregistrement du point
  • *CurBWs |= CurBitMsk;
  • //Calcul de l'erreur
  • ErrToDispatch = PointVal - 255;
  • }
  • else
  • {
  • //Enregistrement du point
  • *CurBWs &= (byte)(255 ^ CurBitMsk);
  • //Calcul de l'erreur
  • ErrToDispatch = PointVal;
  • }
  • //Diffusion de l'erreur
  • *CurErreur7 += ((ErrToDispatch * 7) >> 4);
  • *CurErreur1 += (ErrToDispatch >> 4);
  • *CurErreur5 += ((ErrToDispatch * 5) >> 4);
  • *CurErreur3 = ((ErrToDispatch * 3) >> 4); // = et pas += pour initialisé à la volée
  • //Initialisation des pointeurs et du masque de bit courant pour le point suivant
  • CurBitMsk >>= 1;
  • if (CurBitMsk == 0)
  • {
  • CurBitMsk = 128;//1 << 7;
  • CurBWs++;
  • }
  • }
  • }
  • }
  • //Libération du tableau des erreurs
  • System.Runtime.InteropServices.Marshal.FreeHGlobal(Erreurs);
  • //Sauveguarde de l'image.
  • this._BWImg.UnlockBits(BWDat);
  • }
  • private void Make_BWDat_Random()
  • {
  • //Initialisation de l'image
  • if (this._BWImg != null)
  • this._BWImg.Dispose();
  • this._BWImg = new Bitmap(this._Width, this._Height, PixelFormat.Format1bppIndexed);
  • BitmapData BWDat = this._BWImg.LockBits(new Rectangle(0, 0, this._Width, this._Height), ImageLockMode.WriteOnly, PixelFormat.Format1bppIndexed);
  • //initialisation d'un Tableau de byte aléatoire.
  • Byte[] rnds = new Byte[this._Width * this._Height];
  • new Random().NextBytes(rnds);
  • //Calcule des donnée noir et blanc
  • unsafe
  • {
  • byte* CurBWs = (byte*)BWDat.Scan0.ToPointer();
  • byte* CurGrey = (byte*)this._GreyDat.ToPointer();
  • byte CurBitMsk;
  • double LuminosityRatio = (255 - Math.Abs(this._Luminosity)) / 255.0;
  • for (int y = 0; y < this._Height; y++)
  • {
  • CurBitMsk = 128;// 1 << 7;
  • CurBWs = &(((byte*)BWDat.Scan0.ToPointer())[BWDat.Stride * y]);
  • for (int x = 0; x < this._Width; x++)
  • {
  • //Enregistrement du point
  • *CurBWs = (byte)((((*CurGrey * LuminosityRatio) + ((this._Luminosity < 0) ? (sbyte)0 : this._Luminosity)) > rnds[x + y * this._Width]) ? (*CurBWs | CurBitMsk) : (*CurBWs & (255 ^ CurBitMsk)));
  • //Initialisation des pointeurs et du masque de bit courant pour le point suivant
  • CurBitMsk >>= 1;
  • if (CurBitMsk == 0)
  • {
  • CurBitMsk = 128; // 1 << 7;
  • CurBWs++;
  • }
  • CurGrey++;
  • }
  • }
  • }
  • //Sauveguarde de l'image.
  • this._BWImg.UnlockBits(BWDat);
  • }
  • private void Make_BWDat_HalfTone()
  • {
  • //Initialisation de l'image
  • if (this._BWImg != null)
  • this._BWImg.Dispose();
  • this._BWImg = new Bitmap(this._Width, this._Height, PixelFormat.Format1bppIndexed);
  • BitmapData BWDat = this._BWImg.LockBits(new Rectangle(0, 0, this._Width, this._Height), ImageLockMode.WriteOnly, PixelFormat.Format1bppIndexed);
  • //Calcul des donnée noir et blanc
  • unsafe
  • {
  • byte* CurBWs = (byte*)BWDat.Scan0.ToPointer();
  • byte* CurGrey = (byte*)this._GreyDat.ToPointer();
  • byte CurBitMsk;
  • double LuminosityRatio = (255 - Math.Abs(this._Luminosity)) / 255.0;
  • for (int y = 0; y < this._Height; y++)
  • {
  • CurBitMsk = 1 << 7;
  • CurBWs = &(((byte*)BWDat.Scan0.ToPointer())[BWDat.Stride * y]);
  • for (int x = 0; x < this._Width; x++)
  • {
  • //Enregistrement du point
  • *CurBWs = (byte)((((*CurGrey * LuminosityRatio) + ((this._Luminosity < 0) ? (sbyte)0 : this._Luminosity)) > 128) ? (*CurBWs | CurBitMsk) : (*CurBWs & (255 ^ CurBitMsk)));
  • //Initialisation des pointeurs et du masque de bit courant pour le point suivant
  • CurBitMsk >>= 1;
  • if (CurBitMsk == 0)
  • {
  • CurBitMsk = 1 << 7;
  • CurBWs++;
  • }
  • CurGrey++;
  • }
  • }
  • }
  • //Enregistrement des modification.
  • this._BWImg.UnlockBits(BWDat);
  • }
  • public void Make_GrayDat()
  • {
  • //On redimensionne l'image d'origine.
  • Bitmap SizedImg = new Bitmap (this._OriginalImg ,this._Width,this._Height);
  • //OnRécupére les données de l'image.
  • BitmapData RGBDat = SizedImg.LockBits(new Rectangle(0, 0, this._Width, this._Height), ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);
  • //On initialise le tableau de donnée.
  • if (this._GreyDat != IntPtr.Zero)
  • {
  • System.Runtime.InteropServices.Marshal.ReAllocHGlobal(this._GreyDat,new IntPtr(this._Width * this._Height));
  • }
  • else
  • {
  • this._GreyDat = System.Runtime.InteropServices.Marshal.AllocHGlobal(this._Width * this._Height);
  • }
  • //On Recalcule les données de niveau de gris.
  • unsafe
  • {
  • //tables de precalculs des multiplications pour le calcul du niveau de gris
  • //Les tableaus sont créer dans le tas (+ rapide por les petits tableau)
  • //ils sont libéré à la fin de la fonction.
  • int* multiBleu = stackalloc int[256];
  • int* multiVert = stackalloc int[256];
  • int* multiRouge = stackalloc int[256];
  • for (int i = 0; i < 256; i++)
  • {
  • multiBleu[i] = 76 * i;
  • multiVert[i] = 151 * i;
  • multiRouge[i] = 28 * i;
  • }
  • byte* CurGrey = (byte*)this._GreyDat.ToPointer();
  • for (int y = 0; y < this._Height; y++)
  • {
  • //On pointe vers le début de la ligne
  • RGBPix* CurRGBDat = (RGBPix*)&(((byte*)RGBDat.Scan0.ToPointer())[y * RGBDat.Stride]);
  • //Balayage de la ligne
  • for (
  • // Calcul de la borne supérieur de la ligne
  • byte* afterLastGrey = &(((byte*)this._GreyDat.ToPointer())[(y+1)*this.Width]); //Borne de la ligne des niveaux de gris
  • //On Balaye toute la ligne
  • CurGrey < afterLastGrey;
  • //On pointe vers les données suivantes
  • CurRGBDat++,
  • CurGrey++
  • )
  • *CurGrey = (byte)((multiBleu[CurRGBDat->B] + multiVert[CurRGBDat->G] + multiRouge[CurRGBDat->R]) >> 8);
  • }
  • }
  • //On supprime les donnée en noir et blans pour forcer leur recalcule
  • if (_BWImg != null)
  • {
  • this._BWImg.Dispose();
  • this._BWImg=null;
  • }
  • //On libére les ressoure
  • SizedImg.UnlockBits(RGBDat);
  • SizedImg.Dispose();
  • }
  • }
        //Structure de manipulation de tableaux RGB
    public struct RGBPix
    {
        public byte R;
        public byte G;
        public byte B;
    }

    //Classe de manipulation et conversion d'image en noir et blanc
    public class FaxImage
    {

        //Petite enumération des différent algo gérer par la classe
        public enum BWAlgorithme
        {
            Floyd,
            Haltone,
            Random
        }
        //Immage d'origine
        private Bitmap _OriginalImg = null;
        //Pointer ver un tableau de byte contenant les données en niveaux de gris
        private IntPtr _GreyDat = IntPtr.Zero;
        //Image finale
        private Bitmap _BWImg = null;

        //variable de configuration internes
        private BWAlgorithme _DitherAlgo = BWAlgorithme.Floyd;
        private sbyte _Luminosity = 0;
        private int _Height = 0;
        private int _Width = 0;

        //Constructeur On lui passe l'image d'origine, et la taille finale souhaiter
        public FaxImage(System.Drawing.Bitmap Img,int Width,int Height)
        {
            this.Width = Width;
            this.Height = Height;
            this._OriginalImg = Img;
        }

        //Métode pour changer d'image d'origine
        public void SetImage(Bitmap Image)
        {
            this._OriginalImg = Image;
            if (this._GreyDat != IntPtr.Zero)
            {
                System.Runtime.InteropServices.Marshal.FreeHGlobal(this._GreyDat);
                this._GreyDat = IntPtr.Zero;
            }
        }

        //Propriété pour récupérer l'image en noir et blanc
        public System.Drawing.Bitmap BWImage
        {
            get
            {
                //On recalcul l'image en niveau de gris si besoin.
                if (this._GreyDat == IntPtr.Zero)
                    Make_GrayDat();
                //On recalcul l'image noir et blanc si besoin
                if (_BWImg==null)
                    switch (this._DitherAlgo)
                    {
                        case BWAlgorithme.Floyd:
                            Make_BWDat_Floyd();
                            break;
                        case BWAlgorithme.Haltone:
                            Make_BWDat_HalfTone();
                            break;
                        case BWAlgorithme.Random:
                            Make_BWDat_Random();
                            break;
                    }
                //On retourne l'image
                return this._BWImg;
            }            
        }

        Propriéter de modification de la luminositer de l'image final
        public sbyte Luminosity
        {
            get
            {
                return this._Luminosity;
            }
            set
            {
                if (this._Luminosity != value)
                {
                    this._Luminosity = value;
                    if (this._BWImg != null)
                    {
                        this._BWImg.Dispose();
                        this._BWImg = null;
                    }
                }
            }
        }

        //Propriéter pour indiquer l'algo a utiliser lors de la conversion
        public BWAlgorithme DitherAlgo
        {
            get
            {
                return this._DitherAlgo;
            }
            set
            {
                if (this._DitherAlgo != value)
                {
                    this._DitherAlgo = value;
                    if (this._BWImg != null)
                    {
                        this._BWImg.Dispose();
                        this._BWImg = null;
                    }
                }
            }
        }

        //Propriéter de la hauteur final de l'image
        public int Height
        {
            get
            {
                return this._Height;
            }
            set
            {
                if (value < 1) value = 1;
                if (this._Height != value)
                {
                    this._Height = value;
                    if (this._GreyDat != IntPtr.Zero)
                    {
                        System.Runtime.InteropServices.Marshal.FreeHGlobal(this._GreyDat);
                        this._GreyDat = IntPtr.Zero;
                    }

                }
            }
        }

        //Propriéter de la largeur final de l'image
        public int Width
        {
            get
            {
                return this._Width;
            }
            set
            {   
                if (value < 1) value = 1;
                if (this._Width != value)
                {
                    this._Width = value;
                    if (this._GreyDat != IntPtr.Zero)
                    {
                        System.Runtime.InteropServices.Marshal.FreeHGlobal(this._GreyDat);
                        this._GreyDat = IntPtr.Zero;
                    }

                }
            }
        }

        private void Make_BWDat_Floyd()
        {
            //Initialisation de l'image
            if (this._BWImg != null)
                this._BWImg.Dispose();
            this._BWImg=new Bitmap(this._Width, this._Height, PixelFormat.Format1bppIndexed);
            BitmapData BWDat = this._BWImg.LockBits(new Rectangle(0, 0, this._Width, this._Height), ImageLockMode.WriteOnly, PixelFormat.Format1bppIndexed);

            //Allocation de la mémoire pour les erreurs
            IntPtr Erreurs = System.Runtime.InteropServices.Marshal.AllocHGlobal(((this._Height + 1) * this._Width + 1) * sizeof(int));

            //Calcule des donnée noir et blanc
            unsafe
            {
                byte CurBitMsk;
                int PointVal;
                int ErrToDispatch;
                byte* CurGrey = (byte*)this._GreyDat.ToPointer();
                int* CurErreur = (int*)Erreurs.ToPointer();
                int* CurErreur7 = &(CurErreur[1]);
                int* CurErreur1 = &(CurErreur[this._Width - 1]);
                int* CurErreur5 = &(CurErreur[this._Width]);
                int* CurErreur3 = &(CurErreur[this._Width + 1]);

                //initialisation des premiéres erreurs (les autres seront initialiser à la volée).
                for (int* LastErreur = &(((int*)Erreurs.ToPointer())[this._Width]); CurErreur <= LastErreur; CurErreur++)
                    *CurErreur = 0;
                CurErreur = (int*)Erreurs.ToPointer();

                Random Rnd = new Random();

                float LuminosityRatio = (float)((float)255/(float)(255 - Math.Abs(this._Luminosity)));
                byte LuminosityOffset = (this._Luminosity < 0) ? (byte)0 : (byte)this._Luminosity;

                //Table de précacule de la luminosité
                byte* LuminosityTab = stackalloc byte [255];
                for (int grayValue = 0; grayValue < 255; grayValue++)
                    LuminosityTab[grayValue] = (byte)(grayValue / LuminosityRatio + LuminosityOffset);

                for (int y = 0; y < this._Height; y++)
                {   
                    CurBitMsk = 128;// 1 << 7;
                    for (
                        byte* CurBWs = &(((byte*)BWDat.Scan0.ToPointer())[BWDat.Stride * y]),  //On ointe vers le premier octe de la ligne en cours.
                        afterLastGrey = &(((byte*)this._GreyDat.ToPointer())[(y+1)*this.Width]); //Borne de la ligne des niveaux de gris
                        //On parcourt la ligne courante
                        CurGrey < afterLastGrey;
                        //incrémentation des pointeurs
                        CurGrey++,
                        CurErreur++,
                        CurErreur7++,
                        CurErreur1++,
                        CurErreur5++,
                        CurErreur3++
                    )
                    {
                        //Calcul de la valeur du point
                        PointVal = LuminosityTab[*CurGrey] + *CurErreur + Rnd.Next(-25, 25);
                        
                        if(PointVal > 128)
                        {
                            //Enregistrement du point
                            *CurBWs |= CurBitMsk;
                            //Calcul de l'erreur
                            ErrToDispatch = PointVal - 255;
                        }
                        else
                        {
                            //Enregistrement du point
                            *CurBWs &=  (byte)(255 ^ CurBitMsk);
                            //Calcul de l'erreur
                            ErrToDispatch = PointVal;
                        }

                        //Diffusion de l'erreur
                        *CurErreur7 += ((ErrToDispatch * 7) >> 4);
                        *CurErreur1 += (ErrToDispatch >> 4);
                        *CurErreur5 += ((ErrToDispatch * 5) >> 4);
                        *CurErreur3 = ((ErrToDispatch * 3) >> 4); // = et pas += pour initialisé à la volée

                        //Initialisation des pointeurs et du masque de bit courant pour le point suivant
                        CurBitMsk >>= 1;
                        if (CurBitMsk == 0)
                        {
                            CurBitMsk = 128;//1 << 7;
                            CurBWs++;
                        }
                    }
                }
            }

            //Libération du tableau des erreurs
            System.Runtime.InteropServices.Marshal.FreeHGlobal(Erreurs);
            
            //Sauveguarde de l'image.
            this._BWImg.UnlockBits(BWDat);
        }

        private void Make_BWDat_Random()
        {
            //Initialisation de l'image
            if (this._BWImg != null)
                this._BWImg.Dispose();
            this._BWImg = new Bitmap(this._Width, this._Height, PixelFormat.Format1bppIndexed);
            BitmapData BWDat = this._BWImg.LockBits(new Rectangle(0, 0, this._Width, this._Height), ImageLockMode.WriteOnly, PixelFormat.Format1bppIndexed);

            //initialisation d'un Tableau de byte aléatoire.
            Byte[] rnds = new Byte[this._Width * this._Height];
            new Random().NextBytes(rnds);

            //Calcule des donnée noir et blanc
            unsafe
            {
                byte* CurBWs = (byte*)BWDat.Scan0.ToPointer();
                byte* CurGrey = (byte*)this._GreyDat.ToPointer();
                byte CurBitMsk;
                double LuminosityRatio = (255 - Math.Abs(this._Luminosity)) / 255.0;
                for (int y = 0; y < this._Height; y++)
                {
                    CurBitMsk = 128;// 1 << 7;
                    CurBWs = &(((byte*)BWDat.Scan0.ToPointer())[BWDat.Stride * y]);
                    for (int x = 0; x < this._Width; x++)
                    {                        
                        //Enregistrement du point
                        *CurBWs = (byte)((((*CurGrey * LuminosityRatio) + ((this._Luminosity < 0) ? (sbyte)0 : this._Luminosity)) > rnds[x + y * this._Width]) ? (*CurBWs | CurBitMsk) : (*CurBWs & (255 ^ CurBitMsk)));
                        
                        //Initialisation des pointeurs et du masque de bit courant pour le point suivant
                        CurBitMsk >>= 1;
                        if (CurBitMsk == 0)
                        {
                            CurBitMsk = 128; // 1 << 7;
                            CurBWs++;
                        }
                        CurGrey++;
                    }
                }
            }

            //Sauveguarde de l'image.
            this._BWImg.UnlockBits(BWDat);
        }

        private void Make_BWDat_HalfTone()
        {
            //Initialisation de l'image
            if (this._BWImg != null)
                this._BWImg.Dispose();
            this._BWImg = new Bitmap(this._Width, this._Height, PixelFormat.Format1bppIndexed);
            BitmapData BWDat = this._BWImg.LockBits(new Rectangle(0, 0, this._Width, this._Height), ImageLockMode.WriteOnly, PixelFormat.Format1bppIndexed);

            //Calcul des donnée noir et blanc
            unsafe
            {
                byte* CurBWs = (byte*)BWDat.Scan0.ToPointer();
                byte* CurGrey = (byte*)this._GreyDat.ToPointer();
                byte CurBitMsk;
                double LuminosityRatio = (255 - Math.Abs(this._Luminosity)) / 255.0;
                for (int y = 0; y < this._Height; y++)
                {
                    CurBitMsk = 1 << 7;
                    CurBWs = &(((byte*)BWDat.Scan0.ToPointer())[BWDat.Stride * y]);
                    for (int x = 0; x < this._Width; x++)
                    {
                        //Enregistrement du point
                        *CurBWs = (byte)((((*CurGrey * LuminosityRatio) + ((this._Luminosity < 0) ? (sbyte)0 : this._Luminosity)) > 128) ? (*CurBWs | CurBitMsk) : (*CurBWs & (255 ^ CurBitMsk)));

                        //Initialisation des pointeurs et du masque de bit courant pour le point suivant
                        CurBitMsk >>= 1;
                        if (CurBitMsk == 0)
                        {
                            CurBitMsk = 1 << 7;
                            CurBWs++;
                        }
                        CurGrey++;
                    }
                }
            }

            //Enregistrement des modification.
            this._BWImg.UnlockBits(BWDat);
        }


public void Make_GrayDat()
        {
            //On redimensionne l'image d'origine.
            Bitmap SizedImg = new Bitmap (this._OriginalImg ,this._Width,this._Height);

            //OnRécupére les données de l'image.
            BitmapData RGBDat = SizedImg.LockBits(new Rectangle(0, 0, this._Width, this._Height), ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);

            //On initialise le tableau de donnée.
            if (this._GreyDat != IntPtr.Zero)
            {
                System.Runtime.InteropServices.Marshal.ReAllocHGlobal(this._GreyDat,new IntPtr(this._Width * this._Height));
            }
            else
            {
                this._GreyDat = System.Runtime.InteropServices.Marshal.AllocHGlobal(this._Width * this._Height);
            }

            //On Recalcule les données de niveau de gris.
            unsafe
            {
                //tables de precalculs des multiplications pour le calcul du niveau de gris
                //Les tableaus sont créer dans le tas (+ rapide por les petits tableau)
                //ils sont libéré à la fin de la fonction.
                int* multiBleu = stackalloc int[256];
                int* multiVert = stackalloc int[256];
                int* multiRouge = stackalloc int[256];
                for (int i = 0; i < 256; i++)
                {
                    multiBleu[i] = 76 * i;
                    multiVert[i] = 151 * i;
                    multiRouge[i] = 28 * i;
                }

                byte* CurGrey = (byte*)this._GreyDat.ToPointer();
                for (int y = 0; y < this._Height; y++)
                {
                    //On pointe vers le début de la ligne
                    RGBPix* CurRGBDat = (RGBPix*)&(((byte*)RGBDat.Scan0.ToPointer())[y * RGBDat.Stride]);
                    //Balayage de la ligne
                    for (
                        // Calcul de la borne supérieur de la ligne
                        byte* afterLastGrey = &(((byte*)this._GreyDat.ToPointer())[(y+1)*this.Width]); //Borne de la ligne des niveaux de gris
                        //On Balaye toute la ligne
                        CurGrey < afterLastGrey; 
                        //On pointe vers les données suivantes
                        CurRGBDat++,
                        CurGrey++
                    )
                        *CurGrey = (byte)((multiBleu[CurRGBDat->B] + multiVert[CurRGBDat->G] + multiRouge[CurRGBDat->R]) >> 8);
                }
            }

            //On supprime les donnée en noir et blans pour forcer leur recalcule
            if (_BWImg != null)
            {
                this._BWImg.Dispose();
                this._BWImg=null;
            }

            //On libére les ressoure
            SizedImg.UnlockBits(RGBDat);
            SizedImg.Dispose();
        }
    }

Conclusion

=============================================
Quelques explications
=============================================

les compteurs:
---------------------

On remarque que les for correspondant au balayage des lignes n'utilise pas de compteur classique.
mais travaile directement sur les pointeurs.

En effet lorsque je balaye une ligne j'utilise un pointeur pour acceder au pixel courrant,
et j'incremente directement le pointeur pour acceder au pixel suivant, en effet
*PointVar est plus rapide que PointVar[x]
donc x n'a plus d'autre utilité que de compter le nombre de fois ou je vais répéter les opératon,

la démarche est de se demander comment supprimer x tous bonnement.

et le principe est simple :

Avant d'entrer dans la boucle ou au début de celle ci j'initialize PointVar
pour pointer sur le premier pixel de la ligne,
Dans le même temps je créer un second pointer BornePointVar qui pointe lui sur le dernier pixel de la ligne + 1 (ce n'est pas grave si il est hors de l'image ou même de la mémoire aloué car il ne serat jamais fais accé a la donnée sous jacente).

ca donne ca :
for(
    type* PointVar = AddressePremierPixelLigne,
    type* BornePointVar = AddresseDernierPixelLigne + 1;

    PointVar < BornePointVar;

    PointVar++
)
{
............
}


les masques de pixel :
---------------------

lors de laccés aux donnée d'une image noir et blanc les pixels sont sur chaque bit, il faut donc utilisé un masque de bit pour travailler au niveau de chaqu'un des pixel

la méthode la plus rapide que j'ai trouver est :

a la fin de la boucle for sur chaque pixels de la ligne :

avec CurBWs pointeur byte* sur les octets de la ligne,
et CurBitMsk byte


CurBitMsk >>= 1;
if (CurBitMsk == 0)
{
    CurBitMsk = 128;//1 << 7;
    CurBWs++;
}

bien entendu il faut avant de lire le premier pixel de la ligne initialiser le masque avec :
CurBitMsk = 128;//1 << 7;

ATTENTION la méthode de lecture de la ligne avec des bornes pointeur
n'est pas directement utilisable avec les images en noire et blanc pusique les donnée de pixel ne sont pas caller sur des octets.

mais dans le cas présent ce n'est pas un problème puisque les donnée sont lu depuis un tableau de byte des couleurs en niveaux de gris

======================================================


Je mettrait a jour avec les conseil que j'aurrais retenue ainsi qu'avec d'autre partie du programme qui traite des images.

A bientôt...
 

Historique

08 novembre 2006 17:02:39 :
Ajout d'une Photo, de commentaires et modifications mineur du code...
09 novembre 2006 14:22:08 :
Optimisation de l'algo Floyd et de l'algo de niveau de gris

Commentaires et avis

signaler à un administrateur
Commentaire de Malkuth le 08/11/2006 02:43:41

Sorry
En fait le traitement en noir et blanc a été traiter dans cette source : par une autre méthode que je mettrais sans doute en plus des autres déjà présente.

http://www.csharpfr.com/codes/IMAGES-DITHERING-MOTIF_30132.aspx

toutefois l'algo de cette source transphorme l'image en conservant le format de couleur 32bit, dans le code présenter ci dessus on obtient une image  de 1 bit par pixel (nécessaire pour souver sous forme d'un tiff multipage CCIT4 pour la compatibilité avec les carte Fax de Eicon (et sans doute d'autre mais je les connait pas!!!).

signaler à un administrateur
Commentaire de Malkuth le 08/11/2006 17:13:41

Les gros carrés sur l'image de présentation c'est du a la compression Jpeg de l'image...

signaler à un administrateur
Commentaire de Patrice99 le 09/11/2006 10:10:48

En JPeg2000, les copies d'écran ne sont pas dégradées : il suffit d'enregistrer au format .png puis de renommer en .jpg

Ajouter un commentaire

Discussions en rapport avec ce code source dans le forum

extraction d'une zone d'une image [ par wenna ] salut je cherche un code c# qui permet d'extraire une zone à partir d'une image.l'image est constutiée des zones où le couleur des pixels sont noir et SetPixel transparent [ par amlb ] Bonjour,mon probl&#232;me est le suivant: je traite une image Bitmap et je veux pouvoir transformer tous les pixels blanc de cette image en pixel tran Probleme après conversion de Visual Studio 2003 à 2005 [ par EvilDef ] Je ne peux pas voir le fenetre après la conversion de mon logiciel sous VS2005, j'obtient le message : <td style="VERTICAL-ALIGN: top; LETTER-S CAPTURE D'IMAGE [ par DavDav02 ] Salut à tous,Je bosse actuellement sur un projet et j'ai un petit problème.Je possède un multi-ports usb sur lequel je raccorde 3 webcams.J'ai réalisé Problème écriture fichier image [ par Supopste ] Bonjour,j'ai actuellement un petit problème qui me bloque pour avancer dans mon programme pourtant, cela semble facile à résoudre, mais comment?Je dev Prendre la main sur une image [ par gretata ] bonjour a tous !!je voudrais savoir si été possible en c# de prendre la main sur une image ou autres, je m'explique,j'ai une image dans une form, et j personnaliser datagrid avec image binaire [ par lolo70000 ] Salut tt le monde !je veux personnaliser mon datagrid en affichant tous les champs de ma table y compris un champ image stocke en binaire au niveau de Convertir un object contennant une image JPEG en image [ par fcolo ] Bonjour,J'ai un ActiveX de capture de flux vidéo.Cet ActiveX possède une fonction getCurrentImage() définie comme ceci:void AxAxisMediaControl.GetCurr Créer une image à partir d'un controle [ par bossun ] Salut,Je voudrais créer une image depuis un controle et l'exporter dans un jpg, bmp, ou png (c'est égal)Dans mon cas il s'agit d'un graphique créé à p Treeview et image [ par RMI ] Bonjour, J'ai un problème sur un formulaire sous VS2005. En effet ce formulaire contient un treeview auquel j'affecte aux noeuds des images via une im


Nos sponsors

Sondage...

CalendriCode

Juillet 2009
LMMJVSD
  12345
6789101112
13141516171819
20212223242526
2728293031  

Consulter la suite du CalendriCode

Téléchargements

Logiciels à télécharger sur le même thème :

Comparez les prix Nouvelle version

Photothèque Nouveau !



Développement réalisé par Nicolas SOREL (Nix) avec l'aide de : Cyril DURAND et Emmanuel (EBArtSoft), Merci à Vincent pour ses précieux conseils
CodeS-SourceS.com© Toute reproduction même partielle est interdite sauf accord écrit du Webmaster
CodeS-SourceS.com© est une marque déposée tous droits réservés
Temps d'éxécution de la page : 0,484 sec

Google Coop CodeS-SourceS Google Coop CodeS-SourceS


Certaines images présentes sur le site (notament certains avatars) sont issues des collections IconShock, donc si vous souhaitez utiliser ces icons vous devez les acheter, ne les copiez pas et ne utilisez pas dans vos sites et applications sans les avoir commandé.