c # Redimensionamento de imagem para tamanhos diferentes, preservando a proporção

Estou tentando resize uma imagem enquanto preservo a proporção da imagem original para que a nova imagem não fique esborratada.

por exemplo:

Converta uma imagem de 150 * 100 em uma imagem de 150 * 150.
Os 50 pixels extras da altura precisam ser preenchidos com uma cor de fundo branco.

Este é o código atual que estou usando.

Ele funciona bem para resize, mas alterar a taxa de proporção da imagem original afeta a nova imagem.

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); } 

EDIT: eu gostaria de ter a imagem preenchida em vez de cortada

    Isso deve servir.

     private void resizeImage(string path, string originalFilename, /* note changed names */ int canvasWidth, int canvasHeight, /* new */ int originalWidth, int originalHeight) { Image image = Image.FromFile(path + originalFilename); System.Drawing.Image thumbnail = new Bitmap(canvasWidth, canvasHeight); // changed parm names 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; /* ------------------ new code --------------- */ // Figure out the ratio double ratioX = (double) canvasWidth / (double) originalWidth; double ratioY = (double) canvasHeight / (double) originalHeight; // use whichever multiplier is smaller double ratio = ratioX < ratioY ? ratioX : ratioY; // now we can get the new height and width int newHeight = Convert.ToInt32(originalHeight * ratio); int newWidth = Convert.ToInt32(originalWidth * ratio); // Now calculate the X,Y position of the upper-left corner // (one of these will always be zero) int posX = Convert.ToInt32((canvasWidth - (originalWidth * ratio)) / 2); int posY = Convert.ToInt32((canvasHeight - (originalHeight * ratio)) / 2); graphic.Clear(Color.White); // white padding graphic.DrawImage(image, posX, posY, newWidth, newHeight); /* ------------- end new code ---------------- */ System.Drawing.Imaging.ImageCodecInfo[] info = ImageCodecInfo.GetImageEncoders(); EncoderParameters encoderParameters; encoderParameters = new EncoderParameters(1); encoderParameters.Param[0] = new EncoderParameter(Encoder.Quality, 100L); thumbnail.Save(path + newWidth + "." + originalFilename, info[1], encoderParameters); } 

    Editado para adicionar:

    Aqueles que querem melhorar este código devem colocá-lo nos comentários ou uma nova resposta. Não edite esse código diretamente.

    Eu descobri como resize e preencher a imagem, aprendendo com isso este artigo 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; } 

    Eu uso o seguinte método para calcular o tamanho de imagem desejado:

     using System.Drawing; public static Size ResizeKeepAspect(Size CurrentDimensions, int maxWidth, int maxHeight) { int newHeight = CurrentDimensions.Height; int newWidth = CurrentDimensions.Width; if (maxWidth > 0 && newWidth > maxWidth) //WidthResize { Decimal divider = Math.Abs((Decimal)newWidth / (Decimal)maxWidth); newWidth = maxWidth; newHeight = (int)Math.Round((Decimal)(newHeight / divider)); } if (maxHeight > 0 && newHeight > maxHeight) //HeightResize { Decimal divider = Math.Abs((Decimal)newHeight / (Decimal)maxHeight); newHeight = maxHeight; newWidth = (int)Math.Round((Decimal)(newWidth / divider)); } return new Size(newWidth, newHeight); } 

    Isso coloca o problema da proporção em um método separado, que retorna apenas as novas dimensões da imagem.

    Atualizar:

    Aqui está uma versão mais curta:

     public static Size ResizeKeepAspect(Size src, int maxWidth, int maxHeight) { 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)); } 

    Apenas generalizando para proporções e tamanhos, material de imagem pode ser feito fora desta function

     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; } 

    Para obter um resultado mais rápido, a function que obtém o tamanho pode ser encontrada em resultSize :

     Size original = new Size(640, 480); int maxSize = 100; float percent = (new List { (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)); 

    Usa o Linq para minimizar variables ​​e recálculos, bem como instruções desnecessárias if/else

    Vou adicionar meu código aqui também. Esse código permitirá resize uma imagem com ou sem a proporção atribuída ou resize com preenchimento. Esta é uma versão modificada do código do 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); } /// Resizes an image to a new width and height. /// The folder which holds the original image. /// The file name of the original image. /// The folder which will hold the resized image. /// The file name of the resized image. /// When resizing the image, this is the maximum width to resize the image to. /// When resizing the image, this is the maximum height to resize the image to. /// 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. /// 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. 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); } } } 

    Nota: este código é redimensionado e remove tudo fora da proporção em vez de preenchê-lo.

     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); } } } } ///  /// Saves an image as a jpeg image, with the given quality ///  /// Path to which the image would be saved. // An integer from 0 to 100, with 100 being the /// highest quality 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); } ///  /// Returns the image codec with the given mime type ///  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; } } } 

     // 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); } 

    Aqui está um método de extensão menos específico que funciona com o Image, em vez de fazer o carregamento e o salvamento para você. Ele também permite que você especifique o método de interpolação e renderize corretamente as bordas quando usar a interpolação NearestNeighbour.

    A imagem será renderizada dentro dos limites da área especificada, para que você sempre saiba a largura e a altura da saída. por exemplo:

    Escalado dentro dos limites

     namespace YourApp { #region Namespaces using System; using System.Drawing; using System.Drawing.Imaging; using System.Drawing.Drawing2D; #endregion /// Generic helper functions related to graphics. public static class ImageExtensions { /// Resizes an image to a new width and height value. /// The image to resize. /// The width of the new image. /// The height of the new image. /// Interpolation mode. /// If true, the image is centered in the middle of the returned image, maintaining the aspect ratio of the original image. /// The new image. The old image is unaffected. 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; } } } 

    Manter o aspecto Ração e eliminar o letterbox e o 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; } 

    Plug and play! Eu escrevi um lib que faz isso com bastante facilidade! Veja o link … Documentação e Download

      private static Size CalculateDimensions(Size originalSize, Size targetSize) { Size newSize = new Size(); int cooficientA = targetSize.Height * originalSize.Width; int cooficientB = targetSize.Width * originalSize.Height; if (cooficientA < cooficientB) { newSize.Width = (int)Math.Round((double)cooficientA / originalSize.Height); newSize.Height = targetSize.Height; } else { newSize.Width = targetSize.Width; newSize.Height = (int)Math.Round((double)cooficientB / originalSize.Width); } return newSize; } Size newSize = CalculateDimensions(originalSize, targetSize); x = (targetSize.Width - newSize.Width) / 2; y = (targetSize.Height - newSize.Height) / 2; 

    Eu acabei de escrever isso porque nenhuma das respostas aqui já era bem simples. Você pode replace o 128 codificado para o que quiser ou baseá-lo no tamanho da imagem original. Tudo o que eu queria era resize a imagem para uma imagem de 128×128, mantendo a proporção e centrando o resultado na nova imagem.

      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; }