Pregunta

Se supone que esto calcula el histograma de una imagen en escala de grises de 8 bits. Con un mapa de bits de prueba de 1024x770, CreateTime termina en alrededor de 890 ms. ¿Cómo puedo hacer que esto vaya (camino, camino) más rápido?

EDITAR: Debo mencionar que esto todavía no calcula el histograma, solo saca los valores del mapa de bits. Así que realmente debería haber preguntado, ¿cuál es la forma más rápida de recuperar todos los valores de píxeles de una imagen en escala de grises de 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; }
}
¿Fue útil?

Solución

El algoritmo de histograma básico es algo así como:

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] ]++;
   }
}

El algoritmo suma cuántos píxeles con valor 0 tiene, cuántos con valor = 1 y así sucesivamente. La idea básica es utilizar el valor de píxel como índice de la posición del histograma donde contará.

Tengo una versión de este algoritmo escrita para C # usando código no administrado (que es rápido) No sé si es más rápido que tu, pero no dudes en tomarlo y probarlo, aquí está el código:

    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;
    }

Espero poder ayudarte.

Otros consejos

Deberá utilizar el método Bitmap.LockBits para acceder a los datos de píxeles. Esto es una buena referencia sobre el proceso. Esencialmente, necesitará usar el código inseguro para iterar sobre los datos del mapa de bits.

Aquí hay una versión copiable / pegable de la función que he creado con base en este hilo.

El código inseguro espera que el mapa de bits sea Format24bppRgb, y si no es así, convertirá el mapa de bits a ese formato y funcionará en la versión clonada.

Tenga en cuenta que la llamada a image.Clone () se lanzará si pasa un mapa de bits con un formato de píxel indexado, como Format4bppIndexed.

Toma ~ 200 ms para obtener un histograma de una imagen 9100x2048 en mi máquina de desarrollo.

    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;
    }
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top