Как ускорить мой порог цвета в C # - PullRequest
1 голос
/ 08 марта 2012

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

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

Я хочу спросить, есть ли какой-нибудь эксперт, который может мне помочь, что я делаю неправильно.Вот мой короткий фрагмент кода моей реализации установки порогового цвета.Он основан на этом 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;
}

Ответы [ 3 ]

1 голос
/ 08 марта 2012

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

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

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 голосов
/ 11 марта 2012

Для полноты, вот измененная версия изображения порогового значения в цветовом пространстве RG Цветность, это более чем в 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);</p>

<code>        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;
    }
</code>
0 голосов
/ 08 марта 2012

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

...