I have a image where I am doing resizing,drawString and FillEllipse.
There are many points(FillEllipse) that needs to shown n bitmap, so I am using for loop.
Here is the code:
using (System.Drawing.Graphics Gfx = System.Drawing.Graphics.FromImage(OrginalBitmap))
{
Gfx.SmoothingMode = SmoothingMode.HighQuality;
Gfx.CompositingQuality = CompositingQuality.HighQuality;
Gfx.InterpolationMode = InterpolationMode.HighQualityBicubic;
Gfx.PixelOffsetMode = PixelOffsetMode.HighQuality;
foreach (var points in SelectedPoints)
{
Gfx.FillEllipse(
Brushes.Yellow,new Rectangle(points.X , points.Y, 8, 8));
Gfx.DrawString("M", new Font("Arial",8),
Brushes.Yellow, points.X, points.Y);
//points.X and points.X are the points that needs to be drawn on bitmap(particular location).
}
}
((IDisposable)OrginalBitmap).Dispose;
Loading of drawn bitmap takes very long time if there are many points in SelectedPoints.
Performance had drastically come down and loading takes too much memory.
Please let me know what to do.
Thanks in advance.
Drawing just 200 points should really not cause any performance problems, even at the highest quality setting. Using you code, I can draw around 40000 points in one second on my system.
Assuming that SelectedPoints is Point[] or List<Point> or some other efficient type, I would suspect the FontFacade.Large call. Is a new Font instance created each time around?
EDIT:
Running your modified code using new Font("Arial", 8) on 200 points takes around 20 milliseconds on my system, so there have to be something else that is causing your problems. How long does it take to run the code on your system?
Stopwatch timer = Stopwatch.StartNew();
[...]
Debug.WriteLine(timer.ElapsedMilliseconds);
The created font objects should be disposed when done, I would also move it outside the loop so that only one instance is created although that does not seem to be the source of your problems.
using(Font font = new Font("Arial", 8))
{
foreach(var point = SelectedPoints)
{
[...]
}
}
What dimensions is the OriginalBitmap, and what is it's PixelFormat?
What type is SelectedPoints?
Related
I need to shrink multiple images that contain text. Because of the text they need to be shrunk in such a way as to retain the sharp edges of the text and not smoothed. My first attempt was the following:
RenderOptions.SetBitmapScalingMode(upgradeCard, BitmapScalingMode.HighQuality);
upgradeCard.Height(resizedHeight);
upgradeCard.Width(resizedWidth);
The result was too blurry, the text was hard to read. It was, however, really really fast. I then tried this:
public static class ImageResizer
{
public static Image Resize(Image image, Size size)
{
if (image == null || size.IsEmpty)
return null;
var resizedImage = new Bitmap(size.Width, size.Height, image.PixelFormat);
resizedImage.SetResolution(image.HorizontalResolution, image.VerticalResolution);
using (var graphics = Graphics.FromImage(resizedImage))
{
var location = new Point(0, 0);
graphics.CompositingMode = CompositingMode.SourceCopy;
graphics.CompositingQuality = CompositingQuality.HighQuality;
graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
graphics.SmoothingMode = SmoothingMode.None;
graphics.DrawImage(image, new Rectangle(location, size),
new Rectangle(location, image.Size), GraphicsUnit.Pixel);
}
return resizedImage;
}
}
This worked really well, almost as good as Photoshop Bicubic Sharper. Unfortunately it was also very slow. Way too slow for what I need.
Is there any other way of doing this that produces the results of the second method but does so fairly quickly?
Without examples of your images it's hard to give reliable advice.
For example, how much contrast is in your images already? By what factor are you reducing them?
You could try nearest neighbour scaling, which can be very fast, and then try blurring the output slightly with a Gaussian filter, or similar. If that's too aliased, you could also try linear scaling with a soft blur.
I'm using the Cairo bindings for C#, and I wanted to be able to crop an ImageSurface and put it into a new ImageSurface variable, which I will then use in a seperate subroutine. The question is how would I do this properly.
Here's my code as it stands:
ImageSurface OutputImage = new ImageSurface (Format.Rgb24, (int)RectangleToCropTo.Width, (int)RectangleToCropTo.Height);
using (Cairo.Context cr = new Cairo.Context(OutputImage)) {
cr.SetSource (originalImage);
cr.Rectangle (RectangleToCropTo);
cr.Clip ();
cr.Paint ();
}
As it stands, it does actually crop the image down, and it appears to start at the correct point - but the OutputImage is offset in the x and y axis by the amount of pixels from 0,0 that the top left of the selection is. The area that the image doesn't populate is transparent.
I also tried the following:
ImageSurface OutputImage = new ImageSurface (Format.Rgb24, (int)RectangleToCropTo.Width, (int)RectangleToCropTo.Height);
using (Cairo.Context cr = new Cairo.Context(OutputImage)) {
cr.SetSource (originalImage, RectangleToCropTo.X, RectangleToCropTo.Y);
cr.Rectangle (RectangleToCropTo);
cr.Clip ();
cr.Paint ();
}
This additional two parameters supposedly mark from which point the 'source' should be stored (from my understanding, '40, 40' would mean that 'painting' the image would result in the image from 40, 40 and whatever is to the right of and below it).
However, this neither provides me with a cropped image, but instead includes the troublesome offset and starts drawing from the top left of the image.
I also figured that maybe this would be the solution:
ImageSurface OutputImage = new ImageSurface (Format.Rgb24, (int)RectangleToCropTo.Width, (int)RectangleToCropTo.Height);
using (Cairo.Context cr = new Cairo.Context(OutputImage)) {
cr.SetSource (originalImage, RectangleToCropTo.X, RectangleToCropTo.Y);
cr.Clip ();
cr.Paint ();
}
But to no avail, this simply produces identical results to the previous code chunk.
If any of you could tell me what I'm doing wrong (and it's probably the dumbest of mistakes), I'd appreciate it.
I'm use the Cairo-Sharp that comes with Gtk3.
Thanks :)
You almost got it. Try this:
ImageSurface OutputImage = new ImageSurface (Format.Rgb24, (int)RectangleToCropTo.Width, (int)RectangleToCropTo.Height);
using (Cairo.Context cr = new Cairo.Context(OutputImage)) {
cr.SetSource (originalImage, -RectangleToCropTo.X, -RectangleToCropTo.Y);
cr.Paint ();
}
The important difference is the negative arguments to SetSource. This places the source image so that the (x, y) pixel from the source falls onto the (0, 0) pixel on the target.
Since you target covers all the pixels needed, your rectangle() and clip() calls aren't needed anymore.
clip does not resize the surface, it just masks out anything fur future drawing operations.
Create a new surface of proper size and copy the content of the desired (clipped or not) region.
I need to process (change brightness, contrast etc) very large high-quality bitmaps (often over 10MPx) several times per second and need to update it on screen every time ( on Image control in WPF). Currently I'm using AForge.NET library for unmanaged image processing, but there are some problems I cannot solve. First of all, one operation takes ~300ms (without updating the screen) which is not acceptable for me. Here's sample code:
UnmanagedImage _img;
BrightnessCorrection _brightness = new BrightnessCorrection();
void Load()
{
_img = UnmanagedImage.FromManagedImage((Bitmap)Bitmap.FromFile("image.jpg"));
}
void ChangeBrightness(int val) // this method is invoked by changing Slider value - several times per second
{
_brightness.AdjustValue = val;
_brightness.ApplyInPlace(_img); // it takes ~300ms for image 22MPx, no screen update - just change brightness "in background"
}
I have no experience in image processing, but I think it cannot be much faster since it is very high resolution. Am I right?
Another problem - how to efficiently update the screen? At the moment I have the following (ofc very bad) solution:
void ChangeBrightness(int val)
{
_brightness.AdjustValue = val;
_brightness.ApplyInPlace(_img);
using (MemoryStream ms = new MemoryStream())
{
using (Bitmap b = _img.ToManagedImage())
{
b.Save(ms, ImageFormat.Bmp);
ms.Seek(0, SeekOrigin.Begin);
var bmp = new BitmapImage();
bmp.BeginInit();
bmp.StreamSource = ms;
bmp.CacheOption = BitmapCacheOption.OnLoad;
bmp.EndInit();
MyImageControl.Source = new WriteableBitmap(bmp); // !!!
}
}
}
As you can see, every time new WriteableBitmap is created (you can imagine what is happenin). Instead of these "usings" I tried that way:
WriteableBitmapSource.Lock(); // this object (of type WriteableBitmap) is just MVVM ViewModel's property which is binded to MyImageControl.Source
WriteableBitmapSource.Source.WritePixels(new Int32Rect(0, 0, _img.Width, _img.Height), _img.ImageData, _img.Stride * _img.Height * 3, _img.Stride, 0, 0); // image's PixelFormat is 24bppRgb
... but WritePixels method throws "Value does not fall within the expected range." Any ideas why?
Any help will be much appreciated :)
P.S.
Is AForge.NET a good choice at all? Maybe there is better image processing lib?
sorry for my english ;P
~300ms for image 22MPx is about 20 ns per pixel. That should be about right.
You need to consider CPU cost and memory access cost.
If you want to improve this further, consider:
1) Use multiple threads, each responsible for a section of the bitmap.
2) Write your own implementation, using SIMD instructions.
3) Do not pre-process the bitmap, transform bitmap scanline when they're needed.
I'm looking for a way to speed up the drawing of my game engine, which is currently the significant bottleneck, and is causing slowdowns. I'm on the verge of converting it over to XNA, but I just noticed something.
Say I have a small image that I've loaded.
Image img = Image.FromFile("mypict.png");
We have a picturebox on the screen we want to draw on. So we have a handler.
pictureBox1.Paint += new PaintEventHandler(pictureBox1_Paint);
I want our loaded image to be tiled on the picturebox (this is for a game, after all). Why on earth is this code:
void pictureBox1_Paint(object sender, PaintEventArgs e)
{
for (int y = 0; y < 16; y++)
for (int x = 0; x < 16; x++)
e.Graphics.DrawImage(image, x * 16, y * 16, 16, 16);
}
over 25 TIMES FASTER than this code:
Image buff = new Bitmap(256, 256, PixelFormat.Format32bppPArgb); // actually a form member
void pictureBox1_Paint(object sender, PaintEventArgs e)
{
using (Graphics g = Graphics.FromImage(buff))
{
for (int y = 0; y < 16; y++)
for (int x = 0; x < 16; x++)
g.DrawImage(image, x * 16, y * 16, 16, 16);
}
e.Graphics.DrawImage(buff, 0, 0, 256, 256);
}
To eliminate the obvious, I've tried commenting out the last e.Graphics.DrawImage (which means I don't see anything, but it gets rid a call that isn't in the first example). I've also left in the using block (needlessly) in the first example, but it's still just as blazingly fast. I've set properties of g to match e.Graphics - things like InterpolationMode, CompositingQuality, etc, but nothing I do bridges this incredible gap in performance. I can't find any difference between the two Graphics objects. What gives?
My test with a System.Diagnostics.Stopwatch says that the first code snippet runs at about 7100 fps, while the second runs at a measly 280 fps. My reference image is VS2010ImageLibrary\Objects\png_format\WinVista\SecurityLock.png, which is 48x48 px, and which I modified to be 72 dpi instead of 96, but those made no difference either.
When you're drawing to the screen, the OS is able to take advantage of special hardware in the graphics adapter to do simple operations such as copying an image around.
I'm getting ~5 msec for both. 7100 fps is way too fast for the software rendering done by GDI+. Video drivers notoriously cheat to win benchmarks, they can detect that a BitBlt doesn't have to be performed because the image didn't change. Try passing random values to e.Graphics.TranslateTransform to eliminate the cheat.
Are you sure the difference isn't from the using-block, i.e. setting up the try-finally block and creating the Graphics instance from the image buffer.
I could easily see the latter as being an expensive operation, unlike the paint event where you simply get a reference to an already created graphics instance.
In my project, I'm using (uncompressed 16-bit grayscale) gigapixel images which come from a high resolution scanner for measurement purposes. Since these bitmaps can not be loaded in memory (mainly due to memory fragmentation) I'm using tiles (and tiled TIFF on disc). (see StackOverflow topic on this)
I need to implement panning/zooming in a way like Google Maps or DeepZoom. I have to apply image processing on the fly before presenting it on screen, so I can not use a precooked library which directly accesses an image file. For zooming I intend to keep a multi-resolution image in my file (pyramid storage). The most useful steps seem to be +200%, 50% and show all.
My code base is currently C# and .NET 3.5. Currently I assume Forms type, unless WPF gives me great advantage in this area. I have got a method which can return any (processed) part of the underlying image.
Specific issues:
hints or references on how to implement this pan/zoom with on-demand generation of image parts
any code which could be used as a basis (preferably commercial or LGPL/BSD like licenses)
can DeepZoom be used for this (i.e. is there a way that I can provide a function to provide a tile at the right resulution for the current zoom level?) ( I need to have pixel accurate addressing still)
This CodeProject article: Generate...DeepZoom Image Collection might be a useful read since it talks about generating a DeepZoom image source.
This MSDN article has a section Dynamic Deep Zoom: Supplying Image Pixels at Run Time and links to this Mandelbrot Explorer which 'kinda' sounds similar to what you're trying to do (ie. he is generating specific parts of the mandelbrot set on-demand; you want to retrieve specific parts of your gigapixel image on-demand).
I think the answer to "can DeepZoom be used for this?" is probably "Yes", however as it is only available in Silverlight you will have to do some tricks with an embedded web browser control if you need a WinForms/WPF client app.
Sorry I can't provide more specific answers - hope those links help.
p.s. I'm not sure if Silverlight supports TIFF images - that might be an issue unless you convert to another format.
I decided to try something myself. I came up with a straightforward GDI+ code, which uses the tiles I've already got. I just filter out the parts which are relevant for current clipping region. It works like magic! Please find my code below.
(Form settings double buffering for the best results)
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
Graphics dc = e.Graphics;
dc.ScaleTransform(1.0F, 1.0F);
Size scrollOffset = new Size(AutoScrollPosition);
int start_x = Math.Min(matrix_x_size,
(e.ClipRectangle.Left - scrollOffset.Width) / 256);
int start_y = Math.Min(matrix_y_size,
(e.ClipRectangle.Top - scrollOffset.Height) / 256);
int end_x = Math.Min(matrix_x_size,
(e.ClipRectangle.Right - scrollOffset.Width + 255) / 256);
int end_y = Math.Min(matrix_y_size,
(e.ClipRectangle.Bottom - scrollOffset.Height + 255) / 256);
// start * contain the first and last tile x/y which are on screen
// and which need to be redrawn.
// now iterate trough all tiles which need an update
for (int y = start_y; y < end_y; y++)
for (int x = start_x; x < end_x; x++)
{ // draw bitmap with gdi+ at calculated position.
dc.DrawImage(BmpMatrix[y, x],
new Point(x * 256 + scrollOffset.Width,
y * 256 + scrollOffset.Height));
}
}
To test it, I've created a matrix of 80x80 of 256 tiles (420 MPixel). Of course I'll have to add some deferred loading in real life. I can leave tiles out (empty) if they are not yet loaded. In fact, I've asked my client to stick 8 GByte in his machine so I don't have to bother about performance too much. Once loaded tiles can stay in memory.
public partial class Form1 : Form
{
bool dragging = false;
float Zoom = 1.0F;
Point lastMouse;
PointF viewPortCenter;
private readonly Brush solidYellowBrush = new SolidBrush(Color.Yellow);
private readonly Brush solidBlueBrush = new SolidBrush(Color.LightBlue);
const int matrix_x_size = 80;
const int matrix_y_size = 80;
private Bitmap[,] BmpMatrix = new Bitmap[matrix_x_size, matrix_y_size];
public Form1()
{
InitializeComponent();
Font font = new Font("Times New Roman", 10, FontStyle.Regular);
StringFormat strFormat = new StringFormat();
strFormat.Alignment = StringAlignment.Center;
strFormat.LineAlignment = StringAlignment.Center;
for (int y = 0; y < matrix_y_size; y++)
for (int x = 0; x < matrix_x_size; x++)
{
BmpMatrix[y, x] = new Bitmap(256, 256, PixelFormat.Format24bppRgb);
// BmpMatrix[y, x].Palette.Entries[0] = (x+y)%1==0?Color.Blue:Color.White;
using (Graphics g = Graphics.FromImage(BmpMatrix[y, x]))
{
g.FillRectangle(((x + y) % 2 == 0) ? solidBlueBrush : solidYellowBrush, new Rectangle(new Point(0, 0), new Size(256, 256)));
g.DrawString("hello world\n[" + x.ToString() + "," + y.ToString() + "]", new Font("Tahoma", 8), Brushes.Black,
new RectangleF(0, 0, 256, 256), strFormat);
g.DrawImage(BmpMatrix[y, x], Point.Empty);
}
}
BackColor = Color.White;
Size = new Size(300, 300);
Text = "Scroll Shapes Correct";
AutoScrollMinSize = new Size(256 * matrix_x_size, 256 * matrix_y_size);
}
Turned out this was the easy part. Getting async multithreaded i/o done in the background was a lot harder to acchieve. Still, I've got it working in the way described here. The issues to resolve were more .NET/Form multithreading related than to this topic.
In pseudo code it works like this:
after onPaint (and on Tick)
check if tiles on display need to be retrieved from disc
if so: post them to an async io queue
if not: check if tiles close to display area are already loaded
if not: post them to an async io/queue
check if bitmaps have arrived from io thread
if so: updat them on screen, and force repaint if visible
Result: I now have my own Custom control which uses roughly 50 MByte for very fast access to arbitrary size (tiled) TIFF files.
I guess you can address this issue following the steps below:
Image generation:
segment your image in multiple subimages (tiles) of a small resolution, for instace, 500x500. These images are depth 0
combine a series of tiles with depth 0 (4x4 or 6x6), resize the combination generating a new tile with 500x500 pixels in depth 1.
continue with this approach until get the entire image using only a few tiles.
Image visualization
Start from the highest depth
When user drags the image, load the tiles dynamically
When the user zoom a region of the image, decrease the depth, loading the tiles for that region in a higher resolution.
The final result is similar to Google Maps.