Si le code de cet exemple fonctionne pour vous, vous pouvez l'augmenter massivement (par ordre de grandeur) en utilisant Bitmap.LockBits
, ce qui renvoie un objet BitmapData
qui permet d'accéder aux données de pixel du Bitmap via des pointeurs. Il existe de nombreux exemples sur le web et sur StackOverflow qui montrent comment utiliser LockBits.
Bitmap.SetPixel()
et sont les méthodes les plus lentes connues de l'humanité, et elles utilisent toutes les deux la classe Color
, qui est la classe la plus lente connue de l'humanité. Ils auraient dû être nommés Bitmap.GetPixelAndByGodYoullBeSorryYouDid()
et Bitmap.SetPixelWhileGettingCoffee
comme un avertissement aux développeurs imprudents.
Mise à jour: Si vous allez modifier le code dans cet échantillon, notez que ce morceau:
System.Drawing.Bitmap TempBitmap = Image;
System.Drawing.Bitmap NewBitmap = new System.Drawing.Bitmap(TempBitmap.Width,
TempBitmap.Height);
System.Drawing.Graphics NewGraphics =
System.Drawing.Graphics.FromImage(NewBitmap);
NewGraphics.DrawImage(TempBitmap, new System.Drawing.Rectangle(0, 0,
TempBitmap.Width, TempBitmap.Height),
new System.Drawing.Rectangle(0, 0, TempBitmap.Width, TempBitmap.Height),
System.Drawing.GraphicsUnit.Pixel);
NewGraphics.Dispose();
peut être remplacé par ceci:
Bitmap NewBitmap = (Bitmap)Image.Clone();
Mise à jour 2: Voici la version LockBits de la méthode AdjustContrast (avec quelques autres améliorations de vitesse):
public static Bitmap AdjustContrast(Bitmap Image, float Value)
{
Value = (100.0f + Value)/100.0f;
Value *= Value;
Bitmap NewBitmap = (Bitmap)Image.Clone();
BitmapData data = NewBitmap.LockBits(
new Rectangle(0, 0, NewBitmap.Width, NewBitmap.Height),
ImageLockMode.ReadWrite,
NewBitmap.PixelFormat);
int Height = NewBitmap.Height;
int Width = NewBitmap.Width;
unsafe
{
for (int y = 0; y < Height; ++y)
{
byte* row = (byte*)data.Scan0 + (y * data.Stride);
int columnOffset = 0;
for (int x = 0; x < Width; ++x)
{
byte B = row[columnOffset];
byte G = row[columnOffset + 1];
byte R = row[columnOffset + 2];
float Red = R/255.0f;
float Green = G/255.0f;
float Blue = B/255.0f;
Red = (((Red - 0.5f) * Value) + 0.5f) * 255.0f;
Green = (((Green - 0.5f) * Value) + 0.5f) * 255.0f;
Blue = (((Blue - 0.5f) * Value) + 0.5f) * 255.0f;
int iR = (int)Red;
iR = iR > 255 ? 255 : iR;
iR = iR < 0 ? 0 : iR;
int iG = (int)Green;
iG = iG > 255 ? 255 : iG;
iG = iG < 0 ? 0 : iG;
int iB = (int)Blue;
iB = iB > 255 ? 255 : iB;
iB = iB < 0 ? 0 : iB;
row[columnOffset] = (byte)iB;
row[columnOffset + 1] = (byte)iG;
row[columnOffset + 2] = (byte)iR;
columnOffset += 4;
}
}
}
NewBitmap.UnlockBits(data);
return NewBitmap;
}
REMARQUE: ce code requiert using System.Drawing.Imaging;
dans vos instructions d'utilisation de classe et requiert que l'option allow unsafe code
du projet soit cochée (dans l'onglet Propriétés de construction du projet).
L'une des raisons pour lesquelles GetPixel et SetPixel sont si lents pour les opérations pixel par pixel est que le surcoût de l'appel de méthode commence à devenir un énorme facteur. Normalement, mon exemple de code ici serait considéré comme un candidat pour le refactoring, puisque vous pourriez écrire vos propres méthodes SetPixel et GetPixel qui utilisent un objet BitmapData existant, mais le temps de calcul pour les maths dans les fonctions serait très petit par rapport à la méthode de chaque appel. C'est pourquoi j'ai également supprimé les appels Clamp
dans la méthode d'origine. Une autre façon d'accélérer cela serait de simplement en faire une fonction "destructrice", et de modifier le paramètre Bitmap passé au lieu de faire une copie et de retourner la copie modifiée.
Pourquoi dites-vous que " ce mec "se trompe? – DkAngelito
@DkAngelito Wow, c'est depuis longtemps. Si la mémoire est bonne, l'approche ColorMatrix ne peut pas décaler les valeurs/vers le point médian, ce qui est vraiment un ajustement de contraste. Si cela aide, la réponse acceptée par MusicGenesis ci-dessous semble avoir attiré un consensus comme optimal. –
Le lien "Ce mec" est maintenant cassé. – Chad