Pregunta

Estoy trabajando en el seguimiento de los objetos basados en el color y yo estaba usando la biblioteca de EMGUCV para umbral de imagen en color en blanco y negro binario.El umbral en sí era bastante rápido, 50 ms en la imagen de 320x240.Estoy usando un espacio de color de cromaticidad RG, por lo que hay algunos cálculos necesariamente.

Ahora estoy tratando de acelerarlo con los punteros, pero el resultado es muy similar con lo que hice con EMGUCV (alrededor de 50 ms por imagen).

Quiero preguntar, si hay algún experto que pueda ayudarme, lo que estoy haciendo mal.Aquí está mi fragmento de código corto de mi implementación de umbrales de color.Se basa en este 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;
}
¿Fue útil?

Solución

Estás haciendo muchas matemáticas innecesarias para cada píxel, calculando los valores exactos solo para verificar si están dentro de algunos límites.Puede simplificar las comparaciones por precomputación de algunos ajustes a los límites.

La sustitución más fácil es para la saturación.Estás haciendo una raíz cuadrada que puedes evitar al cuadrillar los límites en su lugar.

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))

Se puede usar un truco similar con el ángulo.En lugar de calcular el ángulo con Math.Atan, averigüe qué esos límites equivalen a en sus rangos R y G.

Otros consejos

No estoy seguro, si será más rápido.Pero aquí escribí un consejo, cómo hacerloTrabaja con mapas de bits muy rápido.

Por completo, aquí se modifica la versión de la imagen de umbral en el espacio de color de la cromaticidad RG, es más de 2 veces más rápido que la versión en mi pregunta.

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

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top