I am trying to load images quickly into a picturebox and draw on them. I have a .13 second delay between the time I assign a bitmap to the picture box and when it shows up. And whenever I do a picturebox.refresh(), it is the same delay of .13 - .15 seconds before the paint method is called. Is there any way to get rid of this delay?
I am using C# in Visual Studio 2010. I load the images using FreeImage library.
Here is the code in my pictureBox_MouseMove event:
if (IsMouseDown || DrawLine.Checked || IsMovingBox)
{
Tracing.Trace("Update Picture Box");
pictureBox.Refresh();
}
Then I trace out a line when my paint event is called. The delay is between the two trace lines.
If I use a bitonal tiff image at 117kb the delay is .13 seconds. To load this image into memory takes .04 seconds. To replace my picturebox bitmap with this bitmap takes .01 seconds.
If I use a gray scale jpg image at 1125kb the delay is .14 seconds. To load this image into memory takes .26 seconds. To replace my picturebox bitmap with this bitmap takes .03 seconds.
Assuming there are no other delays in your code that would prevent the UI thread from re-entering the message loop so that the OnPaint() method can be called: your Paint event handler gets called after PictureBox has drawn the Image. It isn't yet visible, PB uses double-buffering.
That image gets expensive to draw when it has to be resized to fit the PB's client area. Which is very likely in your case because your images are pretty large. It uses a high-quality bi-cubic filter to make the resized image look good. That's pretty expensive, albeit that the result is good.
To avoid that expense, resize the image yourself before assigning it to the Image property. Make it just as large as the PB's ClientSize.
That's going to make a big difference in itself. The next thing you can do is to create the scaled bitmap with the 32bppPArgb pixel format. It's the format that's about 10 times faster then any other because it matches the video adapter on most machines so no pixel format conversions are necessary.
Some code:
private void loadImage(string path) {
using (var srce = new Bitmap(path)) {
var dest = new Bitmap(pictureBox1.Width, pictureBox1.Height, System.Drawing.Imaging.PixelFormat.Format32bppPArgb);
using (var gr = Graphics.FromImage(dest)) {
gr.DrawImage(srce, new Rectangle(Point.Empty, dest.Size));
}
if (pictureBox1.Image != null) pictureBox1.Image.Dispose();
pictureBox1.Image = dest;
}
}
You'll probably want to tinker with this so the image preserves its aspect ratio. Try it first as-is to make sure you do get the perf improvement.
Related
I receve Bitmap image from a camera at 30 fps, and I need to display all images in a pictureBox.
The problem is that the PictureBox is very slow!
I have try to implement a custom PictureBox with DoubleBuffer enabled but the problem is not resolved.
Do you have a custom PictureBox or an user control or a solution that can display the image faster?
Additional information:
The image resolution is 2048x1088 with 256 graylevel (8bit image).
I use AForge.NET for elaborate the images.
Thank you
That image gets expensive to draw when it has to be resized to fit the PB's client area. Which is very likely in your case because your images are pretty large. It uses a high-quality bi-cubic filter to make the resized image look good. That's pretty expensive, albeit that the result is good.
To avoid that expense, resize the image yourself before assigning it to the Image property. Make it just as large as the PB's ClientSize.
That's going to make a big difference in itself. The next thing you can do is to create the scaled bitmap with the 32bppPArgb pixel format. It's the format that's about 10 times faster then any other because it matches the video adapter on most machines so no pixel format conversions are necessary.
I am showing the images that I capture from two cameras in a picturebox (the same for both). Each camera image is shown in a different ROI of the picturebox.
My problem is that the memory used by the application is increasing continuously, most probably I am missing freeing some resources, but I cannot find what I am missing.
This is the code:
// bitmap: input new image
// srcRoi: a rectangle with the ROI for the input image
// dstRoi: a rectangle with the ROI for the pictureBox
// reset: true for the first camera, false for the second one.
if (reset)
{
pictureBoxPreview1.Image.Dispose();
}
if (pictureBoxPreview1.Image == null || reset)
{
pictureBoxPreview1.Image = new Bitmap(pictureBoxPreview1.Width,
pictureBoxPreview1.Height);
}
using (Graphics g = Graphics.FromImage(pictureBoxPreview1.Image))
{
using (var img = Image.FromHbitmap(bitmap.GetHbitmap()))
{
g.DrawImage(img, dstRoi, srcRoi, GraphicsUnit.Pixel);
}
}
if (reset) {
pictureBoxPreview1.Invalidate();
}
The problem is not happening if pictureBoxPreview1.Image.Dispose() is call for both cameras, but then only the image of one camera is shown each time.
I don't understand why if I am only creating a new image and disposing it for half of the images the problem is solved when the same thing is done for all the images.
You can follow the answer from the following link WPF CreateBitmapSourceFromHBitmap() memory leak
In brief bitmap.GetHbitmap() is leaking and must be disposed
BTW in case of your bitmap being System.Drawing.Bitmap you can just write
g.DrawImage(bitmap, dstRoi, srcRoi, GraphicsUnit.Pixel);
since Bitmap is Image
BTW you can cache your image by picture box size and reuse it until resized.
I'm creating a visual which can have some Images as well. If I use the normal (non-transparent) Png images its working fine (in terms of performance and printing) however as soon as I replace a single transparent png, it becomes very slow and takes more than 3 times of time in printing the visual as well.
I'm using the following code.
var source = new Uri(filePath, UriKind.RelativeOrAbsolute);
BitmapImage imageBitMap =new BitmapImage(source);
var pictureImage = new Image();
pictureImage.Source = imageBitMap;
grid.Children.Add(pictureImage);
I used the ANTS Performance profiler and here are the statistics,
Using 4 graphics (Non-Tranparent) each approximately (50 -100KB) the average time to render them was 10ms for each graphic.
Soon I replace one of them with equivalent Transparent graphic, the average time shoots up and goes up to 34 ms for each graphic.
Any ideas why it takes that long for transparent graphics and how can I reduce it.
I tried to convert the tranparent Pngs into xaml using Adobe Illustrator and InkSpace as well but without success.
The Adobe Illustrator Plug-in, converts the png to 1KB xaml file with having an empty canvas with viewbox in it.
The InkSpace is converting the whole image into base64 string and setting it as source of the Image tag but that is not displaying in the visual at all.
In my program I have a picture box, containing a bitmap.(300x300 35kB .PNG file)
If 2 variables(x/z coord) are changed, I draw a new circle every second to the new position accordingly - a timer runs in the background, invoking this method.
void DrawEllipse()
{
// Retrieve the image.
bChamber = new Bitmap(global::Project.Properties.Resources.driveChamber1);
gChamber = Graphics.FromImage(bChamber);
gChamber.FillEllipse(brushChamber, VirtualViewX(), VirtualViewY(), 10, 10);
pictureBoxDriveView.Image = bChamber;
}
Now I'm looking for ways to optimize the performance. Redrawing the pic every 0.2s e.g. slows the program so much, I cant do anything else.
But ultimately I need a more fluent movement of the circle, you can Imagine how it laggs with the 1000ms refresh rate.
Is there a better way to do this, then loading the whole bitmap every time?
Use the Controls the way they were intended.
do not redraw the Bitmap yourself.
just load it 1x in the Picturebox.
handle the Paint event of the picturebox to draw the ellipse
invalidate the Picturebox whenever your coords change.
Draw the circle ONE time in a control (PictureBox)
Put the control across the 300x300 picture box.
When, and only when, the variables change, update the location of the picturebox with the circle.
This way you prevent drawing too many times.
Try setting the DoubleBuffered property of the form to true. This generally results in improved performance.
Also, you should put this
// Retrieve the image.
bChamber = new Bitmap(global::Project.Properties.Resources.driveChamber1);
In the class constructor.
Try this, it does not load the image from disk every time, so it is less expensive.
private Image _origImage = new Bitmap(global::Project.Properties.Resources.driveChamber1);
void DrawEllipse()
{
// Retrieve the image.
Image bChamber = new Bitmap((Image)this._origImage.Clone());
Graphics gChamber = Graphics.FromImage(bChamber);
gChamber.FillEllipse(brushChamber, VirtualViewX(), VirtualViewY(), 10, 10);
pictureBoxDriveView.Image = bChamber;
}
Trying to use 300dpi tif images for display on the web. At the moment, when the user uploads an image, I am dynamically creating a thumbnail. If a page is created referencing the high-res image with a width of 500x500px, can I use the same functionality to convert to a gif/jpg on the fly. What is the impending resolution of the jpg that would be created?
EDIT:
To further explain the usage, the user uploads 300dpi images which are approx 3000x3000 pixels. The user is using these images to create a catalog page which will be used for pdf printing. When they are creating the page, we only need 72dpi images to display to the screen, but for print, need the 300dpi images. Obviously they do not want to add a 3000x3000px image to the page, so it needs to be resized to the correct viewing area, e.g. 500x500px etc.
This boils down to a simple image resize. The discussion of DPIs is just ancillary data to calculate the scale factor.
As #Guffa said, you should do this at the time of the upload so that you can just serve static images in your viewer.
This will be a load on the server:
Load the full image. This will be about 27 MB of memory for your 3000x3000 images.
Resize. Lot's of math done lazily (still CPU intensive).
Compress. More CPU + Cost of writing to your drive.
Since you are already taking the time to generate a thumbnail, you can amortize that cost and this cost by not having to repeat Step 1 above (see the code).
After an image is uplaoded, I would recommend spinning off a thread to do this work. It's a load on the web server for sure, but you're only other option is to devote a second machine to performing the work. It will have to be done eventually.
Here is some code to do the job. The important lines are these:
OutputAsJpeg(Resize(big, 300.0, 72.0), new FileStream("ScreenView.jpg"));
OutputAsJpeg(Resize(big, bigSize, 64.0), new FileStream("Thumbnail.jpg"));
We can resize the big image however we need. In the first line we just scale it down by a fixed scale (72.0 / 300.0). On the second line, we force the image to have a final max dimension of 64 (scale factor = 64.0 / 3000.0).
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.IO;
BitmapSource Resize(BitmapSource original,
double originalScale,
double newScale) {
double s = newScale / originalScale;
return new TransformedBitmap(original, new ScaleTransform(s, s));
}
void OutputAsJpeg(BitmapSource src, Stream out) {
var encoder = new JpegBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(src));
encoder.Save(out);
}
// Load up your bitmap from the file system or whatever,
// then dump it out to a smaller version and a thumbnail.
// Assumes thumbnails have a max dimension of 64
BitmapSource big = new BitmapImage(new Uri("BigPage0.png",
UriKind. RelativeOrAbsolute));
double bigSize = Math.Max(big.PixelWidth, big.PixelHeight);
OutputAsJpeg(Resize(big, 300.0, 72.0), new FileStream("ScreenView.jpg"));
OutputAsJpeg(Resize(big, bigSize, 64.0), new FileStream("Thumbnail.jpg"));
If I understand what you want - you're trying to make a gif or jpg thumbnail of a very high resolution tif, for web display - if not, I apologize in advance....
If you want the thumbnail to be 500x500px, that is the resolution of the jpg/gif you'll want to create - 500x500, or at least 500x<500 or <500x500 (to fit in the box, unless you want distorted images).
For display on the web, the DPI does not matter. Just use the pixel resolution you wish directly.
Technically the JPG/GIF is created at 72-DPI by the Image classes in .NET. But the DPI really doesn't have any meaning to the browser - it just uses the dimensions 500x500.
When displaying an image on a web, the dpi (or more correctly ppi) setting is irrelevant. It's only the size in pixels that is relevant.
You can convert an image on the fly, but it is very work intensive for the server to do that every time the image is displayed. You should create the sizes that you need when the user uploads the image.