Grey and unsharp images when resizing using GDI+ - c#

I'm using asp.net mvc 3 to stream resized images. But the output of the image is greyish and blury eventhough I've set smoothingmode and interpolationmode to highbiqubic.
public ActionResult ImageTEST(int fileID, int width, int height)
{
var file = _fileRep.GetFile(fileID);
byte[] newFile;
float ratioX = (float)width / (float)file.Width;
float ratioY = (float)height / (float)file.Height;
float ratio = Math.Min(ratioX, ratioY);
int newWidth = (int)(file.Width * ratio);
int newHeight = (int)(file.Height * ratio);
using (var resizedImage = new Bitmap(newWidth, newHeight))
{
using (var source = new Bitmap(new MemoryStream(file.FileContent)))
{
using (var g = Graphics.FromImage(resizedImage))
{
g.SmoothingMode = SmoothingMode.HighQuality;
g.InterpolationMode = InterpolationMode.HighQualityBicubic;
g.DrawImage(source, 0, 0, newWidth, newHeight);
}
}
using (var ms = new MemoryStream())
{
resizedImage.Save(ms, System.Drawing.Imaging.ImageFormat.Jpeg);
newFile = ms.ToArray();
}
}
return new FileContentResult(newFile, "image/jpeg");
}
Result:
The right one is the exakt same picture but resized in photoshop.
How can I tune this to make quality much better?

First, try to save in a higher quality.
EncoderParameters ep = new EncoderParameters();
ep.Param[0] = new EncoderParameter(System.Drawing.Imaging.Encoder.Quality, (long)100);
foo.Save(filename, ici, ep);
If that is not satisfiable, you may need to use other libraries such as Emgu cv.
The grey problem may because that the color space (AdobeRGB or sRGB) of original image and the one you saved are not the same.

Related

A generic error occurred in GDI+ when I resize image file [duplicate]

This question already has answers here:
A generic error occurred in GDI+, JPEG Image to MemoryStream
(36 answers)
Closed 3 years ago.
I'm resizing an image, what could be wrong with my code?
var newSize = ResizeImageFile(ConvertToBytes(myFile), 2048);
using (MemoryStream ms = new MemoryStream(newSize, 0, newSize.Length))
{
ms.Write(newSize, 0, newSize.Length);
using (Image image = Image.FromStream(ms, true))
{
image.Save(targetLocation, ImageFormat.Jpeg);
}
}
I have used this function to resize my image
public static byte[] ResizeImageFile(byte[] imageFile, int targetSize) // Set targetSize to 1024
{
using (Image oldImage = Image.FromStream(new MemoryStream(imageFile)))
{
Size newSize = CalculateDimensions(oldImage.Size, targetSize);
using (Bitmap newImage = new Bitmap(newSize.Width, newSize.Height, PixelFormat.Format24bppRgb))
{
using (Graphics canvas = Graphics.FromImage(newImage))
{
canvas.SmoothingMode = SmoothingMode.AntiAlias;
canvas.InterpolationMode = InterpolationMode.HighQualityBicubic;
canvas.PixelOffsetMode = PixelOffsetMode.HighQuality;
canvas.DrawImage(oldImage, new Rectangle(new Point(0, 0), newSize));
MemoryStream m = new MemoryStream();
newImage.Save(m, ImageFormat.Jpeg);
return m.GetBuffer();
}
}
}
}
I have been looking for answers for a long time, please help me.
Thank you
Since the both CalculateDimensions and ConvertToBytes methods are not shown, I tried to assume that the above methods are something like as follows:
// Calculate the Size at which the image width and height is lower than the specified value
// (Keep the aspect ratio)
private static Size CalculateDimensions(Size size, int targetSize)
{
double rate = Math.Max(size.Width * 1.0 / targetSize, size.Height * 1.0 / targetSize);
int w = (int)Math.Floor(size.Width / rate);
int h = (int)Math.Floor(size.Height / rate);
return new Size(w, h);
}
//Convert image file to byte array
private static byte[] ConvertToBytes(string fileName)
{
var result = File.ReadAllBytes(fileName);
return result;
}
If your code does not work well, then some problems must be in the above methods.

