I have an application that generate graphs from some input files. The user has the option to generate an image from these graphs. The graph is created using an external library that draws into a custom visual object (inherits from Canvas).
The Functionality
I created a custom functionality to create the image out of this canvas and it has worked well so far. The problem I'm having is related to its performance when the need to generate an image out of VERY large graph.
The images generated by this functionality has always been big, to be able to generate these large images I generate smaller "chunks" (configurable, but currently at 2000x2000px, larger chunks can lose quality and smaller is slower). After these "chunks" are created I merge them using another external library (this last works fine and it is really fast, the issue does not lie here).
The Issue
The problem is happening that the larger the original visual is, each chunk rendering takes longer.
The problem is that the larger the image is, each "chunk" rendering takes longer. I'll post the code here later, but I've noticed that the "RenderTargetBitmap.Render()" method when using my "DrawingVisual" (with the chunk information). As an example, this method call takes 400ms+ on my local tests where the Canvas is 74052.75px x 1119.4px; the total image creation time takes about 20 secs.
This would not be that bad, but there was a requirement where a large (MUCH larger) image has to be created and each chunk is taking 5 minutes to generate, so my estimation is that it would take about 60 days.
The Code
This is the code I'm using at each "chunk" loop. The code is modified to remove some cancellation token and progress report calls. I also added a comment to highlight the call that takes 400+ms.
for (int i = 0; i < rowCount; i++)
{
for (int j = 0; j < colCount; j++)
{
int row = i, col = j; //this is done because task start could have a modified value on call
Rect rect = new Rect(
col * maxWidth,
row * maxHeight,
col + 1 < colCount ? maxWidth :
surfaceWidth - (colCount - 1) * maxWidth,
row + 1 < rowCount ? maxHeight :
surfaceHeight - (rowCount - 1) * maxHeight);
DrawingVisual visual = new DrawingVisual();
using (var dc = visual.RenderOpen())
{
VisualBrush brush = new VisualBrush(element)
{
Viewbox = rect,
ViewboxUnits = BrushMappingMode.Absolute,
Stretch = Stretch.None
};
dc.DrawRectangle(brush,
null,
new Rect(0, 0, rect.Width, rect.Height));
}
RenderTargetBitmap bitmap = new RenderTargetBitmap(
(int)(rect.Width * (imgdpi / DEFAULT_DPI)),
(int)(rect.Height * (imgdpi / DEFAULT_DPI)),
imgdpi,
imgdpi,
PixelFormat);
//***********************//
//THIS IS THE CALL THAT TAKES 400+ms
//***********************//
bitmap.Render(visual);
PngBitmapEncoder encoder = new PngBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(bitmap));
MemoryStream ms = new MemoryStream();
encoder.Save(ms);
//... Some additional code to work with the chunk asynchronously
}
}
Things I've tried
I've tried several things:
Calling Dispatcher.DisableProcessing: I was thinking that it could have been that the UI kept trying to update the surface while processing the chunks (althought my understanding that if it has not been modified it shouldn't be much of a problem). But this did not change the processing time.
While creating the VisualBrush object to assign to it smaller Viewports (chunk size or bigger). The time actually increased.
Adding CacheMode to the Canvas: The time actually increased and the image looked low resolution.
Some additional desperate measures that made no sense haha.
Additional Information
Since I'm not completely sure how this Render functionality works internally (And I have searched for information on these, but what I've found is superficial at best) I'm not sure if the cause is the large surface or the number of controls in the surface (or a combination of both). So here is some additional information on the test case that is taking 400ms per render:
Surface Size is: 74052.75px x 1119.4px.
N. of Graph's Nodes: 1977.
N. of Connectors (Arrows): 2052.
Each Control visual is defined in the XAML using resources.
Each Graph Node is a Filled Rectangle with a Textblock at its center.
Each Connector is a straight line with a triangle at one of its edges.
Also, since the very large image generation is not something that will happen that often, and not to a regular user. I would not mind if the solution provided sacrifices UI responsiveness.
Notes added to clarify some concerns in the comments section
The graph is shown on screen to the user, this can be manipulated by it.
The "Store as Image" functionality is just an additional optional functionality to save the graph as an Image file. (This works under normal load, the speed issue happens with an unnusually large graph).
I'd appreciate any help you could provide.
Thanks!
Related
I'm writing a CAD-like application which needs to display maybe tens of thousands of lines when stuff is zoomed out.
At the moment I use C++ and Direct2D, which works quite smoothly: I can draw 100000 lines in something like 16 milliseconds. So I know my (average) machine can handle that.
I'm trying to move to WPF but I'm finding the performance disappointing. With the code below the redraw takes nearly one second (when I resize the window for example).
Profiling says the bottleneck is somewhere in [wpfgfx_v0400.dll], but I can't see exactly which functions.
So my questions: What am I doing wrong? How can I improve the performance of the code below?
public partial class MainWindow : Window
{
public MainWindow ()
{
InitializeComponent ();
var gg = new GeometryGroup ();
Random random = new Random ();
for (int i = 0; i < 1000; i++)
{
Point p0 = new Point (random.Next (1000), random.Next (1000));
Point p1 = new Point (random.Next (1000), random.Next (1000));
var lineGeometry = new LineGeometry (p0, p1);
gg.Children.Add (lineGeometry);
}
var stroke = new SolidColorBrush (Colors.Red);
gg.Freeze ();
stroke.Freeze ();
this.Content = new Path () { Data = gg, Stroke = stroke };
}
}
Drawing things in WPF is slower than you'd expect! A line with 1000 points shouldn't be a problem in general, but I wouldn't hope to draw anywhere near 100000 points using WPF geometry primitives - instead you'll eventually have to drop down to either rasterizing yourself or using some raw DirectX embedded in your WPF application.
Specifically the case of a single line of 1000 points should be well within the bounds of reason, some thoughts:
With random data, if the line goes back and forth to cover a lot of area, this can lead to some very ugly performance. This is not simply a case of creating a large area to redraw - going back and forth seems to cause some bad situation with WPF geometries
If you're drawing a single line, try using a PolyLine or PolyLineSegment in your Path
If you do actually want a number of disconnected lines, rather than a GeometryGroup full of LineGeometry, use a Path with many PathFigures.
For the general case as other answers have pointed out, you can look into OnRender or DrawingVisual type approaches, depending what you're doing the performance may or may not be better. I think you'll still struggle with the amount of elements you're suggesting, unless you can do some kind of virtualization.
I am trying to find coordinates of one image inside of another using AForge framework:
ExhaustiveTemplateMatching tm = new ExhaustiveTemplateMatching();
TemplateMatch[] matchings = tm.ProcessImage(new Bitmap("image.png"), new Bitmap(#"template.png"));
int x_coordinate = matchings[0].Rectangle.X;
ProcessImages takes about 2 minutes to perform.
Image's size is about 1600x1000 pixels
Template's size is about 60x60 pixels
Does anyone know how to speed up that process?
As addition to the other answers, I would say that for your case:
Image's size is about 1600x1000 pixels Template's size is about 60x60 pixels
This framework is not the best fit. The thing you are trying to achieve is more search-image-in-other-image, than compare two images with different resolution (like "Search Google for this image" can be used).
About this so
called pyramid search.
it's true that the algorithm works way faster for bigger images. Actually the image-pyramid is based on template matching. If we take the most popular implementation (I found and used):
private static bool IsSearchedImageFound(this Bitmap template, Bitmap image)
{
const Int32 divisor = 4;
const Int32 epsilon = 10;
ExhaustiveTemplateMatching etm = new ExhaustiveTemplateMatching(0.90f);
TemplateMatch[] tm = etm.ProcessImage(
new ResizeNearestNeighbor(template.Width / divisor, template.Height / divisor).Apply(template),
new ResizeNearestNeighbor(image.Width / divisor, image.Height / divisor).Apply(image)
);
if (tm.Length == 1)
{
Rectangle tempRect = tm[0].Rectangle;
if (Math.Abs(image.Width / divisor - tempRect.Width) < epsilon
&&
Math.Abs(image.Height / divisor - tempRect.Height) < epsilon)
{
return true;
}
}
return false;
}
It should give you a picture close to this one:
As bottom line - try to use different approach. Maybe closer to Sikuli integration with .Net. Or you can try the accord .Net newer version of AForge.
If this is too much work, you can try to just extend your screenshot functionality with cropping of the page element that is required (Selenium example).
2 minutes seems too much for a recent CPU with the image a template sizes you are using. But there are a couple of ways to speed up the process. The first one is by using a smaller scale. This is called pyramid search. You can try to divide the image and template by 4 so that you will have an image of 400x250 and a template of 15x15 and match this smaller template. This will run way faster but it will be also less accurate. You can then use the interesting pixels found with the 15x15 template and search the corresponding pixels in the 1600x1000 image using the 60x60 template instead of searching in the whole image.
Depending on the template details you may try at an even lower scale (1/8) instead.
Another thing to know is that a bigger template will run faster. This is counter-intuitive but with a bigger template you will have less pixel to compare. So if possible try to use a bigger template. Sometimes this optimization is not possible if your template is already as big as it can be.
I've looked everywhere but there doesn't seem to be a standard (I could see) of how one would go about checking to see if an image is blank. In C#
I have a way of doing this, but would love to know what the correct way is of checking to see if an image is blank, so everyone could also know in the future.
I'm not going to copy paste a bunch of code in, if you want me to, it will be my pleasure, but I just first want to explain how i go about checking to see if an image is blank.
You take a .jpg image, Get the width of it. For example 500 pixels
Then you divide that by 2
giving you 250
Then you check what the colour of every pixel is in the location of (250 width, and i height) (where you iterate thought the hight of the image.
What this then do is only check the middle line of pixels of an image, vertically. It goes though all the pixels checking to see if the colour is anything Except white. I've done this so you wont have to search ALL 500*height of pixels and since you will almost always come across a colour in the middle of the page.
Its working... a bit slow...There must be a better way to do this? You can change it to search 2/3/4 lines vertically to increase your chance to spot a page that's not blank, but that will take even longer.
(Also note, using the size of the image to check if it contains something will not work in this case, since a page with two sentences on and a blank page's size is too close to one another)
After solution has been added.
Resources to help with the implementation and understanding of the solution.
Writing unsafe code - pointers in C
Using Pointers in C#
/unsafe (C# Compiler Options)
Bitmap.LockBits Method (Rectangle, ImageLockMode, PixelFormat)
(Note that on the first website, the stated Pizelformat is actually Pixelformat) - Small error i know, just mentioning, might cause some confusion to some.
After I implemented the method to speed up the pixel hunting, the speed didn't increase that much. So I would think I'm doing something wrong.
Old time = 15.63 for 40 images.
New time = 15.43 for 40 images
I saw with the great article DocMax quoted, that the code "locks" in a set of pixels. (or thats how i understood it)
So what I did is lock in the middle row of pixels of each page. Would that be the right move to do?
private int testPixels(String sourceDir)
{
//iterate through images
string[] fileEntries = Directory.GetFiles(sourceDir).Where(x => x.Contains("JPG")).ToArray();
var q = from string x in Directory.GetFiles(sourceDir)
where x.ToLower().EndsWith(".jpg")
select new FileInfo(x);
int holder = 1;
foreach (var z in q)
{
Bitmap mybm= Bitmap.FromFile(z.FullName) as Bitmap;
int blank = getPixelData2(mybm);
if (blank == 0)
{
holder = 0;
break;
}
}
return holder;
}
And then the class
private unsafe int getPixelData2(Bitmap bm)
{
BitmapData bmd = bm.LockBits(new System.Drawing.Rectangle((bm.Width / 2), 0, 1, bm.Height), System.Drawing.Imaging.ImageLockMode.ReadOnly, bm.PixelFormat);
int blue;
int green;
int red;
int width = bmd.Width / 2;
for (int y = 0; y < bmd.Height; y++)
{
byte* row = (byte*)bmd.Scan0 + (y * bmd.Stride);
blue = row[width * 3];
green = row[width * 2];
red = row[width * 1];
// Console.WriteLine("Blue= " + blue + " Green= " + green + " Red= " + red);
//Check to see if there is some form of color
if ((blue != 255) || (green != 255) || (red != 255))
{
bm.Dispose();
return 1;
}
}
bm.Dispose();
return 0;
}
If you can tolerate the chance of getting it wrong, the approach seems fine; I have done something very similar in my case, although I always had a visual confirmation to deal with errors.
For the performance, the key open question is how you are getting the pixels to test. If you are using Bitmap.GetPixel, you are bound to have performance problems. (Search for "Bitmap.GetPixel slow" in Google to see lots of discussion.)
Far better performance will come from getting all the pixels at once and then looping over them. I personally like Bob Powell's LockBits discussion for clarity and completeness. With that approach, checking all of the pixels may well be reasonable depending on your performance needs.
If you're using System.Drawing.Bitmap you can speed up things up (substantially), by:
Not using GetPixel to access the pixels, use LockBits and UnlockBits to copy the image bitmap to regular memory. See the examples on the MSDN documentation for usage.
Not calling the Width, Height or Size properties in for loop. Call Size once, store the values in a local variable and use those in the loop.
Notes:
When using System.Drawing.Bitmap your image may be in device memory and accessing it may be time consuming.
I don't remember whether loading an image into a Bitmap already converts it to RGB format as other formats are more difficult to work with, but if that is not the case you can create an RGB Bitmap of the same size as your original image, get it's Graphic object (Graphics.FromImage) and use DrawImage to draw the original image in the RGB bitmap.
Edit: Beat to the punch by DocMax.
In any case for speed you can also try using alternative libraries such as the excellent FreeImage which includes C# wrappers.
Scale the image to 1x1 then check one pixel
new Bitmap(previousImage, new Size(1, 1));
i am wondering about what exactly a blob is? Is it is possible to reduce background noises in the image? Or is it possible to find largest region in and image, more epecifically if an image contains hand and head segments only then is it possible to separete hand or head regions only?? If this is possible then it is also possible to select boundary having larger contours, while eliminating small patches in the image ??
Suggest me, i have an image containing hand gesture only. I used skin
detection technique to do so. But the problem is i have small other
noises in the image that have same color as hand(SKIN). I want typical
hand gestures only, with removed noises. Help me??
Using the example from aforge, any reason you can't just clear the small bits from your image?
// create an instance of blob counter algorithm
BlobCounterBase bc = new ...
// set filtering options
bc.FilterBlobs = true;
bc.MinWidth = 5;
bc.MinHeight = 5;
// process binary image
bc.ProcessImage( image );
Blob[] blobs = bc.GetObjects( image, false );
// process blobs
var rectanglesToClear = from blob in blobs select blob.Rectangle;
using (var gfx = Graphics.FromImage(image))
{
foreach(var rect in rectanglesToClear)
{
if (rect.Height < someMaxHeight && rect.Width < someMaxWidth)
gfx.FillRectangle(Brushes.Black, rect);
}
gfx.Flush();
}
Have a look at morphological opening: this performs and erosion followed by a dilation and essentially removes areas of foreground/background smaller than a "structuring element," the size (and shape) of which you can specify.
I don't know aforge, but in Matlab the reference is here and in OpenCV see here.
I have an dynamic List of Point, new Point can be added at any time. I want to draw lines to connect them using different color. Color is based on the index of those points. Here is the code:
private List<Point> _points;
private static Pen pen1 = new Pen(Color.Red, 10);
private static Pen pen2 = new Pen(Color.Yellow, 10);
private static Pen pen3 = new Pen(Color.Blue, 10);
private static Pen pen4 = new Pen(Color.Green, 10);
private void Init()
{
// use fixed 80 for simpicity
_points = new List<Point>(80);
for (int i = 0; i < 80; i++)
{
_points.Add(new Point(30 + i * 10, 30));
}
}
private void DrawLinesNormal(PaintEventArgs e)
{
for (int i = 0; i < _points.Count-1; i++)
{
if (i < 20)
e.Graphics.DrawLine(pen1, _points[i], _points[i + 1]);
else if (i < 40)
e.Graphics.DrawLine(pen2, _points[i], _points[i + 1]);
else if (i < 60)
e.Graphics.DrawLine(pen3, _points[i], _points[i + 1]);
else
e.Graphics.DrawLine(pen4, _points[i], _points[i + 1]);
}
}
I find this method is not fast enough when I have new points coming in at a high speed. Is there any way to make it faster? I did some research and someone said using GraphicsPath could be faster, but how?
[UPDATE] I collect some possible optimizations:
Using GrahpicsPath, Original Question
Change Graphics quality ( such as SmoothingMode/PixelOffsetMode...), also call SetClip to specify the only necessary region to render.
You won't be able to squeeze much more speed out of that code without losing quality or changing to a faster renderer (GDI, OpenGL, DirectX). But GDI will often be quite a bit faster (maybe 2x), and DirectX/OpenGL can be much faster (maybe 10x), depending on what you're drawing.
The idea of using a Path is that you batch many (in your example, 20) lines into a single method call, rather than calling DrawLine 20 times. This will only benefit you if you can arrange the incoming data into the correct list-of-points format for the drawing routine. Otherwise, you will have to copy the points into the correct data structure and this will waste a lot of the time that you are gaining by batching into a path. In the case of DrawPath, you may have to create a GraphicsPath from an array of points, which may result in no time saved. But if you have to draw the same path more than once, you can cache it, and you may then see a net benefit.
If new points are added to the list, but old ones are not removed (i.e. you are always just adding new lines to the display) then you would be able to use an offscreen bitmap to store the lines rendered so far. That way each time a point is added, you draw one line, rather than drawing all 80 lines every time.
It all depends on exactly what you're trying to do.
Doesn't really help to improve performance, but i would put the pens also into a list and writing all this lines in this way:
int ratio = _points.Count / _pens.Count;
for (int i = 0; i < _points.Count - 1; i++)
{
e.Graphics.DrawLine(_pens[i / ratio], _points[i], _points[i + 1]);
}
This is about as fast as you're going to get with System.Drawing. You might see a bit of gain using Graphics.DrawLines(), but you'd need to format your data differently to get the advantage of drawing a bunch of lines at once with the same pen. I seriously doubt GraphicsPath will be faster.
One sure way to improve speed is to reduce the quality of the output. Set Graphics.InterpolationMode to InterpolationMode.Low, Graphics.CompositingQuality to CompositingQuality.HighSpeed, Graphics.SmoothingMode to SmoothingMode.HighSpeed, Graphics.PixelOffsetMode to PixelOffsetMode.HighSpeed and Graphics.CompositingMode to CompositingMode.SourceCopy.
I remember a speed test once where someone compared Graphics to P/Invoke into GDI routines, and was quite surprised by the much faster P/Invoke speeds. You might check that out. I'll see if I can find that comparison... Apparently this was for the Compact Framework, so it likely doesn't hold for a PC.
The other way to go is to use Direct2D, which can be faster yet than GDI, if you have the right hardware.
Too late, but possibly somebody still need a solution.
I've created small library GLGDI+ with similiar (but not full/equal) GDI+ syntax, which run upon OpenTK: http://code.google.com/p/glgdiplus/
I'm not sure about stability, it has some issues with DrawString (problem with TextPrint from OpenTK). But if you need performance boost for your utility (like level editor in my case) it can be solution.
You might wanna look into the Brush object, and it's true that you won't get near real-time performance out of a GDI+ program, but you can easily maintain a decent fps as long as the geometry and number of objects stay within reasonable bounds. As for line drawing, I don't see why not.
But if you reach the point where you doing what you think is optimal, and all that is, drawing lines.. you should consider a different graphics stack, and if you like .NET but have issues with unmanaged APIs like OpenGL and DirectX, go with WPF or Silverlight, it's quite powerful.
Anyway, you could try setting up a System.Drawing.Drawing2D.GraphicsPath and then using a System.Drawing.Drawing2D.PathGradientBrush to a apply the colors this way. That's a single buffered draw call and if you can't get enough performance out of that. You'll have to go with something other entirely than GDI+
Not GDI(+) at all, but a completely different way to tackle this could be to work with a block of memory, draw your lines into there, convert it to a Bitmap object to instantly paint where you need to show your lines.
Of course this hinges in the extreme on fast ways to
draw lines of given color in the memory representation of choice and
convert that to the Bitmap to show.
Not in the .NET Framework, I think, but perhaps in a third party library? Isn't there a bitmap writer of sorts in Silverlight for stuff like this? (Not into Silverlight myself that much yet...)
At least it might be an out of the box way to approach this. Hope it helps.
I think you have to dispose pen object and e.Graphics object after drawing.
One more thing it is better if you write your drawLine code inside onPaint().
just override onPaint() method it support better drawing and fast too.