What's the fastest way to create 500+ Bitmaps from 500+ different image files? They need to be HQ resized and converted to the pixel format Format32bppPArgb for faster painting.
I'm creating each Bitmap as follows:
Bitmap resized;
using (Bitmap original = new Bitmap(pathToFile))
{
resized = new Bitmap(width, height, PixelFormat.Format32bppPArgb);
resized.SetResolution(original.HorizontalResolution, original.VerticalResolution);
using (Graphics g = Graphics.FromImage(resized))
{
g.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighQuality;
g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
g.DrawImage(original, 0, 0, width, height);
}
}
I'm running a new BackgroundWorker to handle each Bitmap creation, which speeds up the process substantially, but I'm still looking for more speed (without sacrificing image quality). Pre-sizing images is not a possibility for what I'm trying to do.
Any help would be greatly appreciated. :)
Related
I am trying to convert a grayscale Bitmap (Format16bppGrayScale) to a color Bitmap (Format32bppArgb) like so:
Bitmap color = gray.Clone(new Rectangle(0, 0, gray.Width, gray.Height), PixelFormat.Format32bppArgb);
I keep getting a System.OutOfMemoryException. I have been researching and this error usually occurs when the rectangle provided to Clone is bigger than the actual image that you are trying to clone. This is not the case here since I am using the image dimensions to create the rectangle. Are there known issues with this type of conversions? Are there any other ways to achieve a copy in a different PixelFormat?
Thanks,
I read, people have problem like you. Try this way:
Change PixelFormat, while you try clone, system have problem.
Biggest reason is new definition of Bitmap.
Bitmap clone = new Bitmap(gray.Width, gray.Height, PixelFormat.Format32bppArgb);
using (Graphics gr = Graphics.FromImage(clone)) {
gr.DrawImage(color, new Rectangle(0, 0, clone.Width, clone.Height));
}
Or without DrawImage function:
using (Bitmap color = gray.Clone(new Rectangle(0, 0, gray.Width, gray.Height), PixelFormat.Format32bppArgb))
{
// color is now in the desired format.
}
Hi Guys I am resizing Images in C# and saving them as .png.
public static Bitmap resize(int x, int y, Image p)
{
Bitmap Img = new Bitmap(x,y);
using (Graphics gr = Graphics.FromImage(Img))
{
gr.SmoothingMode = SmoothingMode.HighQuality;
gr.InterpolationMode = InterpolationMode.HighQualityBicubic;
gr.PixelOffsetMode = PixelOffsetMode.HighQuality;
gr.DrawImage(p, new Rectangle(0, 0, x, y));
}
return Img;
}
The problem is that when a user uploads a high quality image such as Images from high end smartphones such as galaxys, iphones etc the filesize of the resulting resized image is quite high.
I have constructed a table of resized images file size and pixel size.
1) Img1 1280 * 666px => 458 kb
2) Img1 300 * 399px => 221kb
3) Img2 1280 * 1444px => 2.08mb
4) Img2 300 * 451px => 327kb
I know I have interpolation and smoothing set to high quality for example purposes and saving images as .png to preserve transparency. But what setting should I change to obtain a Image of reasonable quality and filesize ideal for HTML pages. My end goal is making a compromise between Image quality and FileSize. So please point me to to some link demonstrating Filsesize vs Quality comparison for quality of resized images so that I can make the choice myself.
There is one option you can play with, the PixelFormat.
using (Bitmap bmp = new Bitmap(x, y,
System.Drawing.Imaging.PixelFormat.Format32bppArgb))
{
using (Graphics gr = Graphics.FromImage(bmp))
{
gr.SmoothingMode = SmoothingMode.HighQuality;
gr.InterpolationMode = InterpolationMode.HighQualityBicubic;
gr.PixelOffsetMode = PixelOffsetMode.HighQuality;
gr.DrawImage(src, new Rectangle(0, 0, x, y));
}
}
Use 32 bit Argb only if you need the alpha channel (e.g. the image is transparent). You can also query the img.PixelFormat to see if the image even contains an alpha channel!
In this case you don't have to save it as PNG and can go with JPG which will result in lot smaller files!
Normal jpg usually use Format24bppRgb.
Use JPEG instead and adjust the compression for the quality you need.
I am trying to draw a large image represented as set of tiles on a System.Drawing.Graphics surface.
All is great when I do not need to scale the Graphics. But when I do, I get empty lines between tiles.
How can I draw tiles on a Graphics without gaps between tiles?
Or shall I look somewhere else? Maybe you know non-framework classes, 3rd-party libraries etc. that I could use to overcome the issue?
Below are two sample tiles, image, showing the issue and code to reproduce the issue.
float scale = 1.66f;
int width = (int)(128 * scale);
int height = (int)(256 * scale);
using (Bitmap result = new Bitmap(width, height))
{
using (Graphics graphics = Graphics.FromImage(result))
{
graphics.ScaleTransform(scale, scale);
// tried lines below, DOES NOT always help
//graphics.SmoothingMode = SmoothingMode.HighQuality;
//graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
//graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
//graphics.CompositingQuality = CompositingQuality.HighQuality;
int top;
using (Image bitmap = Image.FromFile("topTile.png"))
{
graphics.DrawImage(bitmap, 0, 0, bitmap.Width, bitmap.Height);
top = bitmap.Height;
}
using (Image bitmap = Image.FromFile("bottomTile.png"))
graphics.DrawImage(bitmap, 0, top, bitmap.Width, bitmap.Height);
}
result.Save("result.png");
}
I notice that you're creating bitmaps that have integral width and height by multiplying the scale factor by an initial width and height and then rounding (or likely truncating) to the nearest integer. However, you then do a scale transformation by the floating point scale amount, so the scale amount won't quite match the actual bitmap width and height. Maybe adjusting the scale amount used in the call to ScaleTransformation() would fix the issue?
I had a similar problem, I managed to solve the following lines:
graphics.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.HighQuality;
graphics.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.NearestNeighbor;
I have been having a tough time creating a thumbnail that is not horrible quality. So far the best code i've come up with is:
Bitmap bmp = new Bitmap(width, height);
Graphics graphic = Graphics.FromImage(bmp);
graphic.InterpolationMode = InterpolationMode.HighQualityBicubic;
graphic.SmoothingMode = SmoothingMode.HighQuality;
graphic.PixelOffsetMode = PixelOffsetMode.HighQuality;
graphic.CompositingQuality = CompositingQuality.HighQuality;
graphic.DrawImage(photo, 0, 0, width, height);
return imageToByteArray(bmp);
Which produces this gem:
If I resize the same image in Paint.NET i get this:
Which is WAY better. Everything I've found on line points me to some variation of the code I have above. I know Paint.NET was open source at one point. Does anyone know what magic they were doing to create such nice resize functionality and if that functionality can be reproduced in C#?
UPDATE:
The original image from this example was a jpg
GIFs
I recalled reading that .NET has issues with palette-based formats, like GIF, so I dug up a few articles.
This article describes how to quantize (pick an optimum palette) to improve quality: http://msdn.microsoft.com/en-us/library/aa479306.aspx, as does this (badly formatted) article.
In brief, I believe GDI+ picks a non-optimum palette when performing the resize.
PNGs
PNGs are palette-based, so they may be prone to the same issues as GIFs. I'm not sure if it matters that the palette can be much larger.
JPEG-friendly example
This code should work fine on JPEGs (but does not render GIFs smoothly). If you try it and it pixelates a JPEG, then there is probably something else going on.
private static byte[] GetScaledImage( byte[] inputBytes, int width, int height ) {
Image img = null;
using( MemoryStream ms = new MemoryStream() ) {
ms.Write( inputBytes, 0, inputBytes.Length );
img = Image.FromStream( ms );
}
using( MemoryStream ms = new MemoryStream() ) {
using( Image newImg = new Bitmap( width, height ) ) {
using( Graphics g = Graphics.FromImage( newImg ) ) {
g.InterpolationMode = InterpolationMode.HighQualityBicubic;
g.DrawImage( img, 0, 0, width, height );
newImg.Save( ms, img.RawFormat );
return ms.GetBuffer();
}
}
}
}
since Bitmap(int,int) is effectively Bitmap(int,int,PixelFormat.Format32bppArgb) I think the problem is in source image. Try to create another intermediate copy of the image of the same size as source image if using palette, then use that 32bppArgb image source for your resize function.
I have to resize images exceeding a max size. Different methods I tried so far are not good enough:
System.Drawing.Image.GetThumbnailImage generates very poor quality images in general.
Playing with options like this one I can generate better images in quality but heavier than the original one.
Probably the second option (or something similar) is the best option and I would need to resize using the proper options.
Any advice?
EDIT
My option 2 was generating heavier images for some specific pictures. In general is working as expected so I'd say this is solved.
Try something like this:
public Bitmap Resize(Bitmap originalImage, int newWidth)
{
int newHeight = (int)Math.Round(originalImage.Height * (decimal)newWidth / originalImage.Width, 0);
var destination = new Bitmap(newWidth, newHeight);
using (Graphics g = Graphics.FromImage(destination))
{
g.SmoothingMode = SmoothingMode.AntiAlias;
g.InterpolationMode = InterpolationMode.HighQualityBicubic;
g.PixelOffsetMode = PixelOffsetMode.HighQuality;
g.DrawImage(originalImage, 0, 0, newWidth, newHeight);
}
return destination;
}
I would go with using the WPF libs instead of the GDI+ libs. The WPF libs perform faster and I think they yield better results compared to the GDI+ libs.
Check out these excellent posts from Bertrand Le Roy.
Resizing images from the server using WPF/WIC instead of GDI+
Server-side resizing with WPF: now with JPG
The fastest way to resize images from ASP.NET. And it’s (more) supported-ish
Create a new Bitmap object, then using the Graphics object, re-draw the old image into the new image's buffer at the increased/decreased size based on the resize engine you want.
// inImage is your original
Bitmap outImage = new Bitmap(newWid, newHei);
Graphics gfx = Graphics.FromImage(outImage);
gfx.InterpolationMode = InterpolationMode.HighQualityBicubic;
gfx.DrawImage(inImage,
new Rectangle(0, 0, newWid, new Hei),
new Rectangle(0, 0, inImage.Width, inImage.Height),
GraphicsUnit.Pixel);