How to Resize Center and Crop an image with ImageSharp

I need to convert some System.Drawing based code to use this .NET Core compatible library:
https://github.com/SixLabors/ImageSharp
The System.Drawing based code below resizes an image and crops of the edges, returning the memory stream to then be saved. Is this possible with the ImageSharp library?
private static Stream Resize(Stream inStream, int newWidth, int newHeight)
{
var img = Image.Load(inStream);
if (newWidth != img.Width || newHeight != img.Height)
{
var ratioX = (double)newWidth / img.Width;
var ratioY = (double)newHeight / img.Height;
var ratio = Math.Max(ratioX, ratioY);
var width = (int)(img.Width * ratio);
var height = (int)(img.Height * ratio);
var newImage = new Bitmap(width, height);
Graphics.FromImage(newImage).DrawImage(img, 0, 0, width, height);
img = newImage;
if (img.Width != newWidth || img.Height != newHeight)
{
var startX = (Math.Max(img.Width, newWidth) - Math.Min(img.Width, newWidth)) / 2;
var startY = (Math.Max(img.Height, newHeight) - Math.Min(img.Height, newHeight)) / 2;
img = Crop(img, newWidth, newHeight, startX, startY);
}
}
var ms = new MemoryStream();
img.Save(ms, ImageFormat.Jpeg);
ms.Position = 0;
return ms;
}
private static Image Crop(Image image, int newWidth, int newHeight, int startX = 0, int startY = 0)
{
if (image.Height < newHeight)
newHeight = image.Height;
if (image.Width < newWidth)
newWidth = image.Width;
using (var bmp = new Bitmap(newWidth, newHeight, PixelFormat.Format24bppRgb))
{
bmp.SetResolution(72, 72);
using (var g = Graphics.FromImage(bmp))
{
g.SmoothingMode = SmoothingMode.AntiAlias;
g.InterpolationMode = InterpolationMode.HighQualityBicubic;
g.PixelOffsetMode = PixelOffsetMode.HighQuality;
g.DrawImage(image, new Rectangle(0, 0, newWidth, newHeight), startX, startY, newWidth, newHeight, GraphicsUnit.Pixel);
var ms = new MemoryStream();
bmp.Save(ms, ImageFormat.Jpeg);
image.Dispose();
var outimage = Image.FromStream(ms);
return outimage;
}
}
}
Yeah, super easy.
using (var inStream = ...)
using (var outStream = new MemoryStream())
using (var image = Image.Load(inStream, out IImageFormat format))
{
image.Mutate(
i => i.Resize(width, height)
.Crop(new Rectangle(x, y, cropWidth, cropHeight)));
image.Save(outStream, format);
}
EDIT
If you want to leave the original image untouched you can use the Clone method instead.
using (var inStream = ...)
using (var outStream = new MemoryStream())
using (var image = Image.Load(inStream, out IImageFormat format))
{
var clone = image.Clone(
i => i.Resize(width, height)
.Crop(new Rectangle(x, y, cropWidth, cropHeight)));
clone.Save(outStream, format);
}
You might even be able to optimize this into a single method call to Resize via the overload that accepts a ResizeOptions instance with `ResizeMode.Crop. That would allow you to resize to a ratio then crop off any excess outside that ratio.
So here's relevent code after so far after converting to not use original methods:
using (var fullSizeStream = new MemoryStream())
using (var smallStream = new MemoryStream())
using (var thumbStream = new MemoryStream())
using (var reviewThumbStream = new MemoryStream())
using (var image = Image.Load(inStream))
{
// Save original constrained
var clone = image.Clone(context => context
.Resize(new ResizeOptions
{
Mode = ResizeMode.Max,
Size = new Size(1280, 1280)
}));
clone.Save(fullSizeStream, new JpegEncoder { Quality = 80 });
//Save three sizes Cropped:
var jpegEncoder = new JpegEncoder { Quality = 75 };
clone = image.Clone(context => context
.Resize(new ResizeOptions
{
Mode = ResizeMode.Crop,
Size = new Size(277, 277)
}));
clone.Save(smallStream, jpegEncoder);
clone = image.Clone(context => context
.Resize(new ResizeOptions
{
Mode = ResizeMode.Crop,
Size = new Size(100, 100)
}));
clone.Save(thumbStream, jpegEncoder);
clone = image.Clone(context => context
.Resize(new ResizeOptions
{
Mode = ResizeMode.Crop,
Size = new Size(50, 50)
}));
clone.Save(reviewThumbStream, jpegEncoder);
//...then I just save the streams to blob storage
}

Resize code ruins quality? [duplicate]

This question already has an answer here:
How resize image without losing quality
(1 answer)
Closed 5 years ago.
So I'm trying to resize HUGE images, like 5500x3500 JPGs down to 1024x768 or less. The following function works, but it really runs the quality, makes the text all fuzzy.
static public Bitmap ScaleImage(Image image, int maxWidth, int maxHeight)
{
var ratioX = (double)maxWidth / image.Width;
var ratioY = (double)maxHeight / image.Height;
var ratio = Math.Min(ratioX, ratioY);
var newWidth = (int)(image.Width * ratio);
var newHeight = (int)(image.Height * ratio);
var newImage = new Bitmap(newWidth, newHeight);
Graphics.FromImage(newImage).DrawImage(image, 0, 0, newWidth, newHeight);
Bitmap bmp = new Bitmap(newImage);
return bmp;
}
Any idea of a better way to do this?
As suggested in comment, you should use Interpolation mode to control image quality during scaling. To know how to use it, refer to the following doc from Microsoft,
https://learn.microsoft.com/en-us/dotnet/framework/winforms/advanced/how-to-use-interpolation-mode-to-control-image-quality-during-scaling
You should set the quality-properties in Graphics-object to HighQuality:
static public Bitmap ScaleImage(Image image, int maxWidth, int maxHeight)
{
var ratioX = (double)maxWidth / image.Width;
var ratioY = (double)maxHeight / image.Height;
var ratio = Math.Min(ratioX, ratioY);
var newWidth = (int)(image.Width * ratio);
var newHeight = (int)(image.Height * ratio);
using (Bitmap newImage = new Bitmap(newWidth, newHeight))
{
using (Graphics g = Graphics.FromImage(newImage))
{
g.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighQuality;
g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
g.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.HighQuality;
Bitmap bmp = new Bitmap(newImage);
return bmp;
}
}
}
Also be sure to use using-block with objects that implements iDisposable: Image, Bitmap and Graphichs all do.

Compressing large image to small format

I have images stored in DB as binary, to show them I want to compress them to smaller images (4000 x 3000) to (400 x 300), which basicly works however the images look awefull, can somebody point me to the right direction?
I am using now:
System.IO.MemoryStream myMemStream = new System.IO.MemoryStream(bytes);
System.Drawing.Image fullsizeImage = System.Drawing.Image.FromStream(myMemStream);
Type typeoff = fullsizeImage.GetType();
double height = fullsizeImage.Height;
double width = Convert.ToDouble(fullsizeImage.Width);
double aspect = setWidth / width;
setHeight = Convert.ToInt32(aspect * height);
System.Drawing.Image newImage = fullsizeImage.GetThumbnailImage(Convert.ToInt32(setWidth), setHeight, null, IntPtr.Zero);
System.IO.MemoryStream myResult = new System.IO.MemoryStream();
using (System.IO.MemoryStream imageMemStream = new System.IO.MemoryStream(bytes))
{
using (Bitmap bitmap = new Bitmap(imageMemStream))
{
ImageFormat imageFormat = bitmap.RawFormat;
if (bitmap.RawFormat.Equals(System.Drawing.Imaging.ImageFormat.Jpeg))
{
newImage.Save(myResult, System.Drawing.Imaging.ImageFormat.Jpeg);
}
if (bitmap.RawFormat.Equals(System.Drawing.Imaging.ImageFormat.Gif))
{
newImage.Save(myResult, System.Drawing.Imaging.ImageFormat.Gif);
}
if (bitmap.RawFormat.Equals(System.Drawing.Imaging.ImageFormat.Bmp))
{
newImage.Save(myResult, System.Drawing.Imaging.ImageFormat.Bmp);
}
if (bitmap.RawFormat.Equals(System.Drawing.Imaging.ImageFormat.Png))
{
newImage.Save(myResult, System.Drawing.Imaging.ImageFormat.Png);
}
if (bitmap.RawFormat.Equals(System.Drawing.Imaging.ImageFormat.Icon))
{
newImage.Save(myResult, System.Drawing.Imaging.ImageFormat.Icon);
}
}
}
_bytes = myResult.ToArray(); //Returns a new byte array.
Have been looking to this but have no idea yet how to implenet ths with my binary in and output:
Bitmap image = new Bitmap(fullsizeImage, Convert.ToInt32(newWidth), setHeight);
using (Graphics gr = Graphics.FromImage(image))
{
gr.SmoothingMode = SmoothingMode.HighQuality;
gr.InterpolationMode = InterpolationMode.HighQualityBicubic;
gr.PixelOffsetMode = PixelOffsetMode.HighQuality;
gr.DrawImage(fullsizeImage, new Rectangle(0, 0, Convert.ToInt32(newWidth), setHeight));
_bytes = gr.T.ToArray();
}
Probebly I do something wrong but have no idea where to look to do this right, have not much experience in image compression.
Any help would be appriciated
UPDATE
trying to get out of the Image the mime type but not very lucky to get it, using this and cant find any other, code has a null return.
public byte[] imageToByteArray(System.Drawing.Image newImage)
{
System.IO.MemoryStream ms = new System.IO.MemoryStream();
ImageFormat format = newImage.RawFormat;
if (ImageCodecInfo.GetImageDecoders().FirstOrDefault(c => c.FormatID == format.Guid) != null)
{
ImageCodecInfo codec = ImageCodecInfo.GetImageDecoders().FirstOrDefault(c => c.FormatID == format.Guid);
string mimeType = codec.MimeType;
}
newImage.Save(ms, System.Drawing.Imaging.ImageFormat.Jpeg);
return ms.ToArray();
You can use this function to create the reduced Images:
public static Image ShrinkImage(Image original, int scale)
{
Bitmap bmp = new Bitmap(original.Width / scale, original.Height / scale,
original.PixelFormat);
using (Graphics G = Graphics.FromImage(bmp))
{
G.InterpolationMode = InterpolationMode.HighQualityBicubic;
G.SmoothingMode = SmoothingMode.HighQuality;
Rectangle srcRect = new Rectangle(0,0,original.Width, original.Height);
Rectangle destRect = new Rectangle(0,0,bmp.Width, bmp.Height);
G.DrawImage(original, destRect, srcRect, GraphicsUnit.Pixel);
bmp.SetResolution( original.HorizontalResolution, original.VerticalResolution);
}
return (Image)bmp;
}
Note that it can only work with real Bitmap Images, not with Icons; but it makes little sense trying to reduce icons anyway!
Also note that you may or may not want to change the Dpi of the new Images. In the code I don't but maybe you want to scale it up or set it to a fixed value..
Don't forget to Dispose of your Images, when you're done with them!

Using C# how can I resize a jpeg image?

Using C# how can I resize a jpeg image? A code sample would be great.
I'm using this:
public static void ResizeJpg(string path, int nWidth, int nHeight)
{
using (var result = new Bitmap(nWidth, nHeight))
{
using (var input = new Bitmap(path))
{
using (Graphics g = Graphics.FromImage((System.Drawing.Image)result))
{
g.DrawImage(input, 0, 0, nWidth, nHeight);
}
}
var ici = ImageCodecInfo.GetImageEncoders().FirstOrDefault(ie => ie.MimeType == "image/jpeg");
var eps = new EncoderParameters(1);
eps.Param[0] = new EncoderParameter(System.Drawing.Imaging.Encoder.Quality, 100L);
result.Save(path, ici, eps);
}
}
Good free resize filter and example code.
http://code.google.com/p/zrlabs-yael/
private void MakeResizedImage(string fromFile, string toFile, int maxWidth, int maxHeight)
{
int width;
int height;
using (System.Drawing.Image image = System.Drawing.Image.FromFile(fromFile))
{
DetermineResizeRatio(maxWidth, maxHeight, image.Width, image.Height, out width, out height);
using (System.Drawing.Image thumbnailImage = image.GetThumbnailImage(width, height, new System.Drawing.Image.GetThumbnailImageAbort(ThumbnailCallback), IntPtr.Zero))
{
if (image.Width < thumbnailImage.Width && image.Height < thumbnailImage.Height)
File.Copy(fromFile, toFile);
else
{
ImageCodecInfo ec = GetCodecInfo();
EncoderParameters parms = new EncoderParameters(1);
parms.Param[0] = new EncoderParameter(Encoder.Compression, 40);
ZRLabs.Yael.BasicFilters.ResizeFilter rf = new ZRLabs.Yael.BasicFilters.ResizeFilter();
//rf.KeepAspectRatio = true;
rf.Height = height;
rf.Width = width;
System.Drawing.Image img = rf.ExecuteFilter(System.Drawing.Image.FromFile(fromFile));
img.Save(toFile, ec, parms);
}
}
}
}
C# (or rather: the .NET framework) itself doesn't offer such capability, but it does offer you Bitmap from System.Drawing to easily access the raw pixel data of various picture formats. For the rest, see http://en.wikipedia.org/wiki/Image_scaling
Nice example.
public static Image ResizeImage(Image sourceImage, int maxWidth, int maxHeight)
{
// Determine which ratio is greater, the width or height, and use
// this to calculate the new width and height. Effectually constrains
// the proportions of the resized image to the proportions of the original.
double xRatio = (double)sourceImage.Width / maxWidth;
double yRatio = (double)sourceImage.Height / maxHeight;
double ratioToResizeImage = Math.Max(xRatio, yRatio);
int newWidth = (int)Math.Floor(sourceImage.Width / ratioToResizeImage);
int newHeight = (int)Math.Floor(sourceImage.Height / ratioToResizeImage);
// Create new image canvas -- use maxWidth and maxHeight in this function call if you wish
// to set the exact dimensions of the output image.
Bitmap newImage = new Bitmap(newWidth, newHeight, PixelFormat.Format32bppArgb);
// Render the new image, using a graphic object
using (Graphics newGraphic = Graphics.FromImage(newImage))
{
using (var wrapMode = new ImageAttributes())
{
wrapMode.SetWrapMode(WrapMode.TileFlipXY);
newGraphic.DrawImage(image, destRect, 0, 0, image.Width, image.Height, GraphicsUnit.Pixel, wrapMode);
}
// Set the background color to be transparent (can change this to any color)
newGraphic.Clear(Color.Transparent);
// Set the method of scaling to use -- HighQualityBicubic is said to have the best quality
newGraphic.InterpolationMode = InterpolationMode.HighQualityBicubic;
// Apply the transformation onto the new graphic
Rectangle sourceDimensions = new Rectangle(0, 0, sourceImage.Width, sourceImage.Height);
Rectangle destinationDimensions = new Rectangle(0, 0, newWidth, newHeight);
newGraphic.DrawImage(sourceImage, destinationDimensions, sourceDimensions, GraphicsUnit.Pixel);
}
// Image has been modified by all the references to it's related graphic above. Return changes.
return newImage;
}
Source : http://mattmeisinger.com/resize-image-c-sharp
ImageMagick should be the best way. Easy and reliable.
using (var image = new MagickImage(imgfilebuf))
{
image.Resize(len, len);
image.Strip();
using MemoryStream ms = new MemoryStream();
image.Write(ms);
return ms.ToArray();
}

Categories