【发布时间】:2014-12-05 16:19:01
【问题描述】:
我想做的是从磁盘加载图像并从中创建一个BitmapSource。
图像为 14043 像素 x 9933 像素,为黑白 (1bpp)。但是我遇到了OutOfMemoryException,因为下面的代码消耗了大约 800 MB RAM。
以下代码在我的特定文件的尺寸中生成ImageSource。
我这样做是为了看看是否可以在不使用磁盘上的实际文件的情况下使其工作。
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 时,我无法指定缓存选项。但图像必须在加载时缓存。
该方法的返回值设置为图片控件的来源。
问题重新打开
@Clemens 发布答案后,首先效果非常好。在检查我的 TaskManager 时,我注意到一个非常糟糕的行为。这是我正在使用的代码,它与@Clemens 的答案完全相同。
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 可用)
启动应用后,发现此方法的内存使用峰值非常高:(初始峰值后可用 497 MB)
我尝试了几件事,发现@Clemens 例程可能不是问题所在。我将代码更改为:
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 可用) (如您所见,蓝线在右侧增加了一点点)
有了这些知识,我相信我的图像控制也有问题。当 writeableBitmap 分配给图像控件时,峰值开始。这是我所有的 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>
【问题讨论】:
-
你在计算什么? (公式)
-
如果你的图片是 32bits ARGB,会有 4 字节/像素,图片会消耗 14043*9933*4 = 532Mo。
-
得到它,这是一个黑白图像,所以 8 像素/字节。实际上它导致 16.7 MB。
-
@SebastianL 14043px x 9933px 是 139489119 像素(或位)是 17436139 字节是 17027 kbytes 是 16.7 mbytes
-
请记住,OutOfMemory 异常与可用 RAM 无关。它取决于虚拟内存(每个 32 位进程限制为 2 或 4 GB)。您可以通过 Process Explorer 或 Process Hacker 等第三方应用查看进程的虚拟内存量
标签: c# wpf bitmap out-of-memory