Как ускорить мой порог цвета в C #

1

Я работаю над отслеживанием объектов на основе цвета, и я использовал библиотеку EmguCV для порога моего цветного изображения для двоичного черно-белого изображения. Порог сам был довольно быстрым, 50 мс на 320х240. Я использую цветовое пространство RG Chromaticity, поэтому есть некоторые необходимые вычисления.

Теперь я пытаюсь ускорить его с помощью указателей, но результат очень похож на то, что я сделал с emguCV (около 50 мс на изображение).

Я хочу спросить, есть ли какой-то специалист, который может мне помочь, что я делаю неправильно. Вот мой короткий фрагмент кода моей реализации цветового порога. Он основан на этом http://www.bobpowell.net/onebit.htm.

public static Bitmap ThresholdRGChroma(Bitmap original, double angleMin,
            double angleMax, double satMin, double satMax)
{
    Bitmap bimg = new Bitmap(original.Width, original.Height, PixelFormat.Format1bppIndexed);

    BitmapData imgData = original.LockBits(new Rectangle(0, 0, original.Width, original.Height), ImageLockMode.ReadOnly, original.PixelFormat);
    BitmapData bimgData = bimg.LockBits(new Rectangle(0, 0, bimg.Width, bimg.Height), ImageLockMode.ReadWrite, bimg.PixelFormat);

    int pixelSize = 3;

    double r, g, angle, sat;

    unsafe
    {
        byte* R, G, B;
        byte* row;
        int RGBSum;

        for (int y = original.Height - 1; y >= 0; y--)
        {
            row = (byte*)imgData.Scan0 + (y * imgData.Stride);

            for (int x = original.Width - 1; x >= 0; x--)
            {
                // get rgb values
                B = &row[x * pixelSize];
                G = &row[x * pixelSize + 1];
                R = &row[x * pixelSize + 2];

                RGBSum = *R + *G + *B;

                if (RGBSum == 0)
                {
                    SetIndexedPixel(x, y, bimgData, false);
                    continue;
                }

                //calculate r ang g for rg chroma color space
                r = (double)*R / RGBSum;
                g = (double)*G / RGBSum;

                //and angle and saturation
                angle = GetAngleRad(r, g) * (180.0 / Math.PI);
                sat = Math.Sqrt(Math.Pow(g, 2) + Math.Pow(r, 2));

                //conditions to set pixel black or white
                if ((angle >= angleMin && angle <= angleMax) && (sat >= satMin && sat <= satMax))
                    SetIndexedPixel(x, y, bimgData, true);
                else
                    SetIndexedPixel(x, y, bimgData, false);
            }

        }
    }

    bimg.UnlockBits(bimgData);
    original.UnlockBits(imgData);

    return bimg;
}

private unsafe static void SetIndexedPixel(int x, int y, BitmapData bmd, bool pixel)
{
    int index = y * bmd.Stride + (x >> 3);
    byte* p = (byte*)bmd.Scan0.ToPointer();
    byte mask = (byte)(0x80 >> (x & 0x7));

    if (pixel)
        p[index] |= mask;
    else
        p[index] &= (byte)(mask ^ 0xff);
}

private static double GetAngleRad(double x, double y)
{
    if (x - _rgChromaOriginX == 0)
        return 0.0;
    double angle = Math.Atan((y - _rgChromaOriginY) / (x - _rgChromaOriginX)); // 10ms

    if (x < _rgChromaOriginX && y > _rgChromaOriginY)
        angle = angle + Math.PI;
    else if (x < _rgChromaOriginX && y < _rgChromaOriginY)
        angle = angle + Math.PI;
    else if (x > _rgChromaOriginX && y < _rgChromaOriginY)
        angle = angle + 2 * Math.PI;

    return angle;
}
Теги:
image-processing
threshold

3 ответа

1
Лучший ответ

Вы делаете много ненужной математики для каждого пикселя, вычисляя точные значения только для проверки, находятся ли они в некоторых пределах. Вы можете упростить сравнение, предварительно вычислив некоторые настройки пределов.

Самая простая замена - это насыщение. Вы делаете квадратный корень, который вы можете избежать, возведя квадрат вместо этого.

double satMin2 = satMin*satMin;
double satMax2 = satMax*satMax;
// ...
sat2 = g*g + r*r;

//conditions to set pixel black or white
if ((angle >= angleMin && angle <= angleMax) && (sat2 >= satMin2 && sat <= satMax2))

Аналогичный трюк можно использовать с углом. Вместо того, чтобы вычислять угол с помощью Math.Atan, выясните, какие эти пределы соответствуют вашим диапазонам r и g.

  • 0
    Большое спасибо. Только изменение насыщенности и ее диапазонов ускоряет порог примерно на 20 мс, от 50 до 30. Я также нашел эту прекрасную статью о RG-цветности, где они показывают, как ускорить его еще больше, как вы сказали, удалив Atan. Я выложу исправленную версию с порогом 320x240 на моем компьютере за 18 мс.
0

Для полноты здесь представлена ​​модифицированная версия изображения порога в цветовом пространстве RG Chromaticity более чем в 2 раза быстрее, чем версия в моем Вопросе.

public static Bitmap ThresholdRGChroma(Bitmap original, Rectangle roi, double angle,
            double width, double satMin, double satMax)
        {
            Bitmap bimg = new Bitmap(original.Width, original.Height, PixelFormat.Format1bppIndexed);

        BitmapData imgData = original.LockBits(new Rectangle(0, 0, original.Width, original.Height), ImageLockMode.ReadOnly, original.PixelFormat);
        BitmapData bimgData = bimg.LockBits(new Rectangle(0, 0, bimg.Width, bimg.Height), ImageLockMode.ReadWrite, bimg.PixelFormat);

        int pixelSize = 3;

        double r, g, sat, m;

        double satMin2 = satMin * satMin;
        double satMax2 = satMax * satMax;

        double cr = Math.Sin((2 * Math.PI * angle) / 360.0);
        double cg = Math.Cos((2 * Math.PI * angle) / 360.0);

        // Instead of (Math.Cos(2 * width / 180.0) + 1) / 2.0 I'm using pre-calculated <1; 0> values.
        double w2 = -width;

        unsafe
        {
            byte* R, G, B;
            byte* row;
            int RGBSum;

            for (int y = original.Height - 1; y >= 0; y--)
            {
                row = (byte*)imgData.Scan0 + (y * imgData.Stride);

                for (int x = original.Width - 1; x >= 0; x--)
                {
                    B = &row[x * pixelSize];
                    G = &row[x * pixelSize + 1];
                    R = &row[x * pixelSize + 2];

                    RGBSum = *R + *G + *B;

                    if (RGBSum == 0)
                    {
                        SetIndexedPixel(x, y, bimgData, false);
                        continue;
                    }

                    r = (double)*R / RGBSum - _rgChromaOriginX;
                    g = (double)*G / RGBSum - _rgChromaOriginY;

                    m = cr * r + cg * g;
                    sat = r * r + g * g;

                    if (m > 0 && m * m > w2 * w2 * sat && sat >= satMin2 && sat <= satMax2)
                        SetIndexedPixel(x, y, bimgData, true);
                    else
                        SetIndexedPixel(x, y, bimgData, false);
                }
            }
        }

        bimg.UnlockBits(bimgData);
        original.UnlockBits(imgData);

        return bimg;
    }
0

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

  • 0
    Спасибо за ответ, но я уже использую очень похожий подход. Вы сложнее, хотя.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню