I have a Windows Application project that deals with Image editing (Cropping & Resizing). Unfortunately these image processings consume a lot of Memory and CPU resources (easily reaches 600MB or 50% cpu) and it is all about cropping and resizing just one gif image that weighs 2.5MB (2300*5400px). More than that, due to large resource consumption, the program gets stuck while resizing...
public static Image Resize(Image imgToResize, Size size)
{
Bitmap b = new Bitmap(size.Width, size.Height);
Graphics g = Graphics.FromImage((Image)b);
g.InterpolationMode = InterpolationMode.Default;
g.SmoothingMode = SmoothingMode.HighSpeed;
g.PixelOffsetMode = PixelOffsetMode.Default;
g.DrawImage(imgToResize, 0, 0, size.Width, size.Height);
g.Dispose();
return (Image)b;
}
public static Image Crop(Image img, Point p1, Point p2)
{
Rectangle cropArea = new Rectangle(p1.X, p1.Y, p2.X - p1.X, p2.Y - p1.Y);
return (img as Bitmap).Clone(cropArea, img.PixelFormat);
}
What methods should I use to avoid this?
I've already tried compressing it to memory stream in several formats but it didn't help (even made it worse)
NOTE: I use the standard .NET Drawing libraries: System.Drawing, System.Drawing.Imaging
Your code is creating copies of the image so you should expect unmanaged memory usage to rise when you call these methods. What matters a great deal is what you do with the original. You would be wise to get rid of it so it no longer takes up memory. You have to call its Dispose() method to do so. Waiting for the garbage collector to do it takes too long. The Bitmap class takes very little managed memory but oodles of unmanaged memory.
From an earlier version of this question: http://snippets.dzone.com/posts/show/4336
Also, AForge.net has several resize functions
This is a tricky one, and one I've run into before. You could split the image into x pieces, depending on the size of the file, then save each one to disk to ensure that the memory is clean.
Next, you resize the component images one at a time, each time, ensuring that the component is disposed before proceeding to the next one. Once you're all done, you stitch them back together, and then crop.
A MAJOR PROBLEM with this approach - if you're resizing upward, this approach will put seams in your image, as the interpolation won't have the surrounding pixels to guess at. But I would think that this approach would work well with resizing downward.
HTH.
Another hint again:
For example in resize, like #Hans pointed out, you create a new Bitmap, which one is your bottlenecks.
But, what if you just draw image resized, the same image which was loaded originally (obviously you made a backup file of original image before on the disk).
After crop what if you just draw a portion of a bitmap that user cropped, so user will see only that rectangle. ?
I mean in general, operate over image you already have, and try (as much as it possible) do not initialize new object for it.
Regards.
write Application.DoEvents(); in your functions, at least it wont get stuck
Related
I am writing a program similar to TeamViewer. But I have a problem that the screen resolution is too big. Below is a how I am generating the image from the screen.
byte[] ScreenShut()
{
Bitmap bmp = new Bitmap(Screen.PrimaryScreen.Bounds.Width,Screen.PrimaryScreen.Bounds.Height);
Graphics gr = Graphics.FromImage(bmp);
bmp.SetResolution(96.0F,96.0F);
gr.CopyFromScreen(0, 0, 0, 0, new Size(bmp.Width, bmp.Height));
MemoryStream ms = new MemoryStream();
bmp.Save(ms, ImageFormat.Png);
return ms.GetBuffer();
}
How can I reduce the quality of the incoming picture?
Save it as jpg 8 bit using this
public Bitmap(
int width,
int height,
PixelFormat format
)
I am writing a program similar to TeamViewer
I'll assume you are referring to the RDP/desktop sharing aspects.
Your problem is that you are not taking into account any prior frames so there is much erroneous data being transmitted. Generally, not a great deal of the screen changes from moment to moment. You need to compare prior frames to the current frame to determine what has changed and only send the deltas. Therefore your problem is essentially that of how to stream moving images or consecutive frames in a reasonably fast fashion.
The problem can be solved with any streaming video solution. Perhaps H.264?
You will find that video codecs don't just work on the current frame but also prior frames. Thus you can think of the screen being a slice moving through time of a much larger rectangular prism. So simply to solve it in a 2D fashion like trying to reduce the bit-depth; spacial resolution won't be sufficient.
I think I've found a MUCH faster way to copy bitmaps in c#. (If it's valid, I'm sure I wasn't the first but I haven't seen it anywhere yet.)
The simplest way I can think to ask this is to assert what I based my idea on and if no one shoots holes in it, assume the idea is sound:
void FastCopyRegion(Bitmap CopyMe, ref Bitmap IntoMe, Rectangle CopyArea)
{
//`IntoMe` need not be declared `ref` but it brings
// attention to the fact it will be modified
Debug.Assert(CopyMe.PixelFormat == IntoMe.PixelFormat,
"PixelFormat mismatch, we could have a problem");
Debug.Assert(CopyMe.Width == IntoMe.Width, //This check does not verify
"Stride mismatch, we could have a problem");// sign of `stride` match
BitmapData copyData = CopyMe.LockBits(CopyArea,
ImageLockMode.ReadWrite, CopyMe.PixelFormat);
IntoMe.UnlockBits(copyData);
}
1) LockBits simply copies a block of pixel data out of a bitmap to fixed memory to be edited and copied back in using UnlockBits
2) Using LockBits does not affect the copied memory block so it should have no effect on the image copied from.
3) Since you never enter unsafe code, there should be no risk of corrupting memory.
Possible holes I see:
1) If the PixelFormats of the two bitmaps are different, this method may not always copy correctly. However, since LockBits requires specifying a pixelformat, it seems this is handled. (If so, hooray for that overhead the other 99.9% of the time we're not switching pixelformats! /EndSarcasm)
2) If the stride of the two bitmaps doesn't match, there could be a problem (because stride is the outer for loop's incrementor in the copy operation.) This problem would limit copying to bitmaps with equal stride.
Edit: I think assertion #2 must be wrong... I just found an error when trying to later access the bitmap passed through CopyMe. Workaround below, but I'm not sure if it leaves a block of fixed memory lying around. (memory leak alert!)
void FastCopyRegion(Bitmap CopyMe, ref Bitmap IntoMe, Rectangle CopyArea)
{
//`IntoMe` need not be declared `ref` but it brings attention to the fact it will be modified
Debug.Assert(CopyMe.PixelFormat == IntoMe.PixelFormat, "PixelFormat mismatch, we could have a problem");
Debug.Assert(CopyMe.Width == IntoMe.Width, "Width mismatch, we could have a problem");
BitmapData copyD = IntoMe.LockBits(CopyArea, ImageLockMode.ReadWrite, CopyMe.PixelFormat);
BitmapData copyData = CopyMe.LockBits(CopyArea, ImageLockMode.ReadWrite, CopyMe.PixelFormat);
CopyMe.UnlockBits(copyData);
IntoMe.UnlockBits(copyData);
}
Use Bitmap.Clone() instead. GDI+ tends to not report exceptions and the resulting bugs will be very hard to track.
A very fast way to copy images into a bitmap is with Graphics.DrawImage() as long as you don't convert pixel format or scale the image.
I have a Windows Application project that deals with Image editing (Cropping & Resizing). Unfortunately these image processings consume a lot of Memory and CPU resources (easily reaches 600MB or 50% cpu) and it is all about cropping and resizing just one gif image that weighs 2.5MB (2300*5400px). More than that, due to large resource consumption, the program gets stuck while resizing...
public static Image Resize(Image imgToResize, Size size)
{
Bitmap b = new Bitmap(size.Width, size.Height);
Graphics g = Graphics.FromImage((Image)b);
g.InterpolationMode = InterpolationMode.Default;
g.SmoothingMode = SmoothingMode.HighSpeed;
g.PixelOffsetMode = PixelOffsetMode.Default;
g.DrawImage(imgToResize, 0, 0, size.Width, size.Height);
g.Dispose();
return (Image)b;
}
public static Image Crop(Image img, Point p1, Point p2)
{
Rectangle cropArea = new Rectangle(p1.X, p1.Y, p2.X - p1.X, p2.Y - p1.Y);
return (img as Bitmap).Clone(cropArea, img.PixelFormat);
}
What methods should I use to avoid this?
I've already tried compressing it to memory stream in several formats but it didn't help (even made it worse)
NOTE: I use the standard .NET Drawing libraries: System.Drawing, System.Drawing.Imaging
Your code is creating copies of the image so you should expect unmanaged memory usage to rise when you call these methods. What matters a great deal is what you do with the original. You would be wise to get rid of it so it no longer takes up memory. You have to call its Dispose() method to do so. Waiting for the garbage collector to do it takes too long. The Bitmap class takes very little managed memory but oodles of unmanaged memory.
From an earlier version of this question: http://snippets.dzone.com/posts/show/4336
Also, AForge.net has several resize functions
This is a tricky one, and one I've run into before. You could split the image into x pieces, depending on the size of the file, then save each one to disk to ensure that the memory is clean.
Next, you resize the component images one at a time, each time, ensuring that the component is disposed before proceeding to the next one. Once you're all done, you stitch them back together, and then crop.
A MAJOR PROBLEM with this approach - if you're resizing upward, this approach will put seams in your image, as the interpolation won't have the surrounding pixels to guess at. But I would think that this approach would work well with resizing downward.
HTH.
Another hint again:
For example in resize, like #Hans pointed out, you create a new Bitmap, which one is your bottlenecks.
But, what if you just draw image resized, the same image which was loaded originally (obviously you made a backup file of original image before on the disk).
After crop what if you just draw a portion of a bitmap that user cropped, so user will see only that rectangle. ?
I mean in general, operate over image you already have, and try (as much as it possible) do not initialize new object for it.
Regards.
write Application.DoEvents(); in your functions, at least it wont get stuck
if I try to create a bitmap bigger than 19000 px I get the error: Parameter is not valid.
How can I workaround this??
System.Drawing.Bitmap myimage= new System.Drawing.Bitmap(20000, 20000);
Keep in mind, that is a LOT of memory you are trying to allocate with that Bitmap.
Refer to http://social.msdn.microsoft.com/Forums/en-US/netfxbcl/thread/37684999-62c7-4c41-8167-745a2b486583/
.NET is likely refusing to create an image that uses up that much contiguous memory all at once.
Slightly harder to read, but this reference helps as well:
Each image in the system has the amount of memory defined by this formula:
bit-depth * width * height / 8
This means that an image 40800 pixels by 4050 will require over 660
megabytes of memory.
19000 pixels square, at 32bpp, would require 11552000000 bits (1.37 GB) to store the raster in memory. That's just the raw pixel data; any additional overhead inherent in the System.Drawing.Bitmap would add to that. Going up to 20k pixels square at the same color depth would require 1.5GB just for the raw pixel memory. In a single object, you are using 3/4 of the space reserved for the entire application in a 32-bit environment. A 64-bit environment has looser limits (usually), but you're still using 3/4 of the max size of a single object.
Why do you need such a colossal image size? Viewed at 1280x1024 res on a computer monitor, an image 19000 pixels on a side would be 14 screens wide by 18 screens tall. I can only imagine you're doing high-quality print graphics, in which case a 720dpi image would be a 26" square poster.
Set the PixelFormat when you new a bitmap, like:
new Bitmap(2000, 40000,PixelFormat.Format16bppRgb555)
and with the exact number above, it works for me. This may partly solve the problem.
I suspect you're hitting memory cap issues. However, there are many reasons a bitmap constructor can fail. The main reasons are GDI+ limits in CreateBitmap. System.Drawing.Bitmap, internally, uses the GDI native API when the bitmap is constructed.
That being said, a bitmap of that size is well over a GB of RAM, and it's likely that you're either hitting the scan line size limitation (64KB) or running out of memory.
Got this error when opening a TIF file. The problem was due to not able to open CMYK. Changed colorspace from RGB to CMYK and didn't get an error.
So I used taglib library to get image file size instead.
Code sample:
try
{
var image = new System.Drawing.Bitmap(filePath);
return string.Format("{0}px by {1}px", image.Width, image.Height);
}
catch (Exception)
{
try
{
TagLib.File file = TagLib.File.Create(filePath);
return string.Format("{0}px by {1}px", file.Properties.PhotoWidth, file.Properties.PhotoHeight);
}
catch (Exception)
{
return ("");
}
}
I am loading a JPG image from hard disk into a byte[]. Is there a way to resize the image (reduce resolution) without the need to put it in a Bitmap object?
thanks
There are always ways but whether they are better... a JPG is a compressed image format which means that to do any image manipulation on it you need something to interpret that data. The bimap object will do this for you but if you want to go another route you'll need to look into understanding the jpeg spec, creating some kind of parser, etc. It might be that there are shortcuts that can be used without needing to do full intepretation of the original jpg but I think it would be a bad idea.
Oh, and not to forget there are different file formats for JPG apparently (JFIF and EXIF) that you will ened to understand...
I'd think very hard before avoiding objects that are specifically designed for the sort of thing you are trying to do.
A .jpeg file is just a bag o' bytes without a JPEG decoder. There's one built into the Bitmap class, it does a fine job decoding .jpeg files. The result is a Bitmap object, you can't get around that.
And it supports resizing through the Graphics class as well as the Bitmap(Image, Size) constructor. But yes, making a .jpeg image smaller often produces a file that's larger. That's an unavoidable side-effect of Graphics.Interpolation mode. It tries to improve the appearance of the reduced image by running the pixels through a filter. The Bicubic filter does an excellent job of it.
Looks great to the human eye, doesn't look so great to the JPEG encoder. The filter produces interpolated pixel colors, designed to avoid making image details disappear completely when the size is reduced. These blended pixel values however make it harder on the encoder to compress the image, thus producing a larger file.
You can tinker with Graphics.InterpolationMode and select a lower quality filter. Produces a poorer image, but easier to compress. I doubt you'll appreciate the result though.
Here's what I'm doing.
And no, I don't think you can resize an image without first processing it in-memory (i.e. in a Bitmap of some kind).
Decent quality resizing involves using an interpolation/extrapolation algorithm; it can't just be "pick out every n pixels", unless you can settle with nearest neighbor.
Here's some explanation: http://www.cambridgeincolour.com/tutorials/image-interpolation.htm
protected virtual byte[] Resize(byte[] data, int width, int height) {
var inStream = new MemoryStream(data);
var outStream = new MemoryStream();
var bmp = System.Drawing.Bitmap.FromStream(inStream);
var th = bmp.GetThumbnailImage(width, height, null, IntPtr.Zero);
th.Save(outStream, System.Drawing.Imaging.ImageFormat.Jpeg);
return outStream.ToArray(); }