Memory expensive rubber band selection?
-
What is the reason of memory consumption in the DrawRoi function leading to hundreds of Mb? There is a Frame bitmap and its FrameWithRoi copy to assign to PictureBox. On the mouse move event new ROI coords are estimated and DrawRoi() is called.
DrawRoi()
{
RectangleF roi = Roi;RectangleF rect = new RectangleF()
{
X = (float)Frame.Width * roi.X,
Y = (float)Frame.Height * roi.Y,
Width = (float)Frame.Width * roi.Width,
Height = (float)Frame.Height * roi.Height
};// draw roi if it is smaller than the whole image
if (rect.Left > 0 || rect.Right < (float)FrameWithRoi.Width ||
rect.Top > 0 || rect.Bottom < (float)FrameWithRoi.Height)
{
Graphics g = Graphics.FromImage(FrameWithRoi);
g.DrawImage(Frame, 0, 0);
using (Bitmap region = new Bitmap(FrameWithRoi.Width, FrameWithRoi.Height, FrameWithRoi.PixelFormat))
{
Graphics roi_g = Graphics.FromImage(region);
roi_g.FillRectangle(new SolidBrush(Color.FromArgb(64, 0, 0, 0)), new Rectangle(0, 0, FrameWithRoi.Width, FrameWithRoi.Height));
roi_g.FillRectangle(new SolidBrush(Color.Red), rect);
region.MakeTransparent(Color.Red);
g.DrawImage(region, 0, 0);
}
}this.framePictureBox.Image = FrameWithRoi;
}Чесноков
-
What is the reason of memory consumption in the DrawRoi function leading to hundreds of Mb? There is a Frame bitmap and its FrameWithRoi copy to assign to PictureBox. On the mouse move event new ROI coords are estimated and DrawRoi() is called.
DrawRoi()
{
RectangleF roi = Roi;RectangleF rect = new RectangleF()
{
X = (float)Frame.Width * roi.X,
Y = (float)Frame.Height * roi.Y,
Width = (float)Frame.Width * roi.Width,
Height = (float)Frame.Height * roi.Height
};// draw roi if it is smaller than the whole image
if (rect.Left > 0 || rect.Right < (float)FrameWithRoi.Width ||
rect.Top > 0 || rect.Bottom < (float)FrameWithRoi.Height)
{
Graphics g = Graphics.FromImage(FrameWithRoi);
g.DrawImage(Frame, 0, 0);
using (Bitmap region = new Bitmap(FrameWithRoi.Width, FrameWithRoi.Height, FrameWithRoi.PixelFormat))
{
Graphics roi_g = Graphics.FromImage(region);
roi_g.FillRectangle(new SolidBrush(Color.FromArgb(64, 0, 0, 0)), new Rectangle(0, 0, FrameWithRoi.Width, FrameWithRoi.Height));
roi_g.FillRectangle(new SolidBrush(Color.Red), rect);
region.MakeTransparent(Color.Red);
g.DrawImage(region, 0, 0);
}
}this.framePictureBox.Image = FrameWithRoi;
}Чесноков
Two things strike me immediately: - you are not disposing of the graphics objects you obtain. This might actually be a resource leak. - you create a new RectangleF every time (though I think this is a value type, so it shouldn't matter). It's also possible that the bitmap doesn't get garbage collected immediately, even though it is being disposed of at the end of the using. But it should do.
-
Two things strike me immediately: - you are not disposing of the graphics objects you obtain. This might actually be a resource leak. - you create a new RectangleF every time (though I think this is a value type, so it shouldn't matter). It's also possible that the bitmap doesn't get garbage collected immediately, even though it is being disposed of at the end of the using. But it should do.
Yes, disposing graphics objects solved the problem. But why the drawing is very slow? The larger the image the slower update.
Чесноков
-
What is the reason of memory consumption in the DrawRoi function leading to hundreds of Mb? There is a Frame bitmap and its FrameWithRoi copy to assign to PictureBox. On the mouse move event new ROI coords are estimated and DrawRoi() is called.
DrawRoi()
{
RectangleF roi = Roi;RectangleF rect = new RectangleF()
{
X = (float)Frame.Width * roi.X,
Y = (float)Frame.Height * roi.Y,
Width = (float)Frame.Width * roi.Width,
Height = (float)Frame.Height * roi.Height
};// draw roi if it is smaller than the whole image
if (rect.Left > 0 || rect.Right < (float)FrameWithRoi.Width ||
rect.Top > 0 || rect.Bottom < (float)FrameWithRoi.Height)
{
Graphics g = Graphics.FromImage(FrameWithRoi);
g.DrawImage(Frame, 0, 0);
using (Bitmap region = new Bitmap(FrameWithRoi.Width, FrameWithRoi.Height, FrameWithRoi.PixelFormat))
{
Graphics roi_g = Graphics.FromImage(region);
roi_g.FillRectangle(new SolidBrush(Color.FromArgb(64, 0, 0, 0)), new Rectangle(0, 0, FrameWithRoi.Width, FrameWithRoi.Height));
roi_g.FillRectangle(new SolidBrush(Color.Red), rect);
region.MakeTransparent(Color.Red);
g.DrawImage(region, 0, 0);
}
}this.framePictureBox.Image = FrameWithRoi;
}Чесноков
Graphics
objects are expensive, and you should dispose them. Why not use ausing
statement, just like you did for theregion Bitmap
? And why would you need twoGraphics
instances, and aDrawImage()
? Couldn't you just use theBitmap(Image)
constructor, and have itsGraphics
, just the one? :)Luc Pattyn [My Articles] Nil Volentibus Arduum
The quality and detail of your question reflects on the effectiveness of the help you are likely to get.
Please use <PRE> tags for code snippets, they improve readability.
CP Vanity has been updated to V2.3 -
Graphics
objects are expensive, and you should dispose them. Why not use ausing
statement, just like you did for theregion Bitmap
? And why would you need twoGraphics
instances, and aDrawImage()
? Couldn't you just use theBitmap(Image)
constructor, and have itsGraphics
, just the one? :)Luc Pattyn [My Articles] Nil Volentibus Arduum
The quality and detail of your question reflects on the effectiveness of the help you are likely to get.
Please use <PRE> tags for code snippets, they improve readability.
CP Vanity has been updated to V2.3I was looking for a way to darken non ROI region in the image. You optimizations would be welcomed. Why the picture box update is very slow if I keep drawing on every mouse move event?
Чесноков
-
I was looking for a way to darken non ROI region in the image. You optimizations would be welcomed. Why the picture box update is very slow if I keep drawing on every mouse move event?
Чесноков
-
I was looking for a way to darken non ROI region in the image. You optimizations would be welcomed. Why the picture box update is very slow if I keep drawing on every mouse move event?
Чесноков
I recommend overlaying the semitransparent mask over the base image (either in OnPaint on the picture box or by placing another object over the top). Then all you have to redraw is the mask. Creating graphics objects is slow, which is why you should typically do it in OnPaint. In fact, in this case, you should do that, and your mouse move handler should only call Invalidate.
-
I was looking for a way to darken non ROI region in the image. You optimizations would be welcomed. Why the picture box update is very slow if I keep drawing on every mouse move event?
Чесноков
One other thing to bear in mind. The managed version of GDI+ is slow in comparison to the unmanaged version. You pay a price for the simplicity, and that price is speed.
Forgive your enemies - it messes with their heads
My blog | My articles | MoXAML PowerToys | Mole 2010 - debugging made easier - my favourite utility
-
I was looking for a way to darken non ROI region in the image. You optimizations would be welcomed. Why the picture box update is very slow if I keep drawing on every mouse move event?
Чесноков
Chesnokov Yuriy wrote:
Why the picture box update is very slow if I keep drawing on every mouse move event?
Because the MouseMove events fire at high frequency (allowing you to track well assuming your handler is fast), possibly 50Hz or higher (which is too high for the human eye to notice), because Graphics objects are expensive, and finally because PictureBoxes are stupid (I wouldn't be surprised if the fact you change the image just causes another mouse move event). You want to run such things continuously? I would experiment with keeping the two
Graphics
and theBitmap
around as class members, i.e. not create new objects at all, just keep using them over and over. :)Luc Pattyn [My Articles] Nil Volentibus Arduum
The quality and detail of your question reflects on the effectiveness of the help you are likely to get.
Please use <PRE> tags for code snippets, they improve readability.
CP Vanity has been updated to V2.3 -
Chesnokov Yuriy wrote:
Why the picture box update is very slow if I keep drawing on every mouse move event?
Because the MouseMove events fire at high frequency (allowing you to track well assuming your handler is fast), possibly 50Hz or higher (which is too high for the human eye to notice), because Graphics objects are expensive, and finally because PictureBoxes are stupid (I wouldn't be surprised if the fact you change the image just causes another mouse move event). You want to run such things continuously? I would experiment with keeping the two
Graphics
and theBitmap
around as class members, i.e. not create new objects at all, just keep using them over and over. :)Luc Pattyn [My Articles] Nil Volentibus Arduum
The quality and detail of your question reflects on the effectiveness of the help you are likely to get.
Please use <PRE> tags for code snippets, they improve readability.
CP Vanity has been updated to V2.3I need to implement rubber band like ROI selection in the image as in paint editor
Чесноков
-
What is the reason of memory consumption in the DrawRoi function leading to hundreds of Mb? There is a Frame bitmap and its FrameWithRoi copy to assign to PictureBox. On the mouse move event new ROI coords are estimated and DrawRoi() is called.
DrawRoi()
{
RectangleF roi = Roi;RectangleF rect = new RectangleF()
{
X = (float)Frame.Width * roi.X,
Y = (float)Frame.Height * roi.Y,
Width = (float)Frame.Width * roi.Width,
Height = (float)Frame.Height * roi.Height
};// draw roi if it is smaller than the whole image
if (rect.Left > 0 || rect.Right < (float)FrameWithRoi.Width ||
rect.Top > 0 || rect.Bottom < (float)FrameWithRoi.Height)
{
Graphics g = Graphics.FromImage(FrameWithRoi);
g.DrawImage(Frame, 0, 0);
using (Bitmap region = new Bitmap(FrameWithRoi.Width, FrameWithRoi.Height, FrameWithRoi.PixelFormat))
{
Graphics roi_g = Graphics.FromImage(region);
roi_g.FillRectangle(new SolidBrush(Color.FromArgb(64, 0, 0, 0)), new Rectangle(0, 0, FrameWithRoi.Width, FrameWithRoi.Height));
roi_g.FillRectangle(new SolidBrush(Color.Red), rect);
region.MakeTransparent(Color.Red);
g.DrawImage(region, 0, 0);
}
}this.framePictureBox.Image = FrameWithRoi;
}Чесноков
What I understand is that you are trying to darken a rectangle within a image, and that rectangle must be refreshed as long as the mouse is moving. Am I right? If this is what you want to do, the problem is that you are creating too many new Graphics and Bitmap objects in a very short time (and these are expensive objects), and updating the Image property a lot of times as well. The best way to do it is to subscribe to the Paint event of the control and code everything in this event handler, so you will receive a Graphics object as a parameter. In the next example there is only a
PictureBox
in the Form, calledpct
:public partial class Form1 : Form
{
bool drawRect = false;
Rectangle r = new Rectangle();
Brush br = new SolidBrush(Color.FromArgb(50, Color.Red));public Form1() { InitializeComponent(); } private void pct\_Paint(object sender, PaintEventArgs e) { if (drawRect) e.Graphics.FillRectangle(br, r); } private void pct\_MouseDown(object sender, MouseEventArgs e) { drawRect = true; r.X = e.X; r.Y = e.Y; } private void pct\_MouseMove(object sender, MouseEventArgs e) { r.Width = e.X - r.X; r.Height = e.Y - r.Y; Refresh(); } private void pct\_MouseUp(object sender, MouseEventArgs e) { drawRect = false; Refresh(); }
}
I've made it quick. You would have to modify it in order to draw the rectangle if the mouse moves to a lower X and/or lower Y coordinate.
-
What I understand is that you are trying to darken a rectangle within a image, and that rectangle must be refreshed as long as the mouse is moving. Am I right? If this is what you want to do, the problem is that you are creating too many new Graphics and Bitmap objects in a very short time (and these are expensive objects), and updating the Image property a lot of times as well. The best way to do it is to subscribe to the Paint event of the control and code everything in this event handler, so you will receive a Graphics object as a parameter. In the next example there is only a
PictureBox
in the Form, calledpct
:public partial class Form1 : Form
{
bool drawRect = false;
Rectangle r = new Rectangle();
Brush br = new SolidBrush(Color.FromArgb(50, Color.Red));public Form1() { InitializeComponent(); } private void pct\_Paint(object sender, PaintEventArgs e) { if (drawRect) e.Graphics.FillRectangle(br, r); } private void pct\_MouseDown(object sender, MouseEventArgs e) { drawRect = true; r.X = e.X; r.Y = e.Y; } private void pct\_MouseMove(object sender, MouseEventArgs e) { r.Width = e.X - r.X; r.Height = e.Y - r.Y; Refresh(); } private void pct\_MouseUp(object sender, MouseEventArgs e) { drawRect = false; Refresh(); }
}
I've made it quick. You would have to modify it in order to draw the rectangle if the mouse moves to a lower X and/or lower Y coordinate.
No, I need to darken the area outside rectangle. Consider ROI region. ROI remains clear and the area outside it is darked. I tried preinitialization of graphics and bitmaps and found that the problem part taking much of the time is:
...
roi_g.FillRectangle(new SolidBrush(Color.FromArgb(64, 0, 0, 0)), new Rectangle(0, 0, FrameWithRoi.Width, FrameWithRoi.Height));
roi_g.FillRectangle(new SolidBrush(Color.Red), rect);
region.MakeTransparent(Color.Red);
g.DrawImage(region, 0, 0);
...Despite prinitialization it takes about 150ms
Чесноков
-
No, I need to darken the area outside rectangle. Consider ROI region. ROI remains clear and the area outside it is darked. I tried preinitialization of graphics and bitmaps and found that the problem part taking much of the time is:
...
roi_g.FillRectangle(new SolidBrush(Color.FromArgb(64, 0, 0, 0)), new Rectangle(0, 0, FrameWithRoi.Width, FrameWithRoi.Height));
roi_g.FillRectangle(new SolidBrush(Color.Red), rect);
region.MakeTransparent(Color.Red);
g.DrawImage(region, 0, 0);
...Despite prinitialization it takes about 150ms
Чесноков
Well, then, draw four rectangles around the area you want to keep clear. Just change the Paint event handler I posted before this way:
private void pct_Paint(object sender, PaintEventArgs e)
{
if (drawRect)
{
e.Graphics.FillRectangle(br, 0, 0, pct.Width, r.Y);
e.Graphics.FillRectangle(br, 0, r.Y, r.X, pct.Height);
e.Graphics.FillRectangle(br, r.X, r.Y + r.Height, pct.Width, pct.Height);
e.Graphics.FillRectangle(br, r.X + r.Width, r.Y, pct.Width, r.Height);
}
}This will work pretty well. Do you also want to save the image after you have darkened the area?