How to Resize Center and Crop an image with ImageSharp - c#

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
}

Related

Is it possible to create a qr code using ZXing.Net without a quiet zone?

(How) is it possible to create a qr code using ZXing.Net without a quiet zone?
This is my current code:
BarcodeWriter barcodeWriter = new BarcodeWriter();
barcodeWriter.Format = BarcodeFormat.QR_CODE;
barcodeWriter.Renderer = new BitmapRenderer();
EncodingOptions encodingOptions = new EncodingOptions();
encodingOptions.Width = 500;
encodingOptions.Height = 500;
encodingOptions.Margin = 0;
encodingOptions.Hints.Add(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.L);
barcodeWriter.Options = encodingOptions;
bitmap = barcodeWriter.Write(compressedText);
Thanks!
ZXing.Net doesn't support image scaling with anti-aliasing. That means it can only resize by integer values. In your case you should create the smallest possible image and resize the resulting image with an image manipulation library or the Bitmap and Graphics classes from the framework.
var barcodeWriter = new BarcodeWriter
{
Format = BarcodeFormat.QR_CODE
};
// set width and height to 1 to get the smallest possible representation without a quiet zone around the qr code
var encodingOptions = new EncodingOptions
{
Width = 1,
Height = 1,
Margin = 0
};
encodingOptions.Hints.Add(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.L);
barcodeWriter.Options = encodingOptions;
var bitmap = barcodeWriter.Write(compressedText);
// scale the image to the desired size
var scaledBitmap = ScaleImage(bitmap, 500, 500);
private static Bitmap ScaleImage(Bitmap bmp, int maxWidth, int maxHeight)
{
var ratioX = (double)maxWidth / bmp.Width;
var ratioY = (double)maxHeight / bmp.Height;
var ratio = Math.Min(ratioX, ratioY);
var newWidth = (int)(bmp.Width * ratio);
var newHeight = (int)(bmp.Height * ratio);
var newImage = new Bitmap(newWidth, newHeight, PixelFormat.Format24bppRgb);
using (var graphics = Graphics.FromImage(newImage))
{
graphics.InterpolationMode = InterpolationMode.NearestNeighbor;
graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
graphics.DrawImage(bmp, 0, 0, newWidth, newHeight);
}
return newImage;
}

C# Crop Image Using Co-ordinates

