3

我正在一个网站上工作,我需要能够将 4000x6000 左右的图像拆分为 4 个部分(在许多其他任务中),并且我需要这对多个用户来说尽可能快。

我目前的代码是

var bitmaps = new RenderTargetBitmap[elements.Length];

using (var stream = blobService.Stream(key))
{
    BitmapImage bi = new BitmapImage();
    bi.BeginInit();
    bi.StreamSource = stream;
    bi.EndInit();

    for (var i = 0; i < elements.Length; i++)
    {
        var element = elements[i];

        TransformGroup transformGroup = new TransformGroup();
        TranslateTransform translateTransform = new TranslateTransform();
        translateTransform.X = -element.Left;
        translateTransform.Y = -element.Top;
        transformGroup.Children.Add(translateTransform);

        DrawingVisual vis = new DrawingVisual();
        DrawingContext cont = vis.RenderOpen();
        cont.PushTransform(transformGroup);
        cont.DrawImage(bi, new Rect(new Size(bi.PixelWidth, bi.PixelHeight)));
        cont.Close();

        RenderTargetBitmap rtb = new RenderTargetBitmap(element.Width, element.Height, 96d, 96d, PixelFormats.Default);
        rtb.Render(vis);
        bitmaps[i] = rtb;
    }
}

for (var i = 0; i < bitmaps.Length; i++)
{
    using (MemoryStream ms = new MemoryStream())
    {
        PngBitmapEncoder encoder = new PngBitmapEncoder();
        encoder.Frames.Add(BitmapFrame.Create(bitmaps[i]));
        encoder.Save(ms);
        var regionKey = WebPath.Variant(key, elements[i].Id);
        saveBlobService.Save("image/png", regionKey, ms);
    }
}

我正在运行多个线程,这些线程将作业从队列中取出。我发现如果这部分代码一次被 4 个线程命中,我会得到 OutOfMemory 异常。我可以通过将上面的所有代码包装在 a 中来阻止这种情况发生,lock(obj)但这并不理想。我尝试只包装第一个 using 块(从磁盘读取文件并拆分),但我仍然遇到内存不足异常(这部分代码执行得非常快)。

  • 考虑到这应该占用的内存量,这很正常吗?
  • 我可以做任何优化吗?
  • 我可以增加可用内存吗?

更新:

根据 Moozhe 的帮助,我的新代码

public static void GenerateRegions(this IBlobService blobService, string key, Element[] elements)
{
    using (var stream = blobService.Stream(key))
    {
        foreach (var element in elements)
        {
            stream.Position = 0;
            BitmapImage bi = new BitmapImage();
            bi.BeginInit();
            bi.SourceRect = new Int32Rect(element.Left, element.Top, element.Width, element.Height);
            bi.StreamSource = stream;
            bi.EndInit();

            DrawingVisual vis = new DrawingVisual();
            DrawingContext cont = vis.RenderOpen();
            cont.DrawImage(bi, new Rect(new Size(element.Width, element.Height)));
            cont.Close();

            RenderTargetBitmap rtb = new RenderTargetBitmap(element.Width, element.Height, 96d, 96d, PixelFormats.Default);
            rtb.Render(vis);

            using (MemoryStream ms = new MemoryStream())
            {
                PngBitmapEncoder encoder = new PngBitmapEncoder();
                encoder.Frames.Add(BitmapFrame.Create(rtb));
                encoder.Save(ms);
                var regionKey = WebPath.Variant(key, element.Id);
                blobService.Save("image/png", regionKey, ms);
            }
        }
    }
}
4

1 回答 1

3

如果您尝试并行调用 4000x6000 图像的 DrawImage,那么您将度过一段糟糕的时光。您裁剪它为时已晚,当您将其渲染到 RenderTargetBitmap 时,它已经在内存中呈现全尺寸。

不要使用变换裁剪图像源,而是尝试使用 BitmapImage.SourceRect 属性,如下所示:

BitmapImage.SourceRect = new Rect(element.Left, element.Top, element.Width, element.Height);

您可能想在调用 BeginInit() 之前尝试放置它,并完全摆脱转换。

编辑:实际上,在您的情况下,您必须在 for 循环的每次迭代中更改 SourceRect 。请记住,您必须将 DrawImage 中的 Size 参数更改为元素大小。

于 2012-06-08T15:41:43.473 回答