根据Emgu网站的说法,主要有两种策略:
安全(慢)方式
假设您正在处理一个 Image<Bgr, Byte>
。 您可以通过调用以下方法来获取第y行第x列的像素:
Bgr color = img[y, x];
在第y行和第x列上设置像素也很简单。
img[y,x] = color;
一种快速的方法
图像像素值存储在Data属性中,这是一个3D数组。
好的,这是真实的情况,但并没有说明如何在实际场景中做到这一点。让我们看一些可行的代码,然后讨论性能和优化:
Image<Bgr, Byte> original = newImage<Bgr, byte>(1024, 768);
Stopwatch evaluator = newStopwatch();
int repetitions = 20;
Bgr color = newBgr(100, 40, 243);
evaluator.Start();
for (int run = 0; run < repetitions; run++)
{
for (int j = 0; j < original.Cols; j++)
{
for (int i = 0; i < original.Rows; i++)
{
original[i, j] = color;
}
}
}
evaluator.Stop();
Console.WriteLine("Average execution time for {0} iteration \n using column per row access: {1}ms\n", repetitions, evaluator.ElapsedMilliseconds / repetitions);
这是使用安全的缓慢的设置图像像素的方法,在20次运行后得到的平均运行时间。在我的计算机上,需要1021毫秒...
因此,在循环和设置等于1024*768像素数量时,平均时间为1021毫秒。我们可以通过逐行循环来稍微改进一下。
因此,让我们稍微重构一下代码,并使用直接的Image.Data属性来加快速度:
evaluator.Reset();
evaluator.Start();
for (int run = 0; run < repetitions; run++)
{
for (int i = 0; i < original.Rows; i++)
{
for (int j = 0; j < original.Cols; j++)
{
original.Data[i, j, 0] = 100;
original.Data[i, j, 1] = 40;
original.Data[i, j, 2] = 243;
}
}
}
evaluator.Stop();
Console.WriteLine("Average execution time for {0} iterations \n using Data property: {1}ms\n", repetitions, evaluator.ElapsedMilliseconds / repetitions);
在我的电脑上,执行时间为519毫秒。
因此我们获得了50%的性能提升。执行时间缩短了一倍。
所以仔细查看代码并牢记我们使用的是C#,我们可以进行一个小改变,从而大幅度地提高图片像素设置的性能... 我们不应该在循环内部使用C#属性!!!
evaluator.Reset();
evaluator.Start();
byte[,,] data = original.Data;
for (int run = repetitions - 1; run >= 0; run--)
{
for (int i = original.Rows - 1; i >= 0; i--)
{
for (int j = original.Cols - 1; j >= 0; j--)
{
data[i, j, 0] = 100;
data[i, j, 1] = 40;
data[i, j, 2] = 243;
}
}
}
evaluator.Stop();
使用这段最新的代码,由于正确地使用C#语言,您将获得巨大的性能提升(73ms)。
Data
不执行任何验证或细致的分支(这很可能不会发生),除非底层值被标记为readonly
,否则编译器将无法轻易地知道是否有另一个线程可能已更改其引用的内容。 - Rei Miyasakaunsafe
,因此您仍然会承担边界检查的成本。 - Rei Miyasaka