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 例外が発生することがわかりました。上記のすべてのコードを でラップすることで、これを防ぐことができます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 に答える