对于我的项目,我首先从文件中加载一张图片,并将每个像素放入一个二维数组
我的问题是,当我尝试从非常大的图像(例如1920x1200 = 230万像素)中填充这些箱子时,我正在使用的算法比我预期的要慢得多,而且我已经追踪到了一些C#语言特定的功能,它们似乎比我预期的要花费更长的时间。我想请教如何更好地使用C#来消除这些瓶颈。
在加载图像后,我调用一个名为“fillBinsFromSource”的函数,该函数接收一个可枚举的像素列表,找出它们属于哪个箱子并将它们放入相应的箱子中:
对于大尺寸的图片,预计我的算法会需要一些时间。但当我将一个秒表放在函数调用外面时,结果显示它花费的时间是代码中累计的时间的两倍左右。这意味着使用foreach进行枚举占用了该函数时间的一半(对于1920x1200的图像,此函数总共需要1.6秒,而其中大约有800毫秒是foreach操作占用的时间)。
我需要传递一个可枚举列表是因为有时用户只会添加图片的一小部分,而不是整个图片。这个耗时的调用会从一个图片列表中传递下来多个迭代器,然后从列表中的每个图像中依次传递迭代器,就像这样(简化版):
然后最后我调用这个。
问题2)假设
对于问题的长度,我深表歉意,如果有人能给我一些建议,我将不胜感激!
pixels[,]
中。然后,我想检查每个像素并根据它们的颜色将它们分成“箱子”,然后对每个箱子进行排序。因此,我有一个Bin
对象,它封装了一个List<Pixel>
,我还有一个包含(相对较少的)若干个箱子的List<Bin>
。我的问题是,当我尝试从非常大的图像(例如1920x1200 = 230万像素)中填充这些箱子时,我正在使用的算法比我预期的要慢得多,而且我已经追踪到了一些C#语言特定的功能,它们似乎比我预期的要花费更长的时间。我想请教如何更好地使用C#来消除这些瓶颈。
在加载图像后,我调用一个名为“fillBinsFromSource”的函数,该函数接收一个可枚举的像素列表,找出它们属于哪个箱子并将它们放入相应的箱子中:
public void fillBinsFromSource(IEnumerable<Pixel> source)
{
Stopwatch s = new Stopwatch();
foreach (Pixel p in source)
{
s.Start();
// algorithm removed for brevity
// involves a binary search of all Bins and a List.Add call
s.Stop();
}
}
对于大尺寸的图片,预计我的算法会需要一些时间。但当我将一个秒表放在函数调用外面时,结果显示它花费的时间是代码中累计的时间的两倍左右。这意味着使用foreach进行枚举占用了该函数时间的一半(对于1920x1200的图像,此函数总共需要1.6秒,而其中大约有800毫秒是foreach操作占用的时间)。
我需要传递一个可枚举列表是因为有时用户只会添加图片的一小部分,而不是整个图片。这个耗时的调用会从一个图片列表中传递下来多个迭代器,然后从列表中的每个图像中依次传递迭代器,就像这样(简化版):
class ImageList : IEnumerable<Pixel>
{
private List<Image> imageList;
public IEnumerator<Pixel> GetEnumerator()
{
foreach (Image i in imageList)
foreach (Pixel p in i)
yield return p;
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
class Image : IEnumerable<Pixel>
{
private Pixel[,] pixels; // all pixels in the image
private List<Pixel> selectedPixels;// all pixels in the user's selection
public IEnumerator<Pixel> GetEnumerator()
{
if (selectedPixels == null)
for (int i = 0; i < image.Width; i++)
for (int j = 0; j < image.Height; j++)
yield return pixels[i, j];
else
for (int i = 0; i < selectedPixels.Count; i++)
yield return selectedPixels[i];
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
然后最后我调用这个。
ImageList list; // pretend it contains only 1 image, and it's large
fillBinsFromSource(list);
问题1)由于需要枚举像素的2D数组和选择的区域,根据用户的选择不同,枚举速度非常慢。如何加快速度?
然后,在填充所有这些箱子与Pixel
对象之后,我会对它们进行排序。我调用List<Pixel>.Sort()
并依赖于IComparable
,如下所示:
ImageList list; // pretend it contains only 1 image, and it's large
fillBinsFromSource(list);
foreach(Bin b in allBins)
b.Sort(); // calls List<Pixel>.Sort()
class Pixel : IComparable
{
// store both HSV and RGB
float h, s, v;
byte r, g, b;
// we sort by HSV's value property
public int CompareTo(object obj)
{
// this is much faster than calling CompareTo on a float
Pixel rhs = obj as Pixel;
if (v < rhs.v)
return -1;
else if (v > rhs.v)
return 1;
return 0;
}
问题2)假设
allBins
有7个元素; 对其中包含总共230万个Pixel
的7个不同列表进行排序需要大约2秒钟。对一个包含230万个随机int
的列表进行排序只需要不到200毫秒。我可以理解使用基本数据类型会有一定程度的加速,但使用IComparable
真的会慢10倍以上吗?在这里是否有任何加速的方法?对于问题的长度,我深表歉意,如果有人能给我一些建议,我将不胜感激!
fillBinsFromSource
中不再迭代像素,而是在Image
类中执行基本循环并使用回调。从设计角度来看,这似乎特别(且不必要)混乱,但我将避免使用任何迭代器。如果可能的话,我希望避免破坏我的设计。 - XenoScholarBitmap
类,并错误地认为GetPixel
已经被很好地实现了。在最近实现涉及LockBits
的修复之前,仅仅将图像加载到初始像素数组中就需要大约5秒钟的时间。幸运的是,这个问题已经得到解决,图像的加载与上面的代码是分开的。 - XenoScholar