2012-03-08 72 views
1

我正在跟蹤基於顏色的對象,並且我正在使用EmguCV庫將我的彩色圖像限制爲二進制黑白圖像。閾值本身非常快,在320x240的圖像上爲50ms。我使用RG色度色彩空間,所以有一些必要的計算。如何加快我在C#中的顏色閾值#

現在我試圖用指針來加速它,但結果與我用emguCV做的(每幅圖像大約50ms)非常相似。

我想問問,如果有一些專家能夠幫助我,我做錯了什麼。這是我的顏色閾值實現的短代碼片段。它基於這個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

你正在爲每個像素做很多不必要的數學運算,只計算確切的數值來檢查它們是否在一定範圍內。您可以通過預先計算一些限制的調整來簡化比較。

最簡單的替代是飽和度。你正在做一個平方根,你可以通過平方的限制來避免。

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. 我也發現這個不錯的[論文](http://www.lucs.lu.se/LUCS/M012/Minor12.pdf)關於RG色度,他們展示瞭如何通過去除Atan來加速它,就像你說的那樣。我會發布我的修正版本,它在我的計算機上的閾值爲320x240圖像,時間爲18ms。 – LadislavM 2012-03-11 10:51:43

0

我不確定,如果它會更快。但是here我寫了一個提示,如何使用位圖速度非常快。

+0

感謝您的回答,但我已經在使用非常類似的方法。儘管你的更復雜。 – LadislavM 2012-03-11 18:11:54

0

爲了完整,這裏是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);

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; }
相關問題