Question

Ceci est censé calculer l'histogramme d'une image en niveaux de gris à 8 bits. Avec un bitmap de test 1024x770, CreateTime se termine à environ 890 ms. Comment puis-je aller plus vite? (Chemin, chemin)?

EDIT: Je devrais mentionner que ceci ne calcule pas encore l'histogramme, il extrait seulement les valeurs du bitmap. J'aurais donc dû me demander quel est le moyen le plus rapide de récupérer toutes les valeurs de pixels 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; }
}
Était-ce utile?

La solution

L'algorithme d'histogramme de base ressemble à 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] ]++;
   }
}

L’algorithme additionne le nombre de pixels de valeur 0, le nombre de pixels de valeur = 1, etc. L'idée de base est d'utiliser la valeur en pixels comme index de la position de l'histogramme où vous allez compter.

J'ai une version de cet algorithme écrite pour C # en utilisant du code non managé (ce qui est rapide). Je ne sais pas si est plus rapide que votre, mais n'hésitez pas à le prendre et à le tester, 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 pouvoir vous aider.

Autres conseils

Vous voudrez utiliser la méthode Bitmap.LockBits pour accéder aux données de pixels. Ceci constitue une bonne référence sur le processus. Pour l'essentiel, vous devrez utiliser du code unsafe pour parcourir les données bitmap.

Voici une version copier / coller de la fonction que j'ai créée basée sur ce fil.

Le code non sécurisé s'attend à ce que le bitmap soit au Format24bppRgb. Sinon, il convertira le bitmap dans ce format et fonctionnera sur la version clonée.

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

Il faut environ 200 ms pour obtenir un histogramme d'une image 9100x2048 sur ma machine de développement.

    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;
    }
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top