I'm battling with this for a while now and can't seem to get any results.
I'm using methods from this SO QA > How to crop an image using C#?
I don't get any errors :/ The code just runs, but the image does not get cropped.
Code:
string fileNameWitPath = "finename.png";
fileNameWitPath = context.Server.MapPath("~/content/branding/" + context.Request.QueryString["userId"] + "/logo" + "/" + fileNameWitPath)
using (FileStream fs = new FileStream(fileNameWitPath, FileMode.Open))
{
using (BinaryWriter bw = new BinaryWriter(fs))
{
//get co-ords
int x1 = Convert.ToInt32(context.Request.QueryString["x1"].Trim());
int y1 = Convert.ToInt32(context.Request.QueryString["y1"].Trim());
int x2 = Convert.ToInt32(context.Request.QueryString["x2"].Trim());
int y2 = Convert.ToInt32(context.Request.QueryString["y2"].Trim());
Bitmap b = new Bitmap(fs);
Bitmap nb = new Bitmap((x2 - x1), (y2 - y1));
Graphics g = Graphics.FromImage(nb);
//g.DrawImage(b, x2, y2);
Rectangle cropRect = new Rectangle(x1, y1, nb.Width, nb.Height);
g.DrawImage(b, new Rectangle(x1, y1, nb.Width, nb.Height), cropRect, GraphicsUnit.Pixel);
Byte[] data;
using (var memoryStream = new MemoryStream())
{
nb.Save(memoryStream, ImageFormat.Png);
data = memoryStream.ToArray();
}
bw.Write(data);
bw.Close();
}
}
You can also do it copying pixels between bitmaps with Marshal.Copy:
static void Main()
{
var srcBitmap = new Bitmap(#"d:\Temp\SAE5\Resources\TestFiles\rose.jpg");
var destBitmap = CropBitmap(srcBitmap, new Rectangle(10, 20, 50, 25));
destBitmap.Save(#"d:\Temp\tst.png");
}
static Bitmap CropBitmap(Bitmap sourceBitmap, Rectangle rect)
{
// Add code to check and adjust rect to be inside sourceBitmap
var sourceBitmapData = sourceBitmap.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format32bppRgb);
var destBitmap = new Bitmap(rect.Width, rect.Height, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
var destBitmapData = destBitmap.LockBits(new Rectangle(0, 0, rect.Width, rect.Height), System.Drawing.Imaging.ImageLockMode.WriteOnly, System.Drawing.Imaging.PixelFormat.Format32bppRgb);
var pixels = new int[rect.Width * rect.Height];
System.Runtime.InteropServices.Marshal.Copy(sourceBitmapData.Scan0, pixels, 0, pixels.Length);
System.Runtime.InteropServices.Marshal.Copy(pixels, 0, destBitmapData.Scan0, pixels.Length);
sourceBitmap.UnlockBits(sourceBitmapData);
destBitmap.UnlockBits(destBitmapData);
return destBitmap;
}
After getting a full understanding of what I was doing incorrectly. I then modified the code. I was writing to the same File Stream, so it was not actually saving the cropped image. Changed the code to write to a new File Stream and the cropped image is now being saved.
Byte[] data;
using (FileStream fs = new FileStream(fileNameWitPath, fileMode))
{
using (BinaryWriter bw = new BinaryWriter(fs))
{
//get co-ords
int x1 = Convert.ToInt32(context.Request.QueryString["x1"].Trim());
int y1 = Convert.ToInt32(context.Request.QueryString["y1"].Trim());
int x2 = Convert.ToInt32(context.Request.QueryString["x2"].Trim());
int y2 = Convert.ToInt32(context.Request.QueryString["y2"].Trim());
Bitmap b = new Bitmap(fs);
Bitmap nb = new Bitmap((x2 - x1), (y2 - y1));
Graphics g = Graphics.FromImage(nb);
//g.DrawImage(b, x2, y2);
Rectangle cropRect = new Rectangle(x1, y1, nb.Width, nb.Height);
g.DrawImage(b, new Rectangle(0, 0, nb.Width, nb.Height), cropRect, GraphicsUnit.Pixel);
using (var memoryStream = new MemoryStream())
{
nb.Save(memoryStream, ImageFormat.Png);
data = memoryStream.ToArray();
}
bw.Close();
}
}
using (FileStream fs = new FileStream(fileNameWitPath, fileMode))
{
using (BinaryWriter bw = new BinaryWriter(fs))
{
bw.Write(data);
bw.Close();
}
}

parameter not valid at saving bitmap

I'm resizing an image using the following code:
private void ResizeImage(string path)
{
var maxWidth = 700;
var ms = new MemoryStream(System.IO.File.ReadAllBytes(Server.MapPath(path)));
var imgToResize = Bitmap.FromStream(ms);
var sourceWidth = imgToResize.Width;
var sourceHeight = imgToResize.Height;
if (sourceWidth > maxWidth)
{
var nPercent = ((float)maxWidth / (float)sourceWidth);
var newWidth = (int)(sourceWidth * nPercent);
var newHeight = (int)(sourceHeight * nPercent);
var newImage = new Bitmap(newWidth, newHeight, PixelFormat.Format24bppRgb);
using (Graphics graphics = Graphics.FromImage(newImage))
{
graphics.CompositingQuality = CompositingQuality.HighQuality;
graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
graphics.SmoothingMode = SmoothingMode.HighQuality;
graphics.DrawImage(imgToResize, 0, 0, newWidth, newHeight);
}
imgToResize.Dispose();
var codecInfo = this.GetEncoderInfo(ImageFormat.Jpeg);
var encoder = System.Drawing.Imaging.Encoder.Quality;
var encParams = new EncoderParameters(1);
var encParam = new EncoderParameter(encoder, 60);
encParams.Param[0] = encParam;
newImage.Save(Server.MapPath(path), codecInfo, encParams);
}
}
private ImageCodecInfo GetEncoderInfo(ImageFormat format)
{
return ImageCodecInfo.GetImageDecoders().SingleOrDefault(c => c.FormatID == format.Guid);
}
at the last line [newImage.Save(...)] it throws an exception saying Parameter is not valid.
what exactly is wrong here ?
Your new EncoderParameter(encoder, 60); does not work properly. There is no signature with a single Int32. Use 60L instead.

Grey and unsharp images when resizing using GDI+

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.

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