大的每像素1位的位图导致OutOfMemoryException [英] Large, 1 bit-per-pixel bitmap causes OutOfMemoryException
问题描述
我想做的是从磁盘加载图像并从中创建一个BitmapSource
.
图像为14043px x 9933px,且为黑白(1bpp).但是我遇到了OutOfMemoryException
,因为以下代码消耗了大约800 MB的RAM.
What I'd like to do is to load an image from disk and create a BitmapSource
from it.
The image is 14043px x 9933px and is b/w (1bpp). But I run into a OutOfMemoryException
because the following code consumes about 800 MB RAM.
以下代码在我的特定文件的尺寸中生成一个ImageSource
.
我这样做是为了查看是否可以在不使用磁盘上实际文件的情况下使它工作.
The following code generates an ImageSource
in the dimensions of my specific file.
I did this to see if I could make it work without using the actual file on my disk.
public System.Windows.Media.ImageSource getImageSource(){
int width = 14043;
int height = 9933;
List<System.Windows.Media.Color> colors = new List<System.Windows.Media.Color>();
colors.Add(System.Windows.Media.Colors.Black);
colors.Add(System.Windows.Media.Colors.White);
BitmapPalette palette = new BitmapPalette(colors);
System.Windows.Media.PixelFormat pf = System.Windows.Media.PixelFormats.Indexed1;
int stride = width / pf.BitsPerPixel;
byte[] pixels = new byte[height * stride];
for (int i = 0; i < height * stride; ++i)
{
if (i < height * stride / 2)
{
pixels[i] = 0x00;
}
else
{
pixels[i] = 0xff;
}
}
BitmapSource image = BitmapSource.Create(
width,
height,
96,
96,
pf,
palette,
pixels,
stride);
return image;
}
在我的计算中,该图像应占用大约16.7 MB.
另外,使用BitmapSource.create时无法指定缓存选项.但是图像必须在加载时缓存.
此方法的返回值设置为图像控件的源.
In my calculation the image should consume about 16.7 MB.
Also, I cannot specify a cache option when using BitmapSource.create. But the image must be cached on load.
The return value of this method is set as the source of an image control.
问题已重新开放
@Clemens发布答案后,该系统起初非常出色.在检查TaskManager时,我发现一个非常糟糕的行为.这是我正在使用的代码,它与@Clemens答案完全相同.
After @Clemens posted the answer, wich worked very well in the first place. I noticed a very bad behaviour while inspecting my TaskManager. This is the code I am using, it is excatly the same as @Clemens answer.
public ImageSource getImageSource(){
var width = 14043;
var height = 9933;
var stride = (width + 7) / 8;
var pixels = new byte[height * stride];
for (int i = 0; i < height * stride; i++){
pixels[i] = 0xAA;
}
WriteableBitmap bitmap = new WriteableBitmap(width, height, 96, 96, PixelFormats.BlackWhite, null);
bitmap.WritePixels(new Int32Rect(0, 0, width, height), pixels, stride, 0);
bitmap.Freeze();
return bitmap;
}
在运行任何代码之前,我的任务管理器将显示以下内容:(1057 MB免费)
Before running any code, my taskmanager shows the following: (1057 MB free)
启动应用程序后,发现此方法的内存使用量峰值非常高:(初始峰值后有497 MB可用空间)
After starting the app it turnes out that this method has a very high peak of memory usage: (497 MB free after initial peak)
我尝试了几件事,发现@Clemens例程可能不是问题.我将代码更改为此:
I tried a couple things and found out that @Clemens routine probably isn't the problem. I changed the code to this:
private WriteableBitmap _writeableBitmap; //Added for storing the bitmap (keep it in scope)
public ImageSource getImageSource(){
var width = 14043;
var height = 9933;
var stride = (width + 7) / 8;
var pixels = new byte[height * stride];
for (int i = 0; i < height * stride; i++){
pixels[i] = 0xAA;
}
_writeableBitmap = new WriteableBitmap(width, height, 96, 96, PixelFormats.BlackWhite, null);
_writeableBitmap.WritePixels(new Int32Rect(0, 0, width, height), pixels, stride, 0);
_writeableBitmap.Freeze();
return null; //Return null (Image control source will be set to null now but bitmap still stored in private field)
}
我想将位图保留在内存中,但不影响图像控制,结果是:(997 MB可用) (如您所见,蓝线在右侧仅增加了一点点)
I wanted to keep the bitmap in memory but not affect the image control, this is the result: (997 MB free) (As you can see, the blue line increases just a tiny bit at the right side)
有了这些知识,我相信我的图像控件也存在问题.当将writeableBitmap分配给图像控件时,峰开始.这就是我所有的xaml:
With this knowledge I believe there is something wrong with my image control as well. The peak starts when the writeableBitmap is assigned to the image control. This is all my xaml:
<Window x:Class="TifFileViewer.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:TifFileViewer"
xmlns:xctk="http://schemas.xceed.com/wpf/xaml/toolkit"
Title="MainWindow" Height="563" Width="1046">
<Grid Margin="10">
<Image x:Name="imageControl"/>
</Grid>
</Window>
推荐答案
重要的是,您冻结该位图.另外,由于每个像素格式使用1位,因此应将像素缓冲区的步长计算为width / 8
.
It is important that you freeze the bitmap. Also, since you are using a 1 bit per pixel format, you should calculate the pixel buffer's stride as width / 8
.
以下方法将创建一个像素设置为黑白交替的位图.
The following method creates a bitmap with pixels set to alternating black and white.
public ImageSource CreateBitmap()
{
var width = 14043;
var height = 9933;
var stride = (width + 7) / 8;
var pixels = new byte[height * stride];
for (int i = 0; i < height * stride; i++)
{
pixels[i] = 0xAA;
}
var format = PixelFormats.Indexed1;
var colors = new Color[] { Colors.Black, Colors.White };
var palette = new BitmapPalette(colors);
var bitmap = BitmapSource.Create(
width, height, 96, 96, format, palette, pixels, stride);
bitmap.Freeze(); // reduce memory consumption
return bitmap;
}
或者,您可以使用不带BitmapPalette的BlackWhite
格式:
Alternatively you could use the BlackWhite
format without a BitmapPalette:
var format = PixelFormats.BlackWhite;
var bitmap = BitmapSource.Create(
width, height, 96, 96, format, null, pixels, stride);
如果您创建WriteableBitmap
而不是使用BitmapSource.Create
,则大位图还可以与Zoombox中的Image控件一起使用:
If you create a WriteableBitmap
instead of using BitmapSource.Create
the large bitmap also works with an Image control in a Zoombox:
public ImageSource CreateBitmap()
{
...
var bitmap = new WriteableBitmap(width, height, 96, 96, format, palette);
bitmap.WritePixels(new Int32Rect(0, 0, width, height), pixels, stride, 0);
bitmap.Freeze();
return bitmap;
}
这篇关于大的每像素1位的位图导致OutOfMemoryException的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!