it-swarm.com.ru

c # Изменение размера изображения до другого размера с сохранением соотношения сторон

Я пытаюсь изменить размер изображения, сохраняя пропорции исходного изображения, чтобы новое изображение не выглядело сжатым.

например:

Преобразуйте изображение 150 * 100 в изображение 150 * 150.
Дополнительные 50 пикселей высоты должны быть дополнены белым фоновым цветом.

Это текущий код, который я использую. 

Это хорошо работает для изменения размера, но изменение соотношения сторон исходного изображения сдавливает новое изображение.

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

Правка: я хотел бы, чтобы изображение было дополнено вместо обрезки

73
sf.

Я узнал, как изменить размер и дополнить изображение, узнав из этой этой статьи 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;
    }
69
sf.

Это должно сделать это.

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

Отредактировано, чтобы добавить:

Тем, кто хочет улучшить этот код, следует добавить его в комментарии или новый ответ. Не редактируйте этот код напрямую.

102
egrunin

Я использую следующий метод для расчета желаемого размера изображения:

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

Это ставит проблему соотношения сторон в отдельный метод, который возвращает только новые размеры изображения.

17
fubo

Чтобы получить более быстрый результат, функцию, которая получает размер, можно найти в 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));

Использует Linq для минимизации переменных и пересчетов, а также ненужных операторов if/else

7
ciosoriog

Просто обобщив его до пропорций и размеров, изображение может быть сделано за пределами этой функции.

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

Я собираюсь добавить свой код здесь тоже. Этот код позволит вам изменить размер изображения с применением или без применения соотношения сторон или изменить размер с отступом. Это модифицированная версия кода 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);
        }
    }
}
6
Halcyon

Вот менее специфический метод расширения, который работает с Image, а не выполняет загрузку и сохранение для вас. Это также позволяет вам указать метод интерполяции и корректно отображать края при использовании интерполяции NearestNeighbour.

Изображение будет отображаться в границах указанной области, поэтому вы всегда будете знать ширину и высоту вывода. например:

 Scaled within bounds

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

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

4
user195488

Примечание: этот код изменяет размеры и удаляет все, что находится за пределами соотношения сторон, вместо того, чтобы заполнять его.

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

Сохраняйте соотношение сторон и устраняйте почтовый ящик и 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;
    }
1
user4849858
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);

        }
    }
0
satyender

Я только что написал это, потому что ни один из ответов здесь уже не был достаточно простым. Вы можете заменить жестко закодированные 128 на все, что захотите, или основывать их на размере исходного изображения. Все, что я хотел, это изменить масштаб изображения до размера 128x128, сохранив соотношение сторон и центрировав результат в новом изображении.

    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;
    }
0
adhd engineering
    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;
0
gdZeus