How to speed up my color threshold in C #

I am working on color-based object tracking, and I used the EmguCV library to threshold my color image for a binary black and white image. The threshold itself was pretty fast, 50 ms at 320x240. I use the RG Chromaticity color space, so there are some necessary calculations.

Now I'm trying to speed it up with pointers, but the result is very similar to what I did with emguCV (about 50 ms per image).

I want to ask if there is any specialist who can help me, what am I doing wrong. Here is my short code snippet of my implementation of the color threshold. It is based on this 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; } 
+1
source share
3 answers

You do a lot of unnecessary math for each pixel, calculating exact values ​​only to check if they are within certain limits. You can simplify the comparison by pre-calculating some limit settings.

The simplest replacement is saturation. You make a square root that you can avoid by building a square instead.

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

A similar trick can be used with an angle. Instead of calculating the angle using Math.Atan, find out what these limits correspond to your r and g ranges.

+1
source

I am not sure if it will be faster. But here I wrote a hint how to work with raster images very quickly.

0
source

For completeness, a modified version of the threshold image in the RG Chromaticity color space is presented more than 2 times faster than the version in my Question.

  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
source

Source: https://habr.com/ru/post/910241/


All Articles