Как я могу ускорить этот класс гистограммы? - PullRequest
2 голосов
/ 19 июня 2009

Предполагается, что для расчета гистограммы 8-битного изображения в градациях серого. С тестовым растровым изображением 1024x770 время CreateTime составляет около 890 мс. Как я могу сделать это (путь, путь) быстрее?

РЕДАКТИРОВАТЬ: я должен отметить, что это на самом деле еще не вычисляет гистограмму, он только получает значения из растрового изображения. Поэтому мне действительно следовало бы спросить, каков самый быстрый способ извлечь все значения пикселей из 8-битного изображения в градациях серого?

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

Ответы [ 3 ]

5 голосов
/ 19 июня 2009

Основной алгоритм гистограммы выглядит примерно так:

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

Алгоритм суммирует, сколько пикселей со значением 0 у вас есть, сколько со значением = 1 и так далее. Основная идея состоит в том, чтобы использовать значение пикселя в качестве индекса для позиции гистограммы, где вы будете считать.

У меня есть одна версия этого алгоритма, написанная для C # с использованием неуправляемого кода (это быстро). Я не знаю, быстрее ли это, чем ваш, но вы можете взять его и протестировать, вот код:

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

Надеюсь, я смогу вам помочь.

3 голосов
/ 19 июня 2009

Вы захотите использовать метод Bitmap.LockBits для доступа к данным пикселей. Это является хорошей ссылкой на процесс. По сути, вам нужно будет использовать код unsafe для перебора данных растрового изображения.

1 голос
/ 13 апреля 2012

Вот копия / вставляемая версия функции, с которой я столкнулся, основываясь на этой теме.

Небезопасный код ожидает, что растровое изображение будет Format24bppRgb, и если это не так, оно преобразует растровое изображение в этот формат и будет работать с клонированной версией.

Обратите внимание, что вызов метода image.Clone () будет сброшен, если вы передадите растровое изображение в формате индексированного пикселя, например Format4bppIndexed.

Требуется ~ 200 мс, чтобы получить гистограмму из изображения 9100x2048 на моем компьютере разработчика.

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