如何在 .NET 中使用大位图?

发布于 2024-07-14 16:18:26 字数 1200 浏览 7 评论 0原文

我正在尝试编写一个轻量级图像查看应用程序。 但是,.NET 存在系统内存限制。

当尝试加载大型位图(9000 x 9000 px 或更大,24 位)时,我收到 System.OutOfMemoryException。 这是在一台具有 2GB RAM(其中 1.3GB 已用完)的 Windows 2000 PC 上。 尝试加载文件也需要花费大量时间。

以下代码会生成此错误:

Image image = new Bitmap(filename);
using (Graphics gfx = this.CreateGraphics())
{
    gfx.DrawImage(image, new Point(0, 0));
}

与此代码一样:

Stream stream = (Stream)File.OpenRead(filename);
Image image = Image.FromStream(stream, false, false);
using (Graphics gfx = this.CreateGraphics())
{
    gfx.DrawImage(image, new Rectangle(0, 0, 100, 100), 4000, 4000, 100, 100, GraphicsUnit.Pixel);
}

另外,只需执行以下操作就足够了:

Bitmap bitmap = new Bitmap(filename);
IntPtr handle = bitmap.GetHbitmap();

后一个代码旨在与 GDI 一起使用。 在研究这个问题时,我发现这实际上是一个内存问题,.NET 尝试在单个连续内存块中分配两倍于所需量的内存。

http://bytes.com/groups/net-c/279493-drawing -large-bitmaps

我从其他应用程序(Internet Explorer、MS Paint 等)知道可以打开大图像,而且速度相当快。 我的问题是,如何在 .NET 中使用大型位图?

是否有办法对它们进行流式传输或非内存加载?

I'm trying to write a light-weight image viewing application. However, there are system memory limitations with .NET.

When trying to load large bitmaps (9000 x 9000 px or larger, 24-bit), I get a System.OutOfMemoryException. This is on a Windows 2000 PC with 2GB of RAM (of which 1.3GB is used up). It also takes a lot of time to attempt loading the files.

The following code generates this error:

Image image = new Bitmap(filename);
using (Graphics gfx = this.CreateGraphics())
{
    gfx.DrawImage(image, new Point(0, 0));
}

As does this code:

Stream stream = (Stream)File.OpenRead(filename);
Image image = Image.FromStream(stream, false, false);
using (Graphics gfx = this.CreateGraphics())
{
    gfx.DrawImage(image, new Rectangle(0, 0, 100, 100), 4000, 4000, 100, 100, GraphicsUnit.Pixel);
}

Also, it is enough to do just this:

Bitmap bitmap = new Bitmap(filename);
IntPtr handle = bitmap.GetHbitmap();

The latter code was intended for use with GDI.
While researching this, I found out that this is in fact a memory issue where .NET tries to allocate twice as much as is needed in a single contigous block of memory.

http://bytes.com/groups/net-c/279493-drawing-large-bitmaps

I know from other applications (Internet Explorer, MS Paint etc.) that it IS possible to open large images, and rather quickly. My question is, how do I use large bitmaps with .NET?

Is there anyway to stream them, or non-memory load them?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

内心旳酸楚 2024-07-21 16:18:26

这是一个由两部分组成的问题。 第一个问题是如何在不耗尽内存的情况下加载大图像 (1),第二个问题是如何提高加载性能 (2)。

(1) 考虑像 Photoshop 这样的应用程序,您可以在其中处理文件系统上消耗千兆字节的巨大图像。 在大多数系统(甚至 8GB x64 系统)上,将整个图像保留在内存中并且仍然有足够的可用内存来执行操作(滤镜、图像处理等,甚至只是添加图层)是不可能的。

这就是此类应用程序使用交换文件概念的原因。 在内部,我假设 Photoshop 使用专有的文件格式,适合他们的应用程序设计,并且构建为支持来自交换的部分加载,使他们能够将文件的部分内容加载到内存中进行处理。

(2) 通过为每种文件格式编写自定义加载程序,可以改进性能(相当多)。 这需要您阅读要使用的文件格式的文件头和结构。 一旦你掌握了它,它并不那么难,但也不像方法调用那么简单。

