c#Image.FromFile-批量操作?

时间:2018-10-08 17:35:30

标签: c# image file-io

为了使性能保持“高”,在使用我们的应用程序时,我们将所有列表视图都使用(预加载)图像列表。

biggeset列表视图可以包含大约9000张图像的图像列表。

因此,为了生成图像列表,我们使用了addRange(),它可以使该过程很快完成。

但是,在启动过程中,将图像从文件系统“加载”到image[]阵列中仍然需要4-5秒。 (〜80 MB,在SSD上)

我想知道是否有更好的方法从(本地)文件系统填充“大”图像列表?

当前我们正在使用:

System.IO.DirectoryInfo di2 = new System.IO.DirectoryInfo(Config.ProductImageLocalPath);
System.IO.FileInfo[] files2 = di.EnumerateFiles("*.jpg", SearchOption.AllDirectories).ToArray();

int idx = 1; //0 is reservevd for default image, which is already added.
foreach (FileInfo fi in files2)
{
    Resources.ImageIndex.Add(long.Parse(Path.GetFileNameWithoutExtension(fi.Name)), idx++);
}

Image[] images = Array.ConvertAll(files2, file => Image.FromFile(file.FullName));

Resources.ProductImageList_256.Images.AddRange(images);

ImageIndex只是一个dictionary<Long, int>,它允许将实际的id(从文件名派生)映射到相应图像映射中的基于0的索引,并且不需要任何时间都可以生成。

很明显,Image[] images = Array.ConvertAll(files2, file => Image.FromFile(file.FullName));是瓶颈,因为这等于对Image.fromFile的9000个调用(是否还有一些bulk-Option?)。

这里有“更好”的想法吗?

1 个答案:

答案 0 :(得分:0)

好的,所以我编写了一个函数,该函数可以完成分配给多个线程的工作-然后在我的代码中发现一个“错误”。 (但是,使用多线程不仅有很多帮助,所以image.fromFile显然是CPU的瓶颈,而不是(本地)SSD的瓶颈)。

1。)网络加载

在上述代码中:System.IO.FileInfo[] files2 = di.EnumerateFiles("*.jpg", SearchOption.AllDirectories).ToArray(); -我使用di进行枚举,而不是di2di代表网络共享)

(因此Array.ConvertAll正在访问远程文件)

“此”方法的运行时间: 6秒414毫秒

2。)本地加载

修复此问题后,运行时间将为 1 886毫秒

3。)本地线程加载

但是,我的ThreadedLoading功能已经完成,所以我也尝试了:

0s 543ms -因此,在具有4个物理内核的8内核上,速度仍快将近4倍。

这是代码。可能还不是防弹的,但是会产生可靠和最快的(最佳)结果。

用法:

 DateTime t1 = DateTime.Now;
//Image[] images = Array.ConvertAll(files2, file => Image.FromFile(file.FullName));
Image[] images = loadImagesThreaded(files2);
DateTime t2 = DateTime.Now;

TimeSpan ts = t2.Subtract(t1);
MessageBox.Show("Loading took: " + ts.Seconds + "s " + ts.Milliseconds + "ms"); //6s 414ms

Resources.ProductImageList_256.Images.AddRange(images);

功能:

private Image[] loadImagesThreaded(FileInfo[] files)
{
    int threadCount = 8;
    int chunkSize = files.Length / threadCount; //Round about -  doesn't need to be precise. 
    Thread[] threads = new Thread[threadCount];
    Image[][] result = new Image[threadCount][];

    for (int i = 0; i < threadCount; i++)
    {
        FileInfo[] chunk;
        int lowerBound = i * chunkSize;
        if (i < threadCount - 1)
        {
            chunk = files.Skip(lowerBound).Take(chunkSize).ToArray();
        }
        else
        {
            //take the rest
            chunk = files.Skip(lowerBound).ToArray();
        }

        int j = i; 
        threads[i] = new Thread(() =>
        {
            result[j] = Array.ConvertAll(chunk, file => Image.FromFile(file.FullName));
        });
        threads[i].Start();
    }

    //wait for threads to finish. 
    Boolean oneAlive = true;
    while (oneAlive)
    {
        oneAlive = false;
        foreach (Thread t in threads)
        {
            if (t.IsAlive)
            {
                oneAlive = true;
                break;
            }
        }
    }

    //all done. 
    Image[] finalResult = new Image[files.Count()];
    for (int i = 0; i < threadCount; i++)
    {
        int lowerBound = i * chunkSize;
        result[i].CopyTo(finalResult, lowerBound);
    }
    return finalResult;
}

在预加载图像时这不是一个很好的峰值吗? :-)

enter image description here


4。)Parallel.ForEach

按照Felipe Ramos的建议,我尝试了Parallel.ForEach。它比我的“线程化”解决方案快大约40-50毫秒(通常为480-500毫秒)-但也许更重要的事实:

这很容易,因此更可靠。

Image[] images = new Image[files2.Count()];
Parallel.ForEach(files2, (fileInfo, state, index) =>
{
    images[index] = Image.FromFile(fileInfo.FullName);
});

DateTime t2 = DateTime.Now;
TimeSpan ts = t2.Subtract(t1);
MessageBox.Show("Loading took: " + ts.Seconds + "s " + ts.Milliseconds + "ms");