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.
Related
I have code which I use to crop an image to a specified size. It does the job apart from the fact that, when I crop a bitonal image, it converts it to 24-bit colour which I don't want. Here's a snippet from the code ...
using (var Bmp = new Bitmap(FileImage.Width, Height))
{
using (var Graphic = Graphics.FromImage(Bmp))
{
var MemoryStreamTemp = new MemoryStream();
Graphic.DrawImage(FileImage, new Rectangle(0, 0, FileImage.Width, Height), x, CheckY, FileImage.Width, Height, GraphicsUnit.Pixel);
Bmp.Save(MemoryStreamTemp, ImageFormat.Tiff);
When bmp is created, it defaults to 24-bit colour. I realise that I can specify 1bpp indexed but that causes an exception when the Graphic object is instantiated.
I could convert the bitmap back to bitonal after it is cropped but that seems an unnecessary step that I would prefer to avoid.
Incidentally, I have a similar problem with the file's compression and resolution but that's for another day.
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. :)
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.
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'm having trouble cropping an image in an MVC3 C# application. I am cropping the image but its losing quality when rendered back in a view.
The image to be cropped is loaded from a database and is created from a ByteArray like so...
public static Image ByteArrayToImage(byte[] byteArrayIn)
{
MemoryStream ms = new MemoryStream(byteArrayIn);
Image returnImage = Image.FromStream(ms);
return returnImage;
}
When this image is rendered on a view it looks fine and is off the expected quality.
I am then selecting a region to crop and using the below method to do the crop...
public Image CropImage(System.Drawing.Image Image, int Height, int Width, int StartAtX, int StartAtY)
{
Image outimage;
MemoryStream mm = null;
try
{
//check the image height against our desired image height
if (Image.Height < Height)
{
Height = Image.Height;
}
if (Image.Width < Width)
{
Width = Image.Width;
}
//create a bitmap window for cropping
Bitmap bmPhoto = new Bitmap(Width, Height, PixelFormat.Format24bppRgb);
bmPhoto.SetResolution(Image.VerticalResolution,Image.HorizontalResolution);
//create a new graphics object from our image and set properties
Graphics grPhoto = Graphics.FromImage(bmPhoto);
grPhoto.SmoothingMode = SmoothingMode.AntiAlias;
grPhoto.InterpolationMode = InterpolationMode.HighQualityBicubic;
grPhoto.PixelOffsetMode = PixelOffsetMode.HighQuality;
//now do the crop
grPhoto.DrawImage(Image, new Rectangle(0, 0, Width, Height), StartAtX, StartAtY, Width, Height, GraphicsUnit.Pixel);
// Save out to memory and get an image from it to send back out the method.
mm = new MemoryStream();
bmPhoto.Save(mm, System.Drawing.Imaging.ImageFormat.Jpeg);
Image.Dispose();
bmPhoto.Dispose();
grPhoto.Dispose();
outimage = Image.FromStream(mm);
return outimage;
}
catch (Exception ex)
{
throw new Exception("Error cropping image, the error was: " + ex.Message);
}
}
This cropped image is then converted back to a ByteArray and can be saved into a database like so...
public static byte[] ImageToByteArray(System.Drawing.Image imageIn)
{
MemoryStream ms = new MemoryStream();
imageIn.Save(ms, System.Drawing.Imaging.ImageFormat.Gif);
return ms.ToArray();
}
When this image is later rendered on the view its quality is a lot worse than the original image. It looks quite pixilated.
The original image in this case is .jpg but could be any format.
This is an image of the image after it has been loaded from the database and being cropped...
This image is the result of the crop. As you can see its not good.
I have seen some other posts on the topic but they haven’t helped. Can anyone suggest a solution?
Thank you
This is a great blog that sums it all up. I have it working in my application now. http://blog.tallan.com/2011/02/04/using-mvc3-razor-helpers-and-jcrop-to-upload-and-crop-images/
Gunnar Peipman has a blog post about the quality called Resizing images without loss of quality. Gunnar says some trick about what is going wrong;
If the Image contains an embedded thumbnail image, this method
retrieves the embedded thumbnail and scales it to the requested size.
If the Image does not contain an embedded thumbnail image, this method
creates a thumbnail image by scaling the main image.
The GetThumbnailImage method works well when the requested thumbnail
image has a size of about 120 x 120 pixels. If you request a large
thumbnail image (for example, 300 x 300) from an Image that has an
embedded thumbnail, there could be a noticeable loss of quality in the
thumbnail image. It might be better to scale the main image (instead
of scaling the embedded thumbnail) by calling the DrawImage method.
And razor syntax is amicable with JCrop and there is a cool blog post about how you can use it.