如何在C#中保持宽高比调整图像大小

5
我需要知道如何调整图像大小以适应一个框,而不会让图像拉伸太多。该框具有固定的宽度和高度,我希望图像能够填充框的尽可能多的区域,但仍保持其原始的长宽比。
5个回答

16
//calculate the ratio
double dbl = (double)image.Width / (double)image.Height;

//set height of image to boxHeight and check if resulting width is less than boxWidth, 
//else set width of image to boxWidth and calculate new height
if( (int)((double)boxHeight * dbl) <= boxWidth )
{
    resizedImage = new Bitmap(original, (int)((double)boxHeight * dbl), boxHeight);
}
else
{
    resizedImage = new Bitmap(original, boxWidth, (int)((double)boxWidth / dbl) );
}

与相同比例缩放的公式为:

newWidth =  (int)((double)boxHeight * dbl)

or

newHeight =  (int)((double)boxWidth / dbl)

1
代码需要一些解释吗?或者在你的代码中加入一些注释? - John Demetriou
@MuhammadBashir 您建议的编辑可能有良好的代码,但它可能最好以新答案的形式编写,因为这将是对已接受答案的重大更改。 - AdrianHHH

4

使用 Math.Max 函数可以简化这个问题:

double ratio = Math.Max((double)image.width / (double)box.width , (double)image.height / (double)box.height);
image.width = (int)(image.width / ratio);
image.height = (int)(image.height / ratio);

1
 Bitmap original,resizedImage;
try
                {

                    using (FileStream fs = new System.IO.FileStream(imageLabel.Text, System.IO.FileMode.Open))
                    {

                        original = new Bitmap(fs);
                    }

                    int rectHeight = BOXWIDTH;
                    int rectWidth = BOXWIDTH;
                    //if the image is squared set it's height and width to the smallest of the desired dimensions (our box). In the current example rectHeight<rectWidth
                    if (original.Height == original.Width)
                    {
                        resizedImage = new Bitmap(original, rectHeight, rectHeight);
                    }
                    else
                    {
                        //calculate aspect ratio
                        float aspect = original.Width / (float)original.Height;
                        int newWidth, newHeight;
                        //calculate new dimensions based on aspect ratio
                        newWidth = (int)(rectWidth * aspect);
                        newHeight = (int)(newWidth / aspect);
                        //if one of the two dimensions exceed the box dimensions
                        if (newWidth > rectWidth || newHeight > rectHeight)
                        {
                           //depending on which of the two exceeds the box dimensions set it as the box dimension and calculate the other one based on the aspect ratio
                            if (newWidth > newHeight)
                            {
                                newWidth = rectWidth;
                                newHeight = (int)(newWidth / aspect);

                            }
                            else
                            {
                                newHeight = rectHeight;
                                newWidth = (int)(newHeight * aspect);

                            }
                        }
                        resizedImage = new Bitmap(original, newWidth, newHeight);



                    }
                }
                catch (Exception ex)
                {
                    MessageBox.Show( ex.Message);
                }
            }

@AdrianoRepetti 一个 if-else 就足够了 - γηράσκω δ' αεί πολλά διδασκόμε
理论上只需要一个if else,但我添加了额外的代码以确保对于正方形图像也能完全安全。 - John Demetriou

0

接受的答案可能有效,但我盯着它看了很长时间,仍然不太明白,所以我想分享一下我的解决方案:先缩小高度,然后检查宽度,如果需要,再次缩小,始终保持纵横比。

我使用 SixLabors.ImageSharp,因为它与Linux兼容,但是一旦你有了newHeightnewWidth,你可以轻松更换调整大小的函数。

using SixLabors.ImageSharp;
using SixLabors.ImageSharp.Processing;

public class ImageSharpService : IImageService
{
    public async Task ShrinkAndSaveAsync(Stream stream, string savePath, int maxHeight, int maxWidth)
    {
        using Image image = Image.Load(stream);

        // check if resize is needed
        if (ResizeNeeded(image.Height, image.Width, maxHeight, maxWidth, out int newHeight, out int newWidth))
            // swap this part out if not using ImageSharp
            image.Mutate(x => x.Resize(new ResizeOptions
            {
                Size = new Size(newWidth, newHeight)
            }));

        await image.SaveAsync(savePath);
    }

    private bool ResizeNeeded(int height, int width, int maxHeight, int maxWidth, out int newHeight, out int newWidth)
    {
        // first use existing dimensions
        newHeight = height;
        newWidth = width;

        // if below max on both then do nothing
        if (height <= maxHeight && width <= maxWidth) return false;

        // naively check height first
        if (height > maxHeight)
        {
            // set down to max height
            newHeight = maxHeight;

            // calculate what new width would be
            var heightReductionRatio = maxHeight / height; // ratio of maxHeight:image.Height
            newWidth = width * heightReductionRatio; // apply ratio to image.Width
        }

        // does width need to be reduced? 
        // (this will also re-check width after shrinking by height dimension)
        if (newWidth > maxWidth)
        {
            // if so, re-calculate height to fit for maxWidth
            var widthReductionRatio = maxWidth / newWidth; // ratio of maxWidth:newWidth (height reduction ratio may have been applied)
            newHeight = maxHeight * widthReductionRatio; // apply new ratio to maxHeight to get final height
            newWidth = maxWidth;
        }

        // if we got here, resize needed and out vars have been set
        return true;
    }
}

-1

我相信如果你只改变高度或宽度,它将保持更好的比例,并且宽度/高度也会随之改变。所以你可以尝试一下。


1
不,你不能保持一个尺寸固定。想象一下,你有一个100x50的图像,你必须在一个200x200的框中绘制它:你会绘制一个200x50的图像吗?100x200?保持纵横比,它将是200x100。 - Adriano Repetti
是的,但如果您想要200200并将其更改为该大小,则比例将不正确。但是,如果您想在10050的图片上使用200*200,这是行不通的。这仅适用于您想保持比例的情况。 - Koen
这就是问题的关键。在保持纵横比的情况下尽可能接近所需尺寸。 - John Demetriou
是的,如果你想保持比例,那么你将始终与我的代码相同(以不同的方式),因为你们之间的百分比始终相同。或者我现在混淆了吗?例如:如果你将高度X2,为了保持比例,你将不得不将宽度*2。 - Koen

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接