Best options for image resizing - c#

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);

Related

Change Bitmap PixelFormat from Format16bppGrayScale to Format32bppArgb - Out of Memory Exception

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.
}

Create multiple bitmap objects fast

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. :)

Create a bitmap in c# with specific pixelformat from an image without loading the image

sorry if this question is silly, i'm just starting with C#
I have an image in disk which I know the path. The image is 20000x10000 aprox in size and around 400MB in size (i know this looking at the image)
I need to load it in the code and resize it since the progam dies if I try to put in a picture box 400m of image, but if I do
Bitmap b0 = new Bitmap(pathImage);
int newWidth = (int)(b0.Width * escala);
int newHeight = (int)(b0.Height * escala);
// Convert other formats (including CMYK) to RGB.
Bitmap newImage = new Bitmap(newWidth, newHeight, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
// Draws the image in the specified size with quality mode set to HighQuality
using (Graphics graphics = Graphics.FromImage(newImage))
{
graphics.CompositingQuality = CompositingQuality.HighQuality;
graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
graphics.SmoothingMode = SmoothingMode.HighQuality;
graphics.DrawImage(b0, 0, 0, newWidth, newHeight);
}
But when I do Bitmap b0 = new Bitmap(pathImage); it just dies since 20000x10000x32 it's too much for my poor memory.
The problem is that if not specified, c# just uses as pixel format Format32bppArgb but I want Format1bppIndexed, and the only way i've found to change the format is with the constructor
Bitmap(
int width,
int height,
int stride,
PixelFormat format,
IntPtr scan0
)
But for this constructor I need to know the size of the image, which in the code I don't know because it dies before the code can get it and know the size...
I'm lost. Any ideas?
EDIT:
Doing this
int width;
int height;
Image tif;
using (FileStream file = new FileStream(rutaImagenEntrada, FileMode.Open, FileAccess.ReadWrite))
{
using ( tif = Image.FromStream(stream: file,
useEmbeddedColorManagement: false,
validateImageData: false))
{
width = (int)tif.PhysicalDimension.Width;
height = (int)tif.PhysicalDimension.Height;
}
file.Close();
}
I get the size of the image I cannot load, and can create a bitmap with the pixelformat and size that i wanted, BUT everyway I try to upload it doesn't work, it still crashes on me.
I've tried using graphics .draw(tiff...) and tiff.getThumbnail(new size...) and it just dies on me.
It targets x86 and the .Net 3.5 since those are the requirements of the client...
Any ideas?
If FileStream works to open the file, you might want to give some of the other constructors of Bitmap a try, in particular Bitmap(Stream).
If that doesn't help, you might try loading the raw bmp data, creating a Bitmap with the format you want and copying the data into the Bitmap manually (I'm not familiar with C# Bitmaps, but there should be some way of accessing the data).
Hope this helps.

Resizing High Quality images for web

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.

How to create a simple glass effect

I am currently painting a light blue, partly transparent overlay over owner-drawn objects to indicate certain state. It's OK but I thought that it would be even nicer if I could at some sort of glass effect to further establish the idea that the particular object has "something" overlaid over the top of it.
I thought that some glass streaks, for example, in addition to the blue transparency would lend a nice effect.
I've Googled around for GDI+ (and others) algorithms to do simple things painting like this but have come up empty. Links to any (fairly simple) algorithms in any language would be appreciated. I prefer .NET but can figure out the painting from pseudo-code on up.
Sorry, shoul've also specified that I need to target WinXP and using .NET version 2.0 - So unable to use WPF or Vista/Win7 goodies.
I've not done this myself but, have used codeproject source to render a sample...Try this:
http://www.codeproject.com/KB/GDI-plus/Image-Glass-Reflection.aspx
public static Image DrawReflection(Image _Image, Color _BackgroundColor, int _Reflectivity)
{
// Calculate the size of the new image
int height = (int)(_Image.Height + (_Image.Height * ((float)_Reflectivity / 255)));
Bitmap newImage = new Bitmap(_Image.Width, height, PixelFormat.Format24bppRgb);
newImage.SetResolution(_Image.HorizontalResolution, _Image.VerticalResolution);
using (Graphics graphics = Graphics.FromImage(newImage))
{
// Initialize main graphics buffer
graphics.Clear(_BackgroundColor);
graphics.DrawImage(_Image, new Point(0, 0));
graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
Rectangle destinationRectangle = new Rectangle(0, _Image.Size.Height,
_Image.Size.Width, _Image.Size.Height);
// Prepare the reflected image
int reflectionHeight = (_Image.Height * _Reflectivity) / 255;
Image reflectedImage = new Bitmap(_Image.Width, reflectionHeight);
// Draw just the reflection on a second graphics buffer
using (Graphics gReflection = Graphics.FromImage(reflectedImage))
{
gReflection.DrawImage(_Image,
new Rectangle(0, 0, reflectedImage.Width, reflectedImage.Height),
0, _Image.Height - reflectedImage.Height, reflectedImage.Width,
reflectedImage.Height, GraphicsUnit.Pixel);
}
reflectedImage.RotateFlip(RotateFlipType.RotateNoneFlipY);
Rectangle imageRectangle =
new Rectangle(destinationRectangle.X, destinationRectangle.Y,
destinationRectangle.Width,
(destinationRectangle.Height * _Reflectivity) / 255);
// Draw the image on the original graphics
graphics.DrawImage(reflectedImage, imageRectangle);
// Finish the reflection using a gradiend brush
LinearGradientBrush brush = new LinearGradientBrush(imageRectangle,
Color.FromArgb(255 - _Reflectivity, _BackgroundColor),
_BackgroundColor, 90, false);
graphics.FillRectangle(brush, imageRectangle);
}
return newImage;
}
I was actually able to achieve a basic glass effect by overlaying my image with a rectangle about one third the size of the image below that contains a gradient fill of white that starts at 25% opacity and goes to 75% opacity. This is single bit of painting produces a glassy "streak" that I was happy with. The same idea could be repeated a number of times with a variety of rect widths to produce several "streaks" that will give the illusion of a glass overlay.
You could try the Aero Glass function, if you are using Vista or Windows 7.
These might be helpful:
http://msdn.microsoft.com/en-us/library/aa969537%28VS.85%29.aspx#blurbehind
http://msdn.microsoft.com/en-us/library/ms748975.aspx

Categories

Resources