2012-12-21 181 views
3

我寫了一個計算圖像焦點值的代碼。但要花5秒以上才能完成。將循環轉換爲並行循環

public double GetFValue(Image image) 
     { 
      Bitmap source = new Bitmap(image); 
      int count = 0; 
      double total = 0; 
      double totalVariance = 0; 
      double FM = 0; 
      Bitmap bm = new Bitmap(source.Width, source.Height); 
      Rectangle rect = new Rectangle(0,0,source.Width,source.Height); 
       Bitmap targetRect = new Bitmap(rect.Width, rect.Height); 

      // converting to grayscale 
      for (int y = 0; y < source.Height; y++) 
      { 
       for (int x = 0; x < source.Width; x++) 
       { 
        count++; 
        Color c = source.GetPixel(x, y); 
        int luma = (int)(c.R * 0.3 + c.G * 0.59 + c.B * 0.11); 
        source.SetPixel(x, y, Color.FromArgb(luma, luma, luma)); // the image is now gray scaled 
        var pixelval = source.GetPixel(x, y); 
        // targetRect.Save(@"C:\Users\payam\Desktop\frame-42-rectangle.png", System.Drawing.Imaging.ImageFormat.Png); 
        int pixelValue = pixelval.G; 
        total += pixelValue; 
        double avg = total/count; 
        totalVariance += Math.Pow(pixelValue - avg, 2); 
        double stDV = Math.Sqrt(totalVariance/count); // the standard deviation, which is also the focus value 
        FM = Math.Round(stDV, 2); 
       } 
      } 
      return FM; 
     } 

我想將此代碼轉換爲並行計算。我最終得到的錯誤是我無法將他們的頭圍繞在他們身上。任何建議?

public double CalculateFvalue (Image image) 
    { 
     Bitmap myimage = new Bitmap(image); 
     int count = 0; 
     int total = 0; 
     double totalVariance = 0; 
     double FM = 0; 
     Parallel.For(0, image.Height, y => 
      { 

      for (int x = 0; x < myimage.Width; x++) 
        { 
          count++; 
          Color c = myimage.GetPixel(x, y); 
          int luma = (int)(c.R * 0.3 + c.G * 0.59 + c.B * 0.11); 
          myimage.SetPixel(x, y, Color.FromArgb(luma, luma, luma)); // the image is now gray scaled 
          var pixelval = myimage.GetPixel(x, y); 
          int pixelValue = pixelval.G; 
          total += pixelValue; 
          double avg = total/count; 
          totalVariance += Math.Pow(pixelValue - avg, 2); 
          double stDV = Math.Sqrt(totalVariance/count); // the standard deviation, which is also the focus value 
          FM = Math.Round(stDV, 2); 
        } 


    }); 

     return Math.Round(FM,2); 
    } 
+0

你看到的錯誤是什麼? –

+5

是的,你有一個線程安全問題的整個負載。但無論如何,getpixel是非常緩慢的,而不是鎖定位而不是 – Steve

+2

你在不同的線程上同時更新'count','total'和'FM'。您應該讓每個循環迭代計算這些值的本地值,然後在最後將它們組合在一起。 – Lee

回答

1

爲了擴展我的評論,不要嘗試並行運行GetPixel,而是使用lockBits。

使用您的代碼lockbits:

public double GetFValue(Image image) 
    { 
     Bitmap source = new Bitmap(image); 
     int count = 0; 
     double total = 0; 
     double totalVariance = 0; 
     double FM = 0; 
     Bitmap bm = new Bitmap(source.Width, source.Height); 
     Rectangle rect = new Rectangle(0, 0, source.Width, source.Height); 
     //Bitmap targetRect = new Bitmap(rect.Width, rect.Height); 

     //new 
     ///* 
     BitmapData bmd = source.LockBits(rect, ImageLockMode.ReadWrite, source.PixelFormat); 
     int[] pixelData = new int[(rect.Height * rect.Width) -1]; 
     System.Runtime.InteropServices.Marshal.Copy(bmd.Scan0, pixelData, 0, pixelData.Length); 

     for (int i = 0; i < pixelData.Length; i++) 
     { 
      count++; 
      Color c = Color.FromArgb(pixelData[i]); 
      int luma = (int)(c.R * 0.3 + c.G * 0.59 + c.B * 0.11); 
      //Probably a formula for this 
      pixelData[i] = Color.FromArgb(luma, luma, luma).ToArgb(); 
      total += luma; 
      double avg = total/count; 
      totalVariance += Math.Pow(luma - avg, 2); 
      double stDV = Math.Sqrt(totalVariance/count); 
      FM = Math.Round(stDV, 2); 
     } 
     source.UnlockBits(bmd); 
     return FM; 
    } 

在使用1024×768的JPG從win7的樣本圖象的快速測試(Chrysanthemum.jpg):

lockbits:241毫秒

getPixel: 2208毫秒

注意轉換你的代碼時,我注意到一些奇怪的東西(比如getpixel,setpixel,getpixel在同一像素上?)但我猜你k現在你想要達到什麼目的,這段代碼和你的完全相同

+0

雖然這絕對是一個好建議,但同樣適用於順序執行。下面的答案實際上與原始問題更相關。並行代碼時,共享狀態總是一個壞主意。 –

+0

你搖滾的人,這工作完美。我將把for循環部分轉換爲並行代碼,並將在此處傳遞我的最終結果。最後需要注意的是,此代碼正在測量圖像的焦點,首先將其轉換爲灰度,然後在位圖矩陣上使用標準偏差。這是一種方法,還有許多其他方法來測量圖像的焦點。這是我正在研究的一個有趣的項目,稍後會發布更多關於它的內容。 – user843681

7

發生這種情況是因爲您聲明的變量超出了Parallel.For的範圍。由於它們的訪問(和寫入)是非確定性的,因此可能會使用錯誤的數據覆蓋值(如FM)。

我建議你讓每個迭代產生有關其結果的信息,然後使用收集的數據以線程安全的方式處理外部變量。你也可以通過使用幾個lock聲明逃脫,但我個人會避免這種情況。

+0

謝謝,我正在處理你的建議。會告訴你這件事的進展的。 – user843681

+0

+1:同時確保*不要*不修改位圖而沒有同步。當前的代碼以非線程安全的方式使用'SetPixel'。 (如前所述,如果性能有任何問題,則不應使用獲取/設置像素調用)。 –