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();
}
Related
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
}
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!
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.
It's embarrassing to ask this question but can't find an answer.
I tried this in vain.
Image resultImage = new Bitmap(image1.Width, image1.Height, PixelFormat.Format24bppRgb);
using (Graphics grp = Graphics.FromImage(resultImage))
{
grp.FillRectangle(
Brushes.White, 0, 0, image1.Width, image1.Height);
resultImage = new Bitmap(image1.Width, image1.Height, grp);
}
I basically want to fill a 1024x1024 RGB bitmap image with white in C#. How can I do that?
You almost had it:
private Bitmap DrawFilledRectangle(int x, int y)
{
Bitmap bmp = new Bitmap(x, y);
using (Graphics graph = Graphics.FromImage(bmp))
{
Rectangle ImageSize = new Rectangle(0,0,x,y);
graph.FillRectangle(Brushes.White, ImageSize);
}
return bmp;
}
You are assigning a new image to resultImage, thereby overwriting your previous attempt at creating a white image (which should succeed, by the way).
So just remove the line
resultImage = new Bitmap(image1.Width, image1.Height, grp);
Another approach,
Create a unit bitmap
var b = new Bitmap(1, 1);
b.SetPixel(0, 0, Color.White);
And scale it
var result = new Bitmap(b, 1024, 1024);
Graphics.Clear(Color)
Bitmap bmp = new Bitmap(1024, 1024);
using (Graphics g = Graphics.FromImage(bmp)){g.Clear(Color.White);}
Depending on your needs, a BitmapSource might suit. You can quickly fill a byte array with the 0xff (i.e. white) using Enumerable.Repeat().
int w = 1024;
int h = 1024;
byte[] pix = Enumerable.Repeat((byte)0xff, w * h * 4).ToArray();
SomeImage.Source = BitmapSource.Create(w, h, 96, 96, PixelFormats.Pbgra32, null, pix, w * 4);
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Resizing an Image without losing any quality
The code I always use on my applications, after user uploads image from FileUpload and save on the server reducing the size, is good. But when I do the same thing on photoshop(manually) and uploading a file on facebook the quality is whey better and the reduced size is very fair.
I just can't figure how can I improve the quality of my uploaded images on the server, does anybody use a different approach?
see code below:
private static byte[] ReturnReducedImage(byte[] original, int width, int height)
{
Image img = RezizeImage(Image.FromStream(BytearrayToStream(original)), width, height);
MemoryStream ms = new MemoryStream();
img.Save(ms, System.Drawing.Imaging.ImageFormat.Png);
return ms.ToArray();
}
private static Image RezizeImage(Image img, int maxWidth, int maxHeight)
{
if (img.Height < maxHeight && img.Width < maxWidth) return img;
Bitmap cpy = null;
using (img)
{
Double xRatio = (double)img.Width / maxWidth;
Double yRatio = (double)img.Height / maxHeight;
Double ratio = Math.Max(xRatio, yRatio);
int nnx = (int)Math.Floor(img.Width / ratio);
int nny = (int)Math.Floor(img.Height / ratio);
cpy = new Bitmap(nnx, nny);
using (Graphics gr = Graphics.FromImage(cpy))
{
gr.Clear(Color.Transparent);
gr.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.High;
gr.DrawImage(img, new Rectangle(0, 0, nnx, nny), new Rectangle(0, 0, img.Width, img.Height), GraphicsUnit.Pixel);
}
}
return cpy;
}
I believe this answers the question: https://stackoverflow.com/a/87786/910348.
You'll want to replace this:
using (Graphics gr = Graphics.FromImage(cpy))
{
gr.Clear(Color.Transparent);
gr.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.High;
gr.DrawImage(img, new Rectangle(0, 0, nnx, nny), new Rectangle(0, 0, img.Width, img.Height), GraphicsUnit.Pixel);
}
with something like this:
using (Graphics gr = Graphics.FromImage(cpy))
{
gr.Clear(Color.Transparent);
gr.SmoothingMode = SmoothingMode.AntiAlias;
gr.InterpolationMode = InterpolationMode.HighQualityBicubic;
gr.PixelOffsetMode = PixelOffsetMode.HighQuality;
gr.DrawImage(img, new Rectangle(0, 0, nnx, nny));
}
Cheers.