【问题标题】:Is there an efficient way of handling high-res photos in C#?有没有一种在 C# 中处理高分辨率照片的有效方法?
【发布时间】:2015-08-07 11:40:54
【问题描述】:

我制作了一个程序,它分析图像的第一个像素,然后在列表中记录它的值,这是为了检查图像是黑白的还是彩色的。现在,有谁知道读取高分辨率图像的有效方法?现在我正在使用位图,但它们效率很低。这些图像每个大约 18 兆像素,我想分析大约 400 张照片。代码如下:

Bitmap b;

foreach (FileInfo f in files) {
  // HUGE MEMORY LEAK
  System.GC.Collect();
  System.GC.WaitForPendingFinalizers();

  b = (Bitmap)Bitmap.FromFile(f.FullName);

  // reading pixel (0,0) from bitmap

当我运行我的程序时,它会说:

"An unhandled exception of type 'System.OutOfMemoryException' occurred in System.Drawing.dll
Additional information: There is no available memory."

如您所见,我已尝试使用 System.GC.Collect() 进行清理,但异常并没有消失。如果我分析一个只包含几张照片的文件夹,程序运行良好并且很乐意完成它的工作。

【问题讨论】:

  • 文件列表来自哪里?如果您在创建 Bitmap 对象之前预加载目录中的所有文件,它们都已经在内存中。我会以 List 的形式获取目录中的文件名列表,然后遍历每个文件名,加载单个文件对象并确保它之后为空。手动调用 GC 是一个非常糟糕的主意,因为它没有解决内存使用的根本原因。
  • 另外,看看 PixelFormat 以确定颜色深度。查看第一个像素来确定图像是否为彩色不是一种可靠的方法。如果第一个像素是黑色或白色,您不知道它是灰度、单色还是带有黑色/白色背景的彩色。 msdn.microsoft.com/en-us/library/…。这篇 SO 帖子可能对您有用 - stackoverflow.com/questions/2150504/…
  • 您能否仔细检查问题是否真的是文件数量,而不是您的多文件方案包含更大的图像? .NET 对对象的最大大小有限制。
  • 你记得处理吗?

标签: c# .net memory-leaks photos


【解决方案1】:

使用图像的第一个像素来检查它是否是彩色的是错误的方法。

如果您有一张黑色背景的图像(RGB 中的像素值 0,0,0),您如何知道该图像是黑白的,而不是黑色背景的彩色?

将位图放在 Using 中是正确的,因为它会正确处理。

下面的方法可以解决问题。

class Program
{
    static void Main(string[] args) {
        List<String> ImageExtensions = new List<string> { ".JPG", ".JPE", ".BMP", ".GIF", ".PNG" };

        String rootDir = "C:\\Images";
        foreach (String fileName in Directory.EnumerateFiles(rootDir)) {
            if (ImageExtensions.Contains(Path.GetExtension(fileName).ToUpper())) {
                try {
                    //Image.FromFile will work just as well here.
                    using (Image i = Bitmap.FromFile(fileName)) {
                        if (i.PixelFormat == PixelFormat.Format16bppGrayScale) {
                            //Grey scale...
                        } else if (i.PixelFormat == PixelFormat.Format1bppIndexed) {
                            //1bit colour (possibly b/w, but could be other indexed colours)
                        }
                    }
                } catch (Exception e) {
                    Console.WriteLine("Error - " + e.Message);
                }
            }
        }
    }
}

这里可以找到 PixelFormat 的参考资料 - https://msdn.microsoft.com/en-us/library/system.drawing.imaging.pixelformat%28v=vs.110%29.aspx

C# 中的对象限制为 2Gb,所以我怀疑是单个图像导致了问题。 我还建议您永远不要手动调用 GC 来解决内存泄漏(尽管这在技术上不是泄漏,只是大量的内存使用)。

Using 语句非常适合确保将对象标记为待处理,并且 GC 非常擅长清理。

我们在我们的软件中执行密集的图像处理,并且使用我展示的方法从未遇到过内存问题。

虽然简单地读取标头来查找图像数据是一个完全正确的解决方案,但它确实意味着需要大量额外的工作来解码不同的文件类型,除非您在非常小的内存中处理大量图像(虽然如果这是你的目标,那么直接 C 是比 C# 更好的方法。课程和所有爵士乐的马!)

编辑 - 我刚刚在一个包含 5000 多个高分辨率 TIFF 的目录上运行此程序,没有内存问题。该过程中最慢的部分是控制台输出!

【讨论】:

  • 非常感谢 :) 好吧,我最初的方法是分析图像的“所有”像素并检查 R == G == B,因为它是灰度的。这最终成为一个“非常”重的方法,所以我决定减少到只有 1000 像素,这也很慢。我最终得到了 2 或 1 个像素,哈哈。你似乎在这方面受过良好的教育。祝您有美好的一天,再次感谢您
  • 不客气。我的大学论文涉及很多图像分析,而我们现在开发的软件也涉及很多相同的内容,所以我有很多时间试图让图像分析算法正常工作。 C# 实际上非常适合这一点。图像处理有很多特别有用的功能,尤其是当您将其与 Java 可用的功能进行比较时!与尝试使用 JAI 进行任何图像处理相比,在 C# 中处理图像是一种野餐!
  • 哈哈,伙计,我觉得你。我以前在 Java 中工作过,但 C# 要强大得多。去微软!顺便说一句,调试时,我的黑白图像不被视为 PixelFormat.Format16bppGrayScale 而是 PixelFormat.Format24bppRgb ... 叹息。 Image-class 没有 Bitmap 具有的 .getPixel(x,y) 方法,所以我也不能真正使用我的旧方法...:/
  • 啊,在这种情况下,它们是用有限调色板保存为彩色的黑白图像。有一种方法可以获取位图中的所有像素,只需将 Image i 更改为 Bitmap i。 Bitmap 扩展了 Image,严格来说,任何图像格式都有一个可以查询的位图。你之前的方法是健全的,但必须应用到图像中的所有像素,这会有点慢。我现在不在我的 Windows 机器上,但明天可以看看。可能有一种方法可以更快地将您的方法应用于所有像素...
  • 顺便说一句,如果您有一张真正灰度的图像 (Format16bppGrayScale),则使用 R = G = B 方法会失败,因此最好同时使用这两种方法。检查图像以查看 PixelFormat 是什么,如果是 RGB,则通过您的 R = G = B 算法对其进行处理
【解决方案2】:

我猜,如果您只需要第一个像素 - 不需要读取所有文件。也许您应该只从位图字节数组中获取第一个像素并使用它。您应该手动找到像素数组并先获取。

如何找到像素数组?好吧,这取决于文件格式。您应该找到每种可用格式的规范并使用它。这是BMP reader的示例

【讨论】:

  • 这需要为每种可能的图像类型配备一个解码器。虽然这是一种非常实用的方法,但它确实意味着很多额外的工作,而这对于这项任务并不是非常必要的。
猜你喜欢
  • 2014-11-05
  • 1970-01-01
  • 2013-02-27
  • 1970-01-01
  • 2015-05-14
  • 1970-01-01
  • 1970-01-01
  • 2010-12-14
  • 1970-01-01
相关资源
最近更新 更多