Rank: Advanced Member
Groups: Member, Administration, Moderator Joined: 7/28/2003(UTC) Posts: 1,660
Thanks: 5 times Was thanked: 76 time(s) in 74 post(s)
|
This post is outdated. Graphics Mill 6 and later supports processing of huge images.When you try to process a huge bitmap with Graphics Mill for .NET, you may encounter the "Unable to commit memory" error. This articles describes how to work around this problem.When you try to load a huge bitmap in Graphics Mill for .NET and try to apply some effect to it, you may encounter the following error: ReasonLet's imagine you are trying to load 8000x8000 JPEG image. Each pixel in JPEG takes 24 bits (it is stored in 24-bit RGB pixel format), or, in other words, 3 bytes. The memory buffer size for such bitmap should be 8000x8000x3=192000000, or about 183MB. This is quite block of memory and it is not always possible to allocate it. Even if the system shows you much more free memory available, it is still may be lacking of memory. The problem is that this memory block should be linear (i.e. continuous). But memory is always fragmented (i.e. all the free memory comprises of a big number of memory portions of different sizes). It is not guarantied that the system will find a memory block of enough size when you try to allocate 183MB. Moreover, when you apply some transforms to a bitmap, it is often required to create a copy of this bitmap in memory. So even if you were able to load this bitmap, it may fail to allocate memory later, when you try to process it. General SolutionThe only way to avoid this problem is to process it part by part instead of loading entire bitmap at once. Currently Graphics Mill is not able to load a bitmap partially (the only exception is JPEG file format, it is described below more detailed). However you can minimize the memory usage in the following way: - Load an original file.
- Split it to several smaller bitmaps using the Crop transform and save them to the temporary files.
- Unload the original image.
- Apply the necessary effect to each part of the original bitmap.
- Create an empty bitmap of the original size.
- Load the bitmap parts and draw them onto the empty bitmap.
This way you do not have to allocate huge memory portions at one time when transforming a bitmap (but you have to do it when load and save an image). This method works fine for certain effects, where each pixel is processed separately. However some transforms (e.g. Unsharp Mask) may have so-called edge effects: some pixels at the edge of the bitmap can be processed differently than in the center. The problem is that such algorithms use neighboring pixels in their calculations, and there are not enough neighbors at the edges. When processing the whole image, these effects appear only at the edges. However, if you apply the approach used above, the effects will also appear at the boundaries of the bitmap chunks. To avoid this, you may “overlap” the parts of the bitmap. In other words, instead of splitting the original bitmap into stripes of the Xwidth, you will have to crop stripes of the X + n width, that is the stripes should be larger on each side. When gluing the parts together, crop the margins out. Here is an example how to apply Unsharp Mask to an image using the method described above. Code://Load the bitmap.
Bitmap bitmap = new Bitmap(@"D:\large_image.jpg");
int width = bitmap.Width;
int height = bitmap.Height;
//Thw width of a stripe
int stripeSide = bitmap.Width / 20;
Crop crop = new Crop();
//As this is just an example, stripes are stored in memory.
Bitmap[] parts = new Bitmap[20];
for (int i = 0; i < 20; i++)
{
//Each part will have margins on the left and on the right equal to the
//doubled blur radius
parts[i] = new Bitmap(stripeSide + 8, height, bitmap.PixelFormat);
//Cut out the part
crop.Rectangle = new System.Drawing.RectangleF(i * stripeSide - 4, 0,
stripeSide + 8, height);
crop.ApplyTransform(bitmap, parts[i]);
Console.WriteLine("Cut part:" + i.ToString());
}
//Unload the bitmap.
bitmap.Dispose();
//Apply Unsharp Mask
for (int i = 0; i < 20; i++)
{
parts[i].Transforms.UnsharpMask(1.5f, 2.0f, 0.04f);
Console.WriteLine("Unsharped part:" + i.ToString());
}
//Create an target empty bitmap.
bitmap = new Bitmap(width, height, parts[0].PixelFormat);
//Paste parts to the target bitmap.
for (int i = 0; i < 20; i++)
{
//Cut off the margins.
crop.Rectangle = new System.Drawing.RectangleF(4, 0, stripeSide, height);
crop.ApplyTransform(parts[i]);
//Paste the part.
parts[i].Draw(bitmap, i * stripeSide, 0, stripeSide, height,
CombineMode.Add, 1.0f, InterpolationMode.MediumQuality);
parts[i].Dispose();
Console.WriteLine("Pasted part:" + i.ToString());
}
//Save the bitmap.
bitmap.Save(@"D:\large_image_unsharp_mask.jpg");
bitmap.Dispose();
Special Case: JPEG FilesIf the images you process are stored in JPEG format, you can improve the algorithm of huge bitmap processing. The key difference of JPEG format is that Graphics Mill is able to crop JPEG image without decompressing it and write a portion of image to a compressed JPEG. To do it, Graphics Mill for .NET provides the LosslessJpegTransform class. Here is a modified variant of the above algorithm which uses LosslessJpegTransform to process huge JPEG file. Code://Read the width and the height of the image.
//The following will not actually load the bitmap, so the operation will be fast.
JpegReader reader = new JpegReader(@"D:\large_image.jpg");
Frame frame = reader.LoadFrame(0);
int width = frame.Width;
int height = frame.Height;
frame.Dispose();
reader.Dispose();
int stripeSide = width / 20;
//Create a transform for the bitmap.
LosslessJpegTransform transform = new LosslessJpegTransform(@"D:\large_image.jpg");
//Parts if the bitmap will be stored in memory.
System.IO.MemoryStream[] parts = new System.IO.MemoryStream[20];
for (int i = 0; i < 20; i++)
{
parts[i] = new System.IO.MemoryStream();
//As LosslessJpegTransform cannot crop the bitmaps outside their boundaries,
//we need to handle special cases.
System.Drawing.Rectangle rect;
if (i == 0)
rect = new System.Drawing.Rectangle(i * stripeSide, 0, stripeSide + 4,
height);
else if (i == 19)
rect = new System.Drawing.Rectangle(i * stripeSide - 4 , 0, stripeSide +
4, height);
else
rect = new System.Drawing.Rectangle(i * stripeSide - 4, 0, stripeSide +
8, height);
//The crop rectangle should be aligned to the JPEG sample size.
rect = transform.AlignToSampleSize(rect, JpegAlignToSampleSizeMode.Crop);
//Cut out the part
transform.WriteCropped(parts[i], rect);
}
for (int i = 0; i < 20; i++)
{
//Create a temporary bitmap to apply Unsharp Mask.
Bitmap bitmap = new Bitmap(parts[i]);
bitmap.Transforms.UnsharpMask(1.5f, 2.1f, 0.04f);
//Cut off the margins.
if (i == 0)
bitmap.Transforms.Crop(i * stripeSide, 0, stripeSide, height);
else
bitmap.Transforms.Crop(i * stripeSide + 4, 0, stripeSide, height);
//Write tha part to the target bitmap.
System.Drawing.Point offset = new System.Drawing.Point(i * stripeSide, 0);
transform.WritePatched(@"D:\large_image_unsharp_mask.jpg", offset, bitmap);
bitmap.Dispose();
parts[i].Dispose();
}
transform.Dispose();
Note also that this approach works faster that the general solution described earlier. So if you are sure that you are processing only JPEG files, it is recommended to use it instead of the general one. Edited by user Wednesday, May 21, 2014 8:15:37 PM(UTC)
| Reason: Not specified |
Best regards, Fedor Skvortsov
|