Estoy intentando cambiar el tamaño de una imagen conservando la relación de aspecto de la imagen original para que la nueva imagen no se vea aplastada.
p.ej:
Convierta una imagen de 150 * 100 en una imagen de 150 * 150.
Los 50 píxeles adicionales de la altura deben rellenarse con un color de fondo blanco.
Este es el código actual que estoy usando.
Funciona bien para cambiar el tamaño, pero cambiar la relación de aspecto de la imagen original aplasta la nueva imagen.
private void resizeImage(string path, string originalFilename,
int width, int height)
{
Image image = Image.FromFile(path + originalFilename);
System.Drawing.Image thumbnail = new Bitmap(width, height);
System.Drawing.Graphics graphic =
System.Drawing.Graphics.FromImage(thumbnail);
graphic.InterpolationMode = InterpolationMode.HighQualityBicubic;
graphic.SmoothingMode = SmoothingMode.HighQuality;
graphic.PixelOffsetMode = PixelOffsetMode.HighQuality;
graphic.CompositingQuality = CompositingQuality.HighQuality;
graphic.DrawImage(image, 0, 0, width, height);
System.Drawing.Imaging.ImageCodecInfo[] info =
ImageCodecInfo.GetImageEncoders();
EncoderParameters encoderParameters;
encoderParameters = new EncoderParameters(1);
encoderParameters.Param[0] = new EncoderParameter(Encoder.Quality,
100L);
thumbnail.Save(path + width + "." + originalFilename, info[1],
encoderParameters);
}
EDITAR: Me gustaría que la imagen se rellene en lugar de recortar
Path.Combine(path, originalFileName)
al cargar la imagen original. Lo mismo al guardar la miniatura al final.Descubrí cómo cambiar el tamaño Y rellenar la imagen aprendiendo de este artículo de CodeProject .
static Image FixedSize(Image imgPhoto, int Width, int Height) { int sourceWidth = imgPhoto.Width; int sourceHeight = imgPhoto.Height; int sourceX = 0; int sourceY = 0; int destX = 0; int destY = 0; float nPercent = 0; float nPercentW = 0; float nPercentH = 0; nPercentW = ((float)Width / (float)sourceWidth); nPercentH = ((float)Height / (float)sourceHeight); if (nPercentH < nPercentW) { nPercent = nPercentH; destX = System.Convert.ToInt16((Width - (sourceWidth * nPercent)) / 2); } else { nPercent = nPercentW; destY = System.Convert.ToInt16((Height - (sourceHeight * nPercent)) / 2); } int destWidth = (int)(sourceWidth * nPercent); int destHeight = (int)(sourceHeight * nPercent); Bitmap bmPhoto = new Bitmap(Width, Height, PixelFormat.Format24bppRgb); bmPhoto.SetResolution(imgPhoto.HorizontalResolution, imgPhoto.VerticalResolution); Graphics grPhoto = Graphics.FromImage(bmPhoto); grPhoto.Clear(Color.Red); grPhoto.InterpolationMode = InterpolationMode.HighQualityBicubic; grPhoto.DrawImage(imgPhoto, new Rectangle(destX, destY, destWidth, destHeight), new Rectangle(sourceX, sourceY, sourceWidth, sourceHeight), GraphicsUnit.Pixel); grPhoto.Dispose(); return bmPhoto; }
fuente
Utilizo el siguiente método para calcular el tamaño de imagen deseado:
using System.Drawing; public static Size ResizeKeepAspect(this Size src, int maxWidth, int maxHeight, bool enlarge = false) { maxWidth = enlarge ? maxWidth : Math.Min(maxWidth, src.Width); maxHeight = enlarge ? maxHeight : Math.Min(maxHeight, src.Height); decimal rnd = Math.Min(maxWidth / (decimal)src.Width, maxHeight / (decimal)src.Height); return new Size((int)Math.Round(src.Width * rnd), (int)Math.Round(src.Height * rnd)); }
Esto coloca el problema de la relación de aspecto y las dimensiones en un método separado.
fuente
enlarge
hace?Para obtener un resultado más rápido, la función que obtiene el tamaño se puede encontrar en
resultSize
:Size original = new Size(640, 480); int maxSize = 100; float percent = (new List<float> { (float)maxSize / (float)original.Width , (float)maxSize / (float)original.Height }).Min(); Size resultSize = new Size((int)Math.Floor(original.Width * percent), (int)Math.Floor(original.Height * percent));
Usos
Linq
para minimizar variables y recálculos, así comoif/else
declaraciones innecesariasfuente
If/else
hubiera sido mejor que asignar una lista,Math.Min
también era una opciónSimplemente generalizándolo a relaciones de aspecto y tamaños, las cosas de la imagen se pueden hacer fuera de esta función
public static d.RectangleF ScaleRect(d.RectangleF dest, d.RectangleF src, bool keepWidth, bool keepHeight) { d.RectangleF destRect = new d.RectangleF(); float sourceAspect = src.Width / src.Height; float destAspect = dest.Width / dest.Height; if (sourceAspect > destAspect) { // wider than high keep the width and scale the height destRect.Width = dest.Width; destRect.Height = dest.Width / sourceAspect; if (keepHeight) { float resizePerc = dest.Height / destRect.Height; destRect.Width = dest.Width * resizePerc; destRect.Height = dest.Height; } } else { // higher than wide – keep the height and scale the width destRect.Height = dest.Height; destRect.Width = dest.Height * sourceAspect; if (keepWidth) { float resizePerc = dest.Width / destRect.Width; destRect.Width = dest.Width; destRect.Height = dest.Height * resizePerc; } } return destRect; }
fuente
RectangleF
.Voy a agregar mi código aquí también. Este código le permitirá cambiar el tamaño de una imagen con o sin que se aplique la relación de aspecto o cambiar el tamaño con relleno. Esta es una versión modificada del código de egrunin.
using System.Drawing; using System.Drawing.Drawing2D; using System.Drawing.Imaging; using System.IO; namespace ConsoleApplication1 { public class Program { public static void Main(string[] args) { var path = Directory.GetParent(Directory.GetCurrentDirectory()).Parent.FullName; ResizeImage(path, "large.jpg", path, "new.jpg", 100, 100, true, true); } /// <summary>Resizes an image to a new width and height.</summary> /// <param name="originalPath">The folder which holds the original image.</param> /// <param name="originalFileName">The file name of the original image.</param> /// <param name="newPath">The folder which will hold the resized image.</param> /// <param name="newFileName">The file name of the resized image.</param> /// <param name="maximumWidth">When resizing the image, this is the maximum width to resize the image to.</param> /// <param name="maximumHeight">When resizing the image, this is the maximum height to resize the image to.</param> /// <param name="enforceRatio">Indicates whether to keep the width/height ratio aspect or not. If set to false, images with an unequal width and height will be distorted and padding is disregarded. If set to true, the width/height ratio aspect is maintained and distortion does not occur.</param> /// <param name="addPadding">Indicates whether fill the smaller dimension of the image with a white background. If set to true, the white padding fills the smaller dimension until it reach the specified max width or height. This is used for maintaining a 1:1 ratio if the max width and height are the same.</param> private static void ResizeImage(string originalPath, string originalFileName, string newPath, string newFileName, int maximumWidth, int maximumHeight, bool enforceRatio, bool addPadding) { var image = Image.FromFile(originalPath + "\\" + originalFileName); var imageEncoders = ImageCodecInfo.GetImageEncoders(); EncoderParameters encoderParameters = new EncoderParameters(1); encoderParameters.Param[0] = new EncoderParameter(Encoder.Quality, 100L); var canvasWidth = maximumWidth; var canvasHeight = maximumHeight; var newImageWidth = maximumWidth; var newImageHeight = maximumHeight; var xPosition = 0; var yPosition = 0; if (enforceRatio) { var ratioX = maximumWidth / (double)image.Width; var ratioY = maximumHeight / (double)image.Height; var ratio = ratioX < ratioY ? ratioX : ratioY; newImageHeight = (int)(image.Height * ratio); newImageWidth = (int)(image.Width * ratio); if (addPadding) { xPosition = (int)((maximumWidth - (image.Width * ratio)) / 2); yPosition = (int)((maximumHeight - (image.Height * ratio)) / 2); } else { canvasWidth = newImageWidth; canvasHeight = newImageHeight; } } var thumbnail = new Bitmap(canvasWidth, canvasHeight); var graphic = Graphics.FromImage(thumbnail); if (enforceRatio && addPadding) { graphic.Clear(Color.White); } graphic.InterpolationMode = InterpolationMode.HighQualityBicubic; graphic.SmoothingMode = SmoothingMode.HighQuality; graphic.PixelOffsetMode = PixelOffsetMode.HighQuality; graphic.CompositingQuality = CompositingQuality.HighQuality; graphic.DrawImage(image, xPosition, yPosition, newImageWidth, newImageHeight); thumbnail.Save(newPath + "\\" + newFileName, imageEncoders[1], encoderParameters); } } }
fuente
Aquí hay un método de extensión menos específico que funciona con Image en lugar de cargar y guardar por usted. También le permite especificar el método de interpolación y renderiza correctamente los bordes cuando utiliza la interpolación Ne más cercano.
La imagen se renderizará dentro de los límites del área que especifique para que siempre sepa el ancho y alto de salida. p.ej:
namespace YourApp { #region Namespaces using System; using System.Drawing; using System.Drawing.Imaging; using System.Drawing.Drawing2D; #endregion /// <summary>Generic helper functions related to graphics.</summary> public static class ImageExtensions { /// <summary>Resizes an image to a new width and height value.</summary> /// <param name="image">The image to resize.</param> /// <param name="newWidth">The width of the new image.</param> /// <param name="newHeight">The height of the new image.</param> /// <param name="mode">Interpolation mode.</param> /// <param name="maintainAspectRatio">If true, the image is centered in the middle of the returned image, maintaining the aspect ratio of the original image.</param> /// <returns>The new image. The old image is unaffected.</returns> public static Image ResizeImage(this Image image, int newWidth, int newHeight, InterpolationMode mode = InterpolationMode.Default, bool maintainAspectRatio = false) { Bitmap output = new Bitmap(newWidth, newHeight, image.PixelFormat); using (Graphics gfx = Graphics.FromImage(output)) { gfx.Clear(Color.FromArgb(0, 0, 0, 0)); gfx.InterpolationMode = mode; if (mode == InterpolationMode.NearestNeighbor) { gfx.PixelOffsetMode = PixelOffsetMode.HighQuality; gfx.SmoothingMode = SmoothingMode.HighQuality; } double ratioW = (double)newWidth / (double)image.Width; double ratioH = (double)newHeight / (double)image.Height; double ratio = ratioW < ratioH ? ratioW : ratioH; int insideWidth = (int)(image.Width * ratio); int insideHeight = (int)(image.Height * ratio); gfx.DrawImage(image, new Rectangle((newWidth / 2) - (insideWidth / 2), (newHeight / 2) - (insideHeight / 2), insideWidth, insideHeight)); } return output; } } }
fuente
Nota: este código cambia de tamaño y elimina todo lo que esté fuera de la relación de aspecto en lugar de rellenarlo.
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Drawing; using System.Drawing.Imaging; using System.IO; namespace MyPhotos.Common { public class ThumbCreator { public enum VerticalAlign { Top, Middle, Bottom } public enum HorizontalAlign { Left, Middle, Right } public void Convert(string sourceFile, string targetFile, ImageFormat targetFormat, int height, int width, VerticalAlign valign, HorizontalAlign halign) { using (Image img = Image.FromFile(sourceFile)) { using (Image targetImg = Convert(img, height, width, valign, halign)) { string directory = Path.GetDirectoryName(targetFile); if (!Directory.Exists(directory)) { Directory.CreateDirectory(directory); } if (targetFormat == ImageFormat.Jpeg) { SaveJpeg(targetFile, targetImg, 100); } else { targetImg.Save(targetFile, targetFormat); } } } } /// <summary> /// Saves an image as a jpeg image, with the given quality /// </summary> /// <param name="path">Path to which the image would be saved.</param> // <param name="quality">An integer from 0 to 100, with 100 being the /// highest quality</param> public static void SaveJpeg(string path, Image img, int quality) { if (quality < 0 || quality > 100) throw new ArgumentOutOfRangeException("quality must be between 0 and 100."); // Encoder parameter for image quality EncoderParameter qualityParam = new EncoderParameter(System.Drawing.Imaging.Encoder.Quality, quality); // Jpeg image codec ImageCodecInfo jpegCodec = GetEncoderInfo("image/jpeg"); EncoderParameters encoderParams = new EncoderParameters(1); encoderParams.Param[0] = qualityParam; img.Save(path, jpegCodec, encoderParams); } /// <summary> /// Returns the image codec with the given mime type /// </summary> private static ImageCodecInfo GetEncoderInfo(string mimeType) { // Get image codecs for all image formats ImageCodecInfo[] codecs = ImageCodecInfo.GetImageEncoders(); // Find the correct image codec for (int i = 0; i < codecs.Length; i++) if (codecs[i].MimeType == mimeType) return codecs[i]; return null; } public Image Convert(Image img, int height, int width, VerticalAlign valign, HorizontalAlign halign) { Bitmap result = new Bitmap(width, height); using (Graphics g = Graphics.FromImage(result)) { g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality; g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic; float ratio = (float)height / (float)img.Height; int temp = (int)((float)img.Width * ratio); if (temp == width) { //no corrections are needed! g.DrawImage(img, 0, 0, width, height); return result; } else if (temp > width) { //den e för bred! int overFlow = (temp - width); if (halign == HorizontalAlign.Middle) { g.DrawImage(img, 0 - overFlow / 2, 0, temp, height); } else if (halign == HorizontalAlign.Left) { g.DrawImage(img, 0, 0, temp, height); } else if (halign == HorizontalAlign.Right) { g.DrawImage(img, -overFlow, 0, temp, height); } } else { //den e för hög! ratio = (float)width / (float)img.Width; temp = (int)((float)img.Height * ratio); int overFlow = (temp - height); if (valign == VerticalAlign.Top) { g.DrawImage(img, 0, 0, width, temp); } else if (valign == VerticalAlign.Middle) { g.DrawImage(img, 0, -overFlow / 2, width, temp); } else if (valign == VerticalAlign.Bottom) { g.DrawImage(img, 0, -overFlow, width, temp); } } } return result; } } }
fuente
// This allows us to resize the image. It prevents skewed images and // also vertically long images caused by trying to maintain the aspect // ratio on images who's height is larger than their width public void ResizeImage(string OriginalFile, string NewFile, int NewWidth, int MaxHeight, bool OnlyResizeIfWider) { System.Drawing.Image FullsizeImage = System.Drawing.Image.FromFile(OriginalFile); // Prevent using images internal thumbnail FullsizeImage.RotateFlip(System.Drawing.RotateFlipType.Rotate180FlipNone); FullsizeImage.RotateFlip(System.Drawing.RotateFlipType.Rotate180FlipNone); if (OnlyResizeIfWider) { if (FullsizeImage.Width <= NewWidth) { NewWidth = FullsizeImage.Width; } } int NewHeight = FullsizeImage.Height * NewWidth / FullsizeImage.Width; if (NewHeight > MaxHeight) { // Resize with height instead NewWidth = FullsizeImage.Width * MaxHeight / FullsizeImage.Height; NewHeight = MaxHeight; } System.Drawing.Image NewImage = FullsizeImage.GetThumbnailImage(NewWidth, NewHeight, null, IntPtr.Zero); // Clear handle to original file so that we can overwrite it if necessary FullsizeImage.Dispose(); // Save resized picture NewImage.Save(NewFile); }
fuente
Mantener aspecto Ración y eliminar letterbox y Pillarbox.
static Image FixedSize(Image imgPhoto, int Width, int Height) { int sourceWidth = imgPhoto.Width; int sourceHeight = imgPhoto.Height; int X = 0; int Y = 0; float nPercent = 0; float nPercentW = 0; float nPercentH = 0; nPercentW = ((float)Width / (float)sourceWidth); nPercentH = ((float)Height / (float)sourceHeight); if (nPercentH < nPercentW) { nPercent = nPercentH; } else { nPercent = nPercentW; } int destWidth = (int)(sourceWidth * nPercent); int destHeight = (int)(sourceHeight * nPercent); Bitmap bmPhoto = new Bitmap(destWidth, destHeight, PixelFormat.Format24bppRgb); bmPhoto.SetResolution(imgPhoto.HorizontalResolution, imgPhoto.VerticalResolution); Graphics grPhoto = Graphics.FromImage(bmPhoto); grPhoto.DrawImage(imgPhoto, new Rectangle(X, Y, destWidth, destHeight), new Rectangle(X, Y, sourceWidth, sourceHeight), GraphicsUnit.Pixel); grPhoto.Dispose(); return bmPhoto; }
fuente
public static void resizeImage_n_save(Stream sourcePath, string targetPath, int requiredSize) { using (var image = System.Drawing.Image.FromStream(sourcePath)) { double ratio = 0; var newWidth = 0; var newHeight = 0; double w = Convert.ToInt32(image.Width); double h = Convert.ToInt32(image.Height); if (w > h) { ratio = h / w * 100; newWidth = requiredSize; newHeight = Convert.ToInt32(requiredSize * ratio / 100); } else { ratio = w / h * 100; newHeight = requiredSize; newWidth = Convert.ToInt32(requiredSize * ratio / 100); } // var newWidth = (int)(image.Width * scaleFactor); // var newHeight = (int)(image.Height * scaleFactor); var thumbnailImg = new Bitmap(newWidth, newHeight); var thumbGraph = Graphics.FromImage(thumbnailImg); thumbGraph.CompositingQuality = CompositingQuality.HighQuality; thumbGraph.SmoothingMode = SmoothingMode.HighQuality; thumbGraph.InterpolationMode = InterpolationMode.HighQualityBicubic; var imageRectangle = new Rectangle(0, 0, newWidth, newHeight); thumbGraph.DrawImage(image, imageRectangle); thumbnailImg.Save(targetPath, image.RawFormat); //var img = FixedSize(image, requiredSize, requiredSize); //img.Save(targetPath, image.RawFormat); } }
fuente
Creé un método de extensión que es mucho más simple que las respuestas que se publican. y la relación de aspecto se aplica sin recortar la imagen.
public static Image Resize(this Image image, int width, int height) { var scale = Math.Min(height / (float)image.Height, width / (float)image.Width); return image.GetThumbnailImage((int)(image.Width * scale), (int)(image.Height * scale), () => false, IntPtr.Zero); }
Uso de ejemplo:
using (var img = Image.FromFile(pathToOriginalImage)) { using (var thumbnail = img.Resize(60, 60)){ // Here you can do whatever you need to do with thumnail } }
fuente
Acabo de escribir esto porque ninguna de las respuestas aquí era lo suficientemente simple. Puede reemplazar los 128 codificados por lo que desee o basarlos en el tamaño de la imagen original. Todo lo que quería era cambiar la escala de la imagen a una imagen de 128x128, manteniendo la relación de aspecto y centrando el resultado en la nueva imagen.
private Bitmap CreateLargeIconForImage(Bitmap src) { Bitmap bmp = new Bitmap(128, 128); Graphics g = Graphics.FromImage(bmp); float scale = Math.Max((float)src.Width / 128.0f, (float)src.Height / 128.0f); PointF p = new PointF(128.0f - ((float)src.Width / scale), 128.0f - ((float)src.Height / scale)); SizeF size = new SizeF((float)src.Width / scale, (float)src.Height / scale); g.DrawImage(src, new RectangleF(p, size)); return bmp; }
fuente