Running multiple image manipulations in parallel causing OutOfMemory exception
- by Tom
I am working on a site where I need to be able to split and image around 4000x6000 into 4 parts (amongst many other tasks) and I need this to be as quick as possible for multiple users.
My current code for doing this is
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);
}
}
I am running multiple threads which take jobs off a queue. I am finding that if this part of code is hit by 4 threads at once I get an OutOfMemory exception. I can stop this happening by wrapping all the code above in a lock(obj) but this isn't ideal. I have tried wrapping just the first using block (where the file is read from disk and split) but I still get the out of memory exceptions (this part of the code executes quite quickly).
I this normal considering the amount of memory this should be taking up?
Are there any optimisations I could make?
Can I increase the memory available?
UPDATE:
My new code as per Moozhe's help
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);
}
}
}
}