2009-06-18 9 views
2

Ceci est supposé calculer l'histogramme d'une image en niveaux de gris 8 bits. Avec un bitmap de test 1024x770, CreateTime se termine à environ 890ms. Comment puis-je faire cela (chemin, façon) plus vite?Comment puis-je accélérer cette classe d'histogramme?

EDIT: Je devrais mentionner que cela ne calcule pas réellement l'histogramme, il obtient seulement les valeurs hors de la bitmap. Donc, je devrais vraiment demandé, quel est le moyen le plus rapide pour récupérer toutes les valeurs de pixels à partir d'une image en niveaux de gris 8 bits?

public class Histogram { 

    private static int[,] values; 

    public Histogram(Bitmap b) { 
     var sw = Stopwatch.StartNew(); 
     values = new int[b.Width, b.Height]; 

     for (int w = 0; w < b.Width; ++w) { 
      for (int h = 0; h < b.Height; ++h) { 
       values[w, h] = b.GetPixel(w, h).R; 
      } 
     } 

     sw.Stop(); 
     CreateTime = (sw.ElapsedTicks/
      (double)Stopwatch.Frequency) * 1000; 
    } 

    public double CreateTime { get; set; } 
} 
+0

b est-il en mémoire? –

+0

Jetez un oeil au lien posté par McWafflestix. J'avais l'habitude de le faire en C++ mais quand j'ai essayé de migrer ma lib vers C#, c'est sur ce site que j'ai appris les outils C# pour un accès rapide aux pixels. – Andres

Répondre

5

L'algorithme d'histogramme de base est quelque chose comme:

int[] hist = new hist[256]; 
//at this point dont forget to initialize your vector with 0s. 

for(int i = 0; i < height; ++i) 
{ 
    for(int j = 0 ; j < widthl ++j) 
    { 
     hist[ image[i,j] ]++; 
    } 
} 

Les sommes algorithme combien de pixels avec la valeur 0 vous avez, combien avec la valeur = 1 et ainsi de suite. L'idée de base est d'utiliser la valeur de pixel comme index à la position de l'histogramme où vous comptez.

J'ai une version de cet algorithme écrit pour C# en utilisant le code non géré (ce qui est rapide) Je ne sais pas si est plus rapide que votre mais ne hésitez pas à le prendre et le test, voici le code:

public void Histogram(double[] histogram, Rectangle roi) 
    { 
     BitmapData data = Util.SetImageToProcess(image, roi); 

     if (image.PixelFormat != PixelFormat.Format8bppIndexed) 
      return; 

     if (histogram.Length < Util.GrayLevels) 
      return; 

     histogram.Initialize(); 
     int width = data.Width; 
     int height = data.Height; 
     int offset = data.Stride - width; 

     unsafe 
     { 
      byte* ptr = (byte*)data.Scan0; 

      for (int y = 0; y < height; ++y) 
      { 
       for (int x = 0; x < width; ++x, ++ptr) 
        histogram[ptr[0]]++; 

       ptr += offset; 
      } 
     } 
     image.UnlockBits(data);   
    } 

    static public BitmapData SetImageToProcess(Bitmap image, Rectangle roi) 
    { 
     if (image != null) 
      return image.LockBits(
       roi, 
       ImageLockMode.ReadWrite, 
       image.PixelFormat); 

     return null; 
    } 

J'espère que je pourrais vous aider.

+0

Juste à propos de l'histogramme, et à droite, que la lecture des pixels est la chose à optimiser. –

3

Vous devez utiliser la méthode Bitmap.LockBits pour accéder aux données de pixels. This est une bonne référence sur le processus. Essentiellement, vous devrez utiliser le code unsafe pour itérer sur les données bitmap.

1

Voici une copie/version modifiable de la fonction que j'ai trouvée avec ce fil.

Le code non sécurisé s'attend à ce que le bitmap soit Format24bppRgb, et dans le cas contraire, il convertit le bitmap à ce format et fonctionne sur la version clonée.

Notez que l'appel à image.Clone() sera lancé si vous transmettez un bitmap en utilisant un format de pixel indexé, tel que Format4bppIndexed.

Prend ~ 200ms pour obtenir un histogramme à partir d'une image 9100x2048 sur ma machine dev.

private long[] GetHistogram(Bitmap image) 
    { 
     var histogram = new long[256]; 

     bool imageWasCloned = false; 

     if (image.PixelFormat != PixelFormat.Format24bppRgb) 
     { 
      //the unsafe code expects Format24bppRgb, so convert the image... 
      image = image.Clone(new Rectangle(0, 0, image.Width, image.Height), PixelFormat.Format24bppRgb); 
      imageWasCloned = true; 
     } 

     BitmapData bmd = null; 
     try 
     { 
      bmd = image.LockBits(new Rectangle(0, 0, image.Width, image.Height), ImageLockMode.ReadOnly, 
           PixelFormat.Format24bppRgb); 

      const int pixelSize = 3; //pixels are 3 bytes each w/ Format24bppRgb 

      //For info on locking the bitmap bits and finding the 
      //pixels using unsafe code, see http://www.bobpowell.net/lockingbits.htm 
      int height = bmd.Height; 
      int width = bmd.Width; 
      int rowPadding = bmd.Stride - (width * pixelSize); 
      unsafe 
      { 
       byte* pixelPtr = (byte*)bmd.Scan0;//starts on the first row 
       for (int y = 0; y < height; ++y) 
       { 
        for (int x = 0; x < width; ++x) 
        { 
         histogram[(pixelPtr[0] + pixelPtr[1] + pixelPtr[2])/3]++; 
         pixelPtr += pixelSize;//advance to next pixel in the row 
        } 
        pixelPtr += rowPadding;//advance ptr to the next pixel row by skipping the padding @ the end of each row. 
       } 
      } 
     } 
     finally 
     { 
      if (bmd != null) 
       image.UnlockBits(bmd); 
      if (imageWasCloned) 
       image.Dispose(); 
     } 

     return histogram; 
    } 
Questions connexes