2012-12-21 183 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'。 (如前所述,如果性能有任何问题,则不应使用获取/设置像素调用)。 –