Accueil > > > TRAITEMENT D'IMAGE EN NOIR ET BLANC
TRAITEMENT D'IMAGE EN NOIR ET BLANC
Information sur la source
Description
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=8 d3eb481-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
============================================= Quel ques 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
Sources du même auteur
Sources de la même categorie
Commentaires et avis
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ème est le suivant: je traite une image Bitmap et je veux pouvoir transformer tous les pixels blanc de cette image en pixel tran
comment enlever le fond blanc d'une image avec visual c# [ par ami7 ]
Salut à tous, voilà j'ai ajouter à mon application un bouton , j'ai changé la propriété Image de ce dernier mais le bouton parait une image a un fon
Comment attribué un entier(variable) dans le nom d'un Button? [ par darkdog85 ]
Bonjour, Je ne sais pas si mon titre est très clair alors je vais directement passé a mon exemple : [code=cs] int i; for (i=1;i < 11;i++) { button
Afficher image BMP d'uni fichier XML [ par thib89 ]
Bonsoir, Excuser moi de vous déranger, si quelqu'un pouvais m'aider se serait vraiment bienvenu. Je programme sous VS 2010 en C#. J'ai un fichier XM
comment dessiner des objets graphiques sur une image [ par ami7 ]
bonsoir, j'utilise un code c# qui permet de dessiner des objets ghraphiques (rectangle , ellipse, ligne...) le question c'est que je veut tout d'abord
Trouver tous les pixels noire dans une image binaire trés grande [ par issam414 ]
Bonjour à tous, je débute dans la programmation en c# et je suis déjà perdu, pourtant ça me parait simple comme question [^^peur] . alors, j'ai une i
Application du modèle de Markov en utlisant logistic Map [ par chattachat ]
Salut à tous .Mon problème est comment crypter une image à l'aide de modèle de markov premier ordre en utilisant une fonction logistique Chaos . Ce te
Definir le chemin d'une pictureBox dans le code [ par ahorel ]
Bonjour, Je remercie les personnes qui m'ont aidé pour insérer les images dans mon windows form. J'utilise ce chemin pour les insérer: string imgPat
Extraction d'images et de pages - PDF [ par mbeditions ]
Bonjour à tous, Je développe actuellement un logiciel dont l'une des fonctionnalités est l'extraction des photos contenues dans un fichier PDF. Parti
|
Derniers Blogs
[TECHDAYS 2012] SESSION WEBMATRIX 2 : LE COUTEAU SUISSE GRATUIT POUR VOS DéVELOPPEMENTS WEB - SLIDES[TECHDAYS 2012] SESSION WEBMATRIX 2 : LE COUTEAU SUISSE GRATUIT POUR VOS DéVELOPPEMENTS WEB - SLIDES par gpommier
Suite à la session que j'ai présenté sur WebMatrix 2, vous pouvez trouver les slides ici, ainsi que les démos en packages nuget : démos1 et démos2 J'en profite pour remercier chaleureusement tous ceux qui sont venus très nombreux à cette sess...
Cliquez pour lire la suite de l'article par gpommier [SHAREPOINT] LES SESSIONS TECHDAYS 2012.[SHAREPOINT] LES SESSIONS TECHDAYS 2012. par Patrick Guimonet
Voici donc pour ceux qui n'ont pas pu venir, ou ceux qui n'ont pas pu toutes les suivre la liste des sessions SharePoint aux TechDays 2012, que je mettrais à jour dès que les liens des vidéo seront disponibles. Ou ici : http...
Cliquez pour lire la suite de l'article par Patrick Guimonet TECHDAYS PARIS 2012 : SESSION PLEINIèRE JOUR 3TECHDAYS PARIS 2012 : SESSION PLEINIèRE JOUR 3 par ROMELARD Fabrice
Speaker: Bernard Ourghanlian Cette session est comme chaque jour transmise en live par BrainSonic, et j'ai donc suivi cette troisième pleinière par ce moyen sur mon iPad . Elle est dédiée comme chaque année à la mise en perspective de l'é...
Cliquez pour lire la suite de l'article par ROMELARD Fabrice MISHRA READER : UN LECTEUR RSS TRèS ZUNE STYLE EN OPEN SOURCE !MISHRA READER : UN LECTEUR RSS TRèS ZUNE STYLE EN OPEN SOURCE ! par Vko
Hier durant une session dédiée aux Techdays 2012, j'ai eu le plaisir d'annoncer la sortie de la Béta 2 de Mishra Reader. C'est quoi ? Pour les utilisateurs, c'est une vraie expérience de lecture de flux RSS sur Windows. Rien à voir avec les produit...
Cliquez pour lire la suite de l'article par Vko [FRAMEWORK 4] LES TASKS ET LE THREAD UI[FRAMEWORK 4] LES TASKS ET LE THREAD UI par fathi
Je viens de passer quelques temps au TechDay's et j'ai pu voir pas mal de session intéressante. Par contre une chose m'a un peu étonné lors de certaines de ces sessions qui abordaient les améliorations du framework .NET (donc le 4.5) : en gros, bea...
Cliquez pour lire la suite de l'article par fathi
Logiciels
Academy System (17.2.1.0)ACADEMY SYSTEM (17.2.1.0)Logiciel de gestion des établissements.
- élèves/étudiants (inscription, dossier, absence...)
-... Cliquez pour télécharger Academy System Easy-Planning (1.0.0.1)EASY-PLANNING (1.0.0.1)Basé sur les mêmes principes que MyPlanning, Easy-Planning permet de créer des plannings sous la ... Cliquez pour télécharger Easy-Planning COLLECTOR PLUS (3.00B)COLLECTOR PLUS (3.00B)COLLECTOR PLUS version 3.00B est un logiciel utilisant une base de données alimentée par :
- L... Cliquez pour télécharger COLLECTOR PLUS PONAMEDIA PREMIUM - HELLLOOO FLASH DEMO (V7.4)PONAMEDIA PREMIUM - HELLLOOO FLASH DEMO (V7.4)PONAMEDIA TV DEVIENS HELLLOOO FLASH
LA TV SUR VOTRE ORDINATEUR.
Toute une plateforme Multi... Cliquez pour télécharger PONAMEDIA PREMIUM - HELLLOOO FLASH DEMO LettresFaciles 2011 (8.0.0.1)LETTRESFACILES 2011 (8.0.0.1)LettresFaciles est un logiciel facilitant la création et la rédaction de lettres types.
Son inte... Cliquez pour télécharger LettresFaciles 2011
|