例如,您可以在 google 上搜索 FastBitmap,查看有关如何快速加载位图 (BMP) 文件的示例,其中包括解码位图标头。 这涉及 pInvoke 并让您了解您所面临的问题,您需要定义位图结构,例如

        [StructLayout(LayoutKind.Sequential, Pack = 1)]
        public struct BITMAPFILEHEADER
        {
            public Int16 bfType;
            public Int32 bfSize;
            public Int16 bfReserved1;
            public Int16 bfReserved2;
            public Int32 bfOffBits;
        }

        [StructLayout(LayoutKind.Sequential)]
        public struct BITMAPINFO
        {
            public BITMAPINFOHEADER bmiHeader;
            public RGBQUAD bmiColors;
        }

        [StructLayout(LayoutKind.Sequential)]
        public struct BITMAPINFOHEADER
        {
            public uint biSize;
            public int biWidth;
            public int biHeight;
            public ushort biPlanes;
            public ushort biBitCount;
            public BitmapCompression biCompression;
            public uint biSizeImage;
            public int biXPelsPerMeter;
            public int biYPelsPerMeter;
            public uint biClrUsed;
            public uint biClrImportant;
        }

可能与创建 DIB 一起工作(http://www.herdsoft.com/ti/davincie/imex3j8i.htm)以及诸如数据“颠倒”存储在位图中的奇怪现象,您需要考虑在内,否则当您打开它时您会看到镜像:-)

现在这仅适用于位图。 假设您想做 PNG,那么您需要做类似的事情,但解码 PNG 标头,其最简单的形式并不难,但如果您想获得完整的 PNG 规范支持,那么您将享受一段有趣的旅程:- )

PNG 与位图不同,因为它使用基于块的格式,其中具有“标头”,您可以找到不同的数据。 我在使用该格式时使用的一些块的示例是

    string[] chunks =  
new string[] {"?PNG", "IHDR","PLTE","IDAT","IEND","tRNS",
"cHRM","gAMA","iCCP","sBIT","sRGB","tEXt","zTXt","iTXt",
"bKGD","hIST","pHYs","sPLT","tIME"};

您还必须了解 PNG 文件的 Adler32 校验和。 因此,您想要使用的每种文件格式都会增加一系列不同的挑战。

我真的希望我可以在回复中提供更完整的源代码示例,但这是一个复杂的主题,而且说实话,我自己还没有实现交换,所以我无法就此提供太多可靠的建议。

简而言之,BCL 中的图像处理能力并不是那么热门。 中等答案是尝试查找是否有人编写了可以帮助您的图像库,而长答案是卷起袖子自己编写应用程序的核心。

既然你在现实生活中认识我,你就知道在哪里可以找到我;)

This is a two part question. The first question is how you can load large images without running out of memory (1), the second one is on improving loading performance (2).

(1) Concider an application like Photoshop where you have the ability to work with huge images consuming gigabites on the filesystem. Keeping the entire image in memory and still have enough free memory to perform operations (filters, image processing and so on, or even just adding layers) would be impossible on most systems (even 8gb x64 systems).

That is why applications such as this uses the concept of swap files. Internally I'm assuming that photoshop uses a proprietary file format, suitable for their application design and built to support partial loads from the swap, enabling them to load parts of a file into memory to process it.

(2) Performande can be improved (quite a lot) by writing custom loaders for each file format. This requires you to read up on the file headers and structure of the file formats you want to work with. Once you've gotten the hand of it its not ****that**** hard, but it's not as trivial as doing a method call.

For example you could google for FastBitmap to see examples on how you can load a bitmap (BMP) file very fast, it included decoding the bitmap header. This involved pInvoke and to give you some idea on what you are up against you will need to define the bitmap structues such as

        [StructLayout(LayoutKind.Sequential, Pack = 1)]
        public struct BITMAPFILEHEADER
        {
            public Int16 bfType;
            public Int32 bfSize;
            public Int16 bfReserved1;
            public Int16 bfReserved2;
            public Int32 bfOffBits;
        }

        [StructLayout(LayoutKind.Sequential)]
        public struct BITMAPINFO
        {
            public BITMAPINFOHEADER bmiHeader;
            public RGBQUAD bmiColors;
        }

        [StructLayout(LayoutKind.Sequential)]
        public struct BITMAPINFOHEADER
        {
            public uint biSize;
            public int biWidth;
            public int biHeight;
            public ushort biPlanes;
            public ushort biBitCount;
            public BitmapCompression biCompression;
            public uint biSizeImage;
            public int biXPelsPerMeter;
            public int biYPelsPerMeter;
            public uint biClrUsed;
            public uint biClrImportant;
        }

