将循环转换为并行循环
我写了一个计算图像焦点值的代码。但要花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。
使用您的代码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现在你想要达到什么目的,这段代码和你的完全相同
虽然这绝对是一个好建议,但同样适用于顺序执行。下面的答案实际上与原始问题更相关。并行代码时,共享状态总是一个坏主意。 –
你摇滚的人,这工作完美。我将把for循环部分转换为并行代码,并将在此处传递我的最终结果。最后需要注意的是,此代码正在测量图像的焦点,首先将其转换为灰度,然后在位图矩阵上使用标准偏差。这是一种方法,还有许多其他方法来测量图像的焦点。这是我正在研究的一个有趣的项目,稍后会发布更多关于它的内容。 – user843681
发生这种情况是因为您声明的变量超出了Parallel.For
的范围。由于它们的访问(和写入)是非确定性的,因此可能会使用错误的数据覆盖值(如FM
)。
我建议你让每个迭代产生有关其结果的信息,然后使用收集的数据以线程安全的方式处理外部变量。你也可以通过使用几个lock
声明逃脱,但我个人会避免这种情况。
谢谢,我正在处理你的建议。会告诉你这件事的进展的。 – user843681
+1:同时确保*不要*不修改位图而没有同步。当前的代码以非线程安全的方式使用'SetPixel'。 (如前所述,如果性能有任何问题,则不应使用获取/设置像素调用)。 –
你看到的错误是什么? –
是的,你有一个线程安全问题的整个负载。但无论如何,getpixel是非常缓慢的,而不是锁定位而不是 – Steve
你在不同的线程上同时更新'count','total'和'FM'。您应该让每个循环迭代计算这些值的本地值,然后在最后将它们组合在一起。 – Lee