2010-06-25 5 views
6

Existe-t-il un moyen efficace d'ajuster le contraste d'une image en C#?Ajuster efficacement le contraste d'une image en C#

J'ai vu this article qui préconise de faire une opération par pixel. Pas rapide. J'utilise déjà des matrices de couleurs dans des endroits et je les trouve rapides. Y a-t-il un moyen d'ajuster le contraste en les utilisant? (Note: This guy se trompe.)

J'utilise aussi EmguCV. Je remarque que OpenCV (dont Emgu enveloppe) seems to have a contrast function - Y at-il un moyen d'y accéder via Emgu? Pour le moment, tout ce que je peux faire dans Emgu est de normaliser l'histogramme, ce qui change le contraste, mais pas avec un degré de contrôle de ma part.

Vous avez des idées?

+0

Pourquoi dites-vous que " ce mec "se trompe? – DkAngelito

+0

@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. –

+0

Le lien "Ce mec" est maintenant cassé. – Chad

Répondre

26

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.

+2

Je suppose que vous êtes responsable de nommer des choses là où vous travaillez? –

+0

À droite, comme * I * ont un travail. :) – MusiGenesis

+0

+1 bravo

2

@MusiGenesis,

Je voulais juste noter que j'utilisé cette méthode pour un éditeur d'image que j'ai écrit.Il fonctionne bien, mais parfois cette méthode déclenche une AccessViolationException sur cette ligne:

byte B = row[columnOffset]; 

Je compris que c'était parce qu'il n'y avait pas de normalisation bitdepth, donc si une image était de couleur 32 bits je recevais cette erreur. J'ai donc changé cette ligne:

BitmapData data = NewBitmap.LockBits(new Rectangle(0, 0, NewBitmap.Width, NewBitmap.Height), ImageLockMode.ReadWrite, NewBitmap.PixelFormat); 

à:

BitmapData data = NewBitmap.LockBits(new Rectangle(0, 0, NewBitmap.Width, NewBitmap.Height), ImageLockMode.ReadWrite, PixelFormat.Format32bppRgb); 

Hope this helps comme il semble avoir éradiqué mon problème.

Merci pour la publication.

Jib

+1

Vous pouvez utiliser' PixelFormat.Format32bppArgb' à la place, puisqu'il s'agit du format bitmap .NET par défaut. – MusiGenesis

-1

Je suis un peu en retard, mais utilise une implémentation de la matrice de couleur que ceux-ci seront optimisés pour de telles transformations et est beaucoup plus facile que de manipuler les pixels vous: http://www.geekpedia.com/tutorial202_Using-the-ColorMatrix-in-Csharp.html

+1

C'est la même erreur que Bob fait dans l'article auquel je suis lié dans ma question: Les matrices de couleurs ne sont pas adaptées aux altérations de contraste. C'est parce que les changements sont faits par rapport au gris moyen, pas noir ou blanc. –

+0

Juste pour corriger cela, 'nouveau flotteur [] {0.1f, 0, 0, 0, 0}, nouveau flotteur [] {0, 0.1f, 0, 0, 0}, nouveau flotteur [] {0 , 0, 0.1f, 0, 0}, nouveau flotteur [] {0, 0, 0, 1, 0}, nouveau flotteur [] {0.8f, 0.8f, 0.8f, 0, 1} ' le travail dans l'exemple ci-dessus. La cinquième rangée ajoute un décalage qui peut être ajusté. Ceci (0.8) donne un effet "high-Key". 0,2 est un contraste très faible. – nick66

Questions connexes