Possibly work with creating a DIB (http://www.herdsoft.com/ti/davincie/imex3j8i.htm) and oddities like data being stored "upside down" in a bitmap which you need to take into account or you'll see a mirror image when u open it :-)

Now that's just for bitmaps. Say you wanted to do PNG then you'd need to do similar stuff but decoding the PNG header, which in its simplest form isnt that hard, but if you want to get full PNG specification support then you are in for a fun ride :-)

PNG is different to say a bitmap since it uses a chunk based format where it has "headers" you can loacate to find the diffrent data. Example of some chunks I used while playing with the format was

    string[] chunks =  
new string[] {"?PNG", "IHDR","PLTE","IDAT","IEND","tRNS",
"cHRM","gAMA","iCCP","sBIT","sRGB","tEXt","zTXt","iTXt",
"bKGD","hIST","pHYs","sPLT","tIME"};

You are also going to have to learn about Adler32 checksums for PNG files. So each file format you'd want to do would add a different set of challenges.

I really wish I could give more complete source code examples in my reply but it's a complex subject, and to be honest I've not implemented a swap myself so I wouldn't be able to give too much solid advice on that.

The short answer is that the image processing cababilities in the BCL isn't that hot. The medium answer would be to try and find if someone has written an image library that could help you and the long answer would be to pull up your sleeves and write the core of your application yourself.

Since you know me in real-life you know where to find me ;)

烂人 2024-07-21 16:18:26

为了获得真正全面的答案,我会使用 Reflector 查看 Paint.NET 的源代码(http://www. getpaint.net/); 用 C# 编写的高级图形编辑程序。

(正如评论中指出的,Paint.NET曾经是开源的,但现在是闭源的)。

For a really comprehensive answer, I would use Reflector to look at the source code of Paint.NET (http://www.getpaint.net/); an advanced graphics editing program written in C#.

(as pointed out in the comment, Paint.NET used to be open source but is now closed source).

不知所踪 2024-07-21 16:18:26

有一件事让我印象深刻。 您是否绘制整个图像而不仅仅是可见部分? 您不应绘制比应用程序中显示的图像更大的部分,请使用 x、y、宽度和高度参数来限制绘制区域。

One thing just struck me. Are you drawing the entire image and not only the visible part? You should not draw a bigger portion of the image than you are showing in your application, use the x, y, width and heigth parameters to restrict the drawn area.

贱贱哒 2024-07-21 16:18:26

关于什么:

Image image = new Bitmap(filename);
using (Graphics gfx = Graphics.FromImage(image))
{    
// Stuff
}

What about:

Image image = new Bitmap(filename);
using (Graphics gfx = Graphics.FromImage(image))
{    
// Stuff
}
断桥再见 2024-07-21 16:18:26

只是一个快速修复,我遇到了同样的问题,我创建了第二个位图实例并在构造函数中传递了位图。

just a quick fix, i had the same problem, i created a second bitmap instance and passed the bitmap in the constructor.

温柔一刀 2024-07-21 16:18:26

您可以创建一个具有相同尺寸和颜色深度的新空白位图吗? 如果是这种情况,您至少知道您的环境可以处理该图像。 那么问题出在图像加载子系统中,当然您的链接指出的情况可能就是这样。

我想您可以编写自己的位图加载器,但这对于重要的格式来说需要大量工作,所以我不建议这样做。

也许有可用的替代库可以解决标准加载器的这些问题?

Can you create a new, blank, bitmap of the same dimensions and color depth? If that is the case, you at least know that your environment can handle the image. Then the problem resides in the image loading subsystem, as of course your link indicated might be the case.

I guess you could write your own bitmap loaders, but that is a lot of work for non-trivial formats, so I wouldn't suggest it.

Perhaps there are replacement libraries available that work around these issues with the standard loaders?

兮子 2024-07-21 16:18:26

所以你是说不是位图的加载而是渲染导致了内存不足?

如果是这样,您可以使用 Bitmap.LockBits 来获取像素并自己编写基本的图像缩放器吗?

So you're saying that it is not the loading of the bitmap but the rendering that causes an out of memory?

If so, can you use Bitmap.LockBits to get ahold of the pixels and write a basic image resizer yourself?

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文