简体   繁体   English

有没有一种有效的方法来处理C#中的高分辨率照片?

[英]Is there an efficient way of handling high-res photos in C#?

I've made a program that analyzes the first pixel of an image and then notes the values of it in a List, this is for checking if the image is black&white or in color. 我已经制作了一个程序,可以分析图像的第一个像素,然后在列表中记录其值,以检查图像是黑白还是彩色。 Now, does anyone know an efficient way of reading high-res images? 现在,有人知道读取高分辨率图像的有效方法吗? Right now I'm using Bitmaps but they are highly inefficient. 现在我正在使用位图,但是它们的效率很低。 The images are around 18 megapixels each and I want to analyze around 400 photos. 每个图像大约18兆像素,我想分析大约400张照片。 Code below: 代码如下:

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

When I run my program it says: 当我运行程序时,它说:

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

I've tried with System.GC.Collect() to clean up, as you can see, but the exception doesn't go away. 如您所见,我已经尝试使用System.GC.Collect()进行清理,但是异常不会消失。 If I analyse a folder that contains only a few photos, the program runs fine and gladly does it's job. 如果我分析一个仅包含几张照片的文件夹,则该程序可以正常运行,并且很高兴可以完成此工作。

Using the first pixel of an image to check if it is colour or not is the wrong way to do this. 使用图像的第一个像素检查其是否为彩色是错误的方法。

If you have an image with a black background (pixel value 0,0,0 in RGB), how do you know the image is black and white, and not colour with a black background? 如果您有黑色背景的图像(RGB中的像素值为0,0,0),您如何知道该图像是黑白的,而不是黑色背景的?

Placing the bitmap in a Using is correct, as it will dispose properly. 将位图放置在“使用中”是正确的,因为它将正确处理。

The following will do the trick. 以下将解决问题。

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);
                }
            }
        }
    }
}

The reference for PixelFormat is found here - https://msdn.microsoft.com/en-us/library/system.drawing.imaging.pixelformat%28v=vs.110%29.aspx 为的PixelFormat参考这里找到- https://msdn.microsoft.com/en-us/library/system.drawing.imaging.pixelformat%28v=vs.110%29.aspx

Objects in C# are limited to 2Gb, so I doubt that an individual image is causing the problem. C#中的对象限制为2Gb,因此我怀疑单个图像是否引起了问题。 I also would suggest that you should NEVER manually call the GC to solve a memory leak (though this is not technically a leak, just heavy memory usage). 我还建议您不要手动调用GC来解决内存泄漏(尽管从技术上讲这不是泄漏,而只是占用大量内存)。

Using statements are perfect for ensuring that an object is marked for disposal, and the GC is very good at cleaning up. 使用语句非常适合确保将对象标记为要处置,并且GC非常善于清理。

We perform intensive image processing in our software, and have never had issues with memory using the approach I have shown. 我们在软件中执行密集的图像处理,并且使用我展示的方法从未遇到内存问题。

While simply reading the header to find image data is a perfectly correct solution, it does mean a lot of extra work to decode different file types, which is not necessary unless you are working with vast amounts of images in very small memory (although if that is your aim, straight C is a better way to do it rather than C#. Horses for courses and all that jazz!) 虽然简单地读取标题以查找图像数据是一个完全正确的解决方案,但这确实意味着要解码不同的文件类型,这需要进行大量额外的工作,除非您要在非常小的内存中处理大量图像,否则这不是必需的(尽管是您的目标,比起C#,直线C是一种更好的实现方式。

EDIT - I just ran this on a directory containing over 5000 high-res TIFFs with no memory issues. 编辑-我只是在包含5000多个高分辨率TIFF的目录上运行了此文件,而没有内存问题。 The slowest part of the process was the console output! 这个过程中最慢的部分是控制台输出!

I guess, if you need only first pixel - not necessary to read all file. 我想,如果您只需要第一个像素-不需要读取所有文件。 Maybe you should take just first pixel from bitmap byte array and work with it. 也许您应该只从位图字节数组中提取第一个像素并对其进行处理。 You should find pixel array manually and take first. 您应该手动找到像素阵列,然后再进行拍摄。

How find pixel array? 如何找到像素阵列? Well, it depends on file format. 好吧,这取决于文件格式。 You should find specification for each usable format and use it. 您应该找到每种可用格式的规范并使用它。 Here is example for BMP reader 这是BMP阅读器的示例

I think you can solve the problem by disposing the bitmaps. 我认为您可以通过布置位图来解决问题。

Try this: 尝试这个:

     foreach (var f in files)
     {
        using (Bitmap b = (Bitmap)Image.FromFile(f))
        {
           //Do some work
        }
     }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM