I'm using the System.Drawing classes to generate thumbnails and watermarked images from user-uploaded photos. The users are also able to crop the images using jCrop after uploading the original. I've taken over this code from someone else, and am looking to simplify and optimize it (it's being used on a high-traffic website).
The previous guy had static methods that received a bitmap as a parameter and returned one as well, internally allocating and disposing a Graphics object. My understanding is that a Bitmap instance contains the entire image in memory, while Graphics is basically a queue of draw operations, and that it is idempotent.
The process currently works as follows:
Receive the image and store it in a temporary file.
Receive crop coordinates.
Load the original bitmap into memory.
Create a new bitmap from the original, applying the cropping.
Do some crazy-ass brightness adjusting on the new bitmap, maybe (?) returning a new bitmap (I'd rather not touch this; pointer arithmetics abound!), lets call this A.
Create another bitmap from the resulting one, applying the watermark (lets call this B1)
Create a 175x175 thumbnail bitmap from A.
Create a 45x45 thumbnail bitmap from A.
This seems like a lot of memory allocations; my question is this: is it a good idea to rewrite portions of the code and reuse the Graphics instances, in effect creating a pipeline? In effect, I only need 1 image in memory (the original upload), while the rest can be written directly to disk. All the generated images will need the crop and brightness transformations, and a single transformation that is unique to that version, effectively creating a tree of operations.
Any thought or ideas?
Oh, and I should probably mention that this is the first time I'm really working with .NET, so if something I say seems confused, please bear with me and give me some hints.
Reusing Graphics objects will probably not result in significant performance gain.
The underlying GDI code simple creates a device context for the bitmap you have loaded in RAM (a Memory DC).
The bottleneck of your operation appears to be in loading the image from disk.
Why reload the image from disk? If it is already in a byte array in RAM, which it should be when it is uploaded - you can just create a memory stream on the byte array and then create a bitmap from that memory stream.
In other words, save it to the disk, but don't reload it, just operate on it from RAM.
Also, you shouldn't need to create a new bitmap to apply the watermark (depending on how it'd done.)
You should profile the operation to see where it needs improvement (or even if it needs to be improved.)
The process seems reasonable. Each image has to exist in memory before it is saved to disk - so each version of your thumbnails will be in memory first. The key to making sure this works efficiently is to Dispose your Graphics and Bitmap objects. The easiest way to do that is with the using statement.
using( Bitmap b = new Bitmap( 175, 175 ) )
using( Graphics g = Graphics.FromBitmap( b ) )
{
...
}
I completed a similar project a while ago and did some practical testing to see if there was a difference in performance if I reused the Graphics object rather than spin up a new one for every image. In my case, I was working on a steady stream of large numbers of images (>10,000 in a "batch"). I found that I did get a slight performance increase by reusing the Graphics object.
I also found I got a slight increase by using GraphicsContainers in the Graphics object to easily swap different states into/out of the object as it was used to perform various actions. (Specifically, I had to apply a crop and draw some text and a box (rectangle) on each image.) I don't know if this makes sense for what you need to do. You might want to look at the BeginContainer and EndContainer methods in the Graphics object.
In my case, the difference was slight. I don't know if you would get more or less improvement in your implementation. But since you will incur a cost in rewriting your code, you might want to consider finishing the current design and doing some perf tests before rewriting. Just a thought.
Some links you might find useful:
Using Nested Graphics Containers
GraphicsContainer Class
I am only going to throw this out there casually, but if you wanted a quick 'guide' to best practices for working with images, look at the Paint.NET project. For free high-proformance tools for doing image manipulation, look at AForge.NET.
The benefit of AForge is to allow you to do alot of these steps without creating a new bitmap every time. If this is for a website, I can almost guarentee that the code you are working with will be the performance bottleneck for the application.
Related
I have dictionary of lists of objects, and for each list I need to load a bitmap using the associated key, and for each object in the list, calculate a transform, clone the bitmap using the transform, and save the result. The amount of objects in each list varies from 60,000 to one sometimes, and doing it serially will most likely take forever. So, I tried parallelizing the for each loop in each list, but unfortunately, the Bitmap class holds a global lock, and so, to avoid an exception, I have to place a lock on the cloning and saving code, which really defeats the point of using a parallel loop in the first place, as the cloning and saving is the most time-consuming part of the process. Is there a way I can get a subimage of a bitmap in multiple threads?
It turns out the real answer, for me at least, is to drop GDI+, and use Magick.NET, which is better designed for this. Now it works perfectly, with multithreading and the expected speedup.
I'm working on an application where the performance is very important. This application requires lots of image processing so, as most of us know that Bitmap's pixels accessing using GDI+ methods GetPixel and SetPixel is quite slow. To solve this issue we use Bitmap.LockBits and Bitmap.UnlockBits methods and i'm totally aware of how to access pixels using this method but my question is:
What is the performance of both Bitmap.LockBits and Bitmap.UnlockBits ? Do they perform any pixels copying or something that may have non-linear order?
I'm asking this question because I found a lots of calling for Bitmap.LockBits and Bitmap.UnlockBits methods in my code. I made a search but I didn't find anything
LockBits method returns BitmapData object, which is used to describe the memory sector.
BitmapData _bmd = _bmp.LockBits(new Rectangle(0, 0, _bmp.Width,_bmp.Height) , ImageLockMode ReadWrite, _bmp.PixelFormat);
Take a look here http://www.mfranc.com/programming/operacje-na-bitmapkach-net-1/
Update:
Lockbits will copy the bitmap data from a bitmap to a location in memory that is ready to be read/written to. The lockbits function will give you Scan0 that is a pointer to the start of this bitmap data.So it involves copying but even that would be much faster when compared to the Normal GDI+ Operation as per the comparison chart above.Also look out for out of bound memory .
I have a C# program which creates a List of Bitmaps from the images in a directory. It'll use the whole list of Bitmaps to generate one new image, which it will then save. When the user loads a new directory to repeat the process, the List of Bitmaps is cleared and refilled. The bitmaps are loaded upon creation of the object, "new Bitmap(path)".
I had a problem which occurred when the user performed the following steps:
Load images from directory 1
Chose not to save and instead load images from directory 2
Tries to save by overwriting an image from directory 1
Program is unable to save due to "A generic error in GDI+", because it is still "using" the image that is being overwritten.
The original List of Bitmaps loaded from directory 1 is indeed cleared and then refilled with images from directory 2. However, Bitmap.Save() refuses to overwrite an image it had previously loaded unless I call System.GC.Collect() after I perform Clear().
I'm pretty sure the problem has something to do with keeping the Bitmaps around even though there are no references, otherwise why would garbage collecting solve the problem? Is this the right way to go about solving this problem, or is there a "proper" way to dispose of Bitmaps in C#? Thanks.
You need to call Dispose on the Bitmap instances.. so they free their file handle.
var bitmap = new Bitmap(path_to_file);
// use it
bitmap.Dispose();
Or, since Bitmap inherits from Image which implements IDisposable, you can do this:
using (var bitmap = new Bitmap(path_to_file)) {
// use it..
}
How do you get a bitmap from a graphics object (or at least a pointer to it's Scan0)?
If a graphics object really always refers to a bitmap, then it IS possible to get to the bitmap data from the graphics object. (Think: the graphics object HAS TO have a pointer to the bmp data. I'd code it in C but I'm on a project that requires everyone be hobbled by .NET.)
Applications of this would include things like:
- using unsafe code to obtain faster screenshots
- modifying what's on a control using CreateGraphics
- (and the task I'm actually trying to accomplish which would take too long to explain)
Yes, this has been asked before but never answered. I'm not looking for how to get a graphics object from a bitmap (obviously trivial).
FAIL1, FAIL2, FAIL3, FAIL4, FAIL5, FAIL6, FAIL7
I don't think what you're trying to do is possible since your assumption that "a graphics object really always refers to a bitmap" is false.
There's a good article here that shows how to render a control to bitmap if you really want a bitmap and another one here that shows how to quickly update the screen at the WndProc level. If you're more familiar with C++ that might get you going the right direction.
I want to provide different parts of an application with independent Graphics instances which end up painting on the same base Graphics. Simply cloning the Graphics works, but since both instances refer to the same GDI handle, there are not independent. I can't use Begin and EndContainer as well since I have a method which has to provide the new Graphics instances. -so I cannot determine when to call EndContainer. The use case is quite similar to the Graphics.create() method in Java.
I've found some workarounds, but none of them works for a Graphics provided by the PrintController.
Is there any proxy-Graphics I can use? Or is there a possibility to create another Graphics for the same device for instance?
This sounds bad. Do not store references to a Graphics object, it only ever lives temporarily and is only valid while a Paint or PrintPage event handler is running. Do make sure to pass it as an argument to whatever method does the drawing instead of storing it in a field or a global variable.
If the method is altering the state of the object then use the Save() and Restore() methods to prevent this from causing problems in subsequent methods that use that same object. Cloning it is never necessary with this approach.
Graphics objects are not meant to be persisted. You could use a backbuffer approach by drawing to a Bitmap before your final render.
Perhaps you could raise an event to which listening drawing components could subscribe, and your calling code could chain these together. That way you could use the same Graphics instance without compromising GDI efficiency.
Not sure what exactly you're trying to do but you can use CreateGraphics() on a Control or Graphics.FromImage(xx) to create a new Graphics object for the control and/or image. There's also a few more functions in Graphics.FromXXX
A possibility would be to create multiple graphics objects which are pointing to multiple targets, for example an memory image. Then after done, combine all images into one.
But the thing I don't understand is, if all graphics instances should paint to the same target why do you need multiple graphics objects in the first place?
I was facing same problem, I found the only solution is to duplicate the drawings code line !!
Like the following:
e.Graphics.DrawString(points(i).pointText, myFont, Brushes.Blue, New Point(points(i).crossPointX4, points(i).crossPointY4)) : G.DrawString(points(i).pointText, myFont, Brushes.Blue, New Point(points(i).crossPointX4, points(i).crossPointY4))