【问题标题】:Converting for loops to parallel loops将 for 循环转换为并行循环
【发布时间】:2012-12-09 08:23:32
【问题描述】:

我编写了一个计算图像焦点值的代码。但它需要超过 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);
    }

【问题讨论】:

  • 您看到的错误是什么?
  • 是的,你有一大堆线程安全问题。但是不管怎样,getpixel 很慢,改用lockbits
  • 您正在不同线程上同时更新counttotalFM。您应该让每个循环迭代计算这些的局部值,然后在最后将它们组合起来。

标签: c# for-loop parallel-processing


【解决方案1】:

这是因为您声明的变量超出了Parallel.For 的范围。由于它们的访问(和写入)是不确定的,因此您可能会使用错误的数据(例如 FM)覆盖值。

我建议您让每次迭代都产生有关其结果的信息,然后使用收集到的数据以线程安全的方式在外部操作您的变量。你也可以通过使用一些lock 语句来避免它,但我个人会避免这样做。

【讨论】:

  • 谢谢,我会按照你的建议工作。会告诉你进展如何。
  • +1:还要确保不要在不同步的情况下修改位图。当前代码以非线程安全的方式使用SetPixel。 (同样如前所述,如果有任何性能问题,则不应使用 Get/Set 像素调用)。
【解决方案2】:

要扩展我的评论,不要尝试并行运行 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;
     }

在使用来自 win7 示例图片 (Chrysanthemum.jpg) 的 1024 x 768 jpg 的快速测试中:

锁定位:241 毫秒

getPixel:2208 毫秒

请注意,在转换代码时我注意到一些奇怪的东西(比如 getpixel、setpixel、getpixel 在同一个像素上?)但我想你知道你想要实现什么,并且这段代码与你的完全等价

【讨论】:

  • 虽然这绝对是个好建议,但同样适用于顺序实现。 Eve 在下面的回答实际上与原始问题更相关。在并行化代码时,共享状态总是一个坏主意。
  • 你这个摇滚人,这很好用。我要将 for 循环部分转换为并行代码,并将我的最终结果传递给这里。最后一点,这段代码测量图像的焦点,首先将其转换为灰度,然后使用位图矩阵上的标准偏差。这是一种方法,还有许多其他方法可以测量图像的焦点。这是我正在进行的一个有趣的项目,稍后会发布更多相关信息。
猜你喜欢
  • 2016-01-26
  • 1970-01-01
  • 2021-03-12
  • 2018-02-22
  • 2017-04-26
  • 2018-08-19
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多