简体   繁体   English

图像在C#中调整大小 - 确定调整大小尺寸(高度和宽度)的算法

[英]Image Resize in C# - Algorith to determine resize dimensions (height and width)

I need to scale down an image that has a height or width greater than a predefined pixel value. 我需要缩小高度或宽度大于预定义像素值的图像。

I wrote some code that takes a look at the original image, checks to see if either the width, the height, or the height and width are greater than the Max Width/Max Height settings. 我编写了一些代码来查看原始图像,检查宽度,高度或高度和宽度是否大于最大宽度/最大高度设置。

I now need to figure out what dimensions to resize to based on the max of the latter value. 我现在需要根据后一个值的最大值找出要调整大小的尺寸。

For example: If the image is 900h x 300w and the MAX Height is 700h I will need to resize the height to 700 and the width to ???? 例如:如果图像为900h x 300w且MAX高度为700h我需要将高度调整为700 ,将宽度调整为???? <-- this is what I need to calculate.. < - 这是我需要计算的..

Creating and saving the image file is simple, and outside the scope of this post: 创建和保存图像文件很简单,超出了本文的范围:

// First I get the max height and width allowed:

int resizeMaxHeight =  int.Parse(Utility.GetConfigValue("ResizeMaxHeight")); // in config: 700px
int resizeMaxWidth =  int.Parse(Utility.GetConfigValue("ResizeMaxWidth"));  //  in config: 500px

// Save original: 
try
{
    filebase.SaveAs(savedFileName);
}
catch (System.IO.DirectoryNotFoundException ex)
{
    Logger.Instance.LogException(ex, 0, "FileTransfer");
}

// Determin original dimensions:
Image image = System.Drawing.Image.FromFile(Server.MapPath(savedFileName));

int resizeHeight, resizeWidth;
bool doResize = true;

// both height and width are greater than the allowed height and width:
if (image.Width > resizeMaxWidth && image.Height > resizeMaxHeight)
{
    if (image.Height > image.Width) 
        resizeHeight = resizeMaxHeight;
    else
        resizeWidth = resizeMaxWidth;
}
else if (image.Width > resizeMaxWidth)
{
    // width is too great, but height is ok
    resizeWidth = resizeMaxWidth;
}
else if (image.Height > resizeMaxHeight)
{
    // height is too great, but width is ok
    resizeHeight = resizeMaxHeight;
}
else
{
    // image is ok size, don't resize:
    doResize = false;
}

Create thumbnail: This is what I'm working now... not complete: 创建缩略图:这就是我现在正在工作的......不完整:

if (doResize)
{
    ImageUtilities.ResizeImage(image, resizeWidth, resizeHeight);
}

The solution posted by Nathaniel actually fails if the image height is larger than the image width. 如果图像高度大于图像宽度,Nathaniel发布的解决方案实际上失败了。 The following example yields the correct result : 以下示例生成正确的结果:

private Size ResizeFit(Size originalSize, Size maxSize)
{
    var widthRatio = (double)maxSize.Width / (double)originalSize.Width;
    var heightRatio = (double) maxSize.Height/(double) originalSize.Height;
    var minAspectRatio = Math.Min(widthRatio, heightRatio);
    if (minAspectRatio > 1)
        return originalSize;
    return new Size((int)(originalSize.Width*minAspectRatio), (int)(originalSize.Height*minAspectRatio));
}

Here are two ways to make this calculation. 以下是两种进行此计算的方法。 Depending upon how you think about the problem, one may seem more intuitive than the other. 根据您对问题的看法,可能看起来比另一个更直观。 They are mathematically equivalent to several decimal places. 它们在数学上等效于几个小数位。

Both are safe for Math.Round, but only ConstrainVerbose produces results that are always less than maxWidth/maxHeight. 两者对Math.Round都是安全的,但只有ConstrainVerbose产生的结果总是小于maxWidth / maxHeight。

SizeF ConstrainConcise(int imageWidth, int imageHeight, int maxWidth, int maxHeight){
    // Downscale by the smallest ratio (never upscale)
    var scale = Math.Min(1, Math.Min(maxWidth / (float)imageWidth, maxHeight / (float) imageHeight));
    return new SizeF(scale * imageWidth, scale * imageHeight);
}

SizeF ConstrainVerbose(int imageWidth, int imageHeight, int maxWidth, int maxHeight){
    // Coalculate the aspect ratios of the image and bounding box
    var maxAspect = (float) maxWidth / (float) maxHeight;
    var aspect =  (float) imageWidth / (float) imageHeight;
    // Bounding box aspect is narrower
    if (maxAspect <= aspect && imageWidth > maxWidth)
    {
        // Use the width bound and calculate the height
        return new SizeF(maxWidth, Math.Min(maxHeight, maxWidth / aspect));
    }
    else if (maxAspect > aspect && imageHeight > maxHeight)
    {
        // Use the height bound and calculate the width
        return new SizeF(Math.Min(maxWidth, maxHeight * aspect), maxHeight);
    }else{
        return new SizeF(imageWidth, imageHeight);
    }
}

Brute force unit-test here 蛮力单位测试在这里

You can avoid calculating the aspect ratio (and using doubles) using a few integer tricks.. 您可以使用一些整数技巧来避免计算纵横比(并使用双精度数)。

// You have the new height, you need the new width
int orgHeight = 1200;
int orgWidth = 1920;

int newHeight = 400;
int newWidth = (newHeight * orgWidth) / orgHeight; // 640

or... 要么...

// You have the new width, you need the new height.
int orgWidth = 1920;
int orgHeight = 1200;

int newWidth = 800;
int newHeight = (newWidth * orgHeight) / orgWidth; // 500

The following example will resize an image to any desired rectangle (desWidth and desHeight) and center the image within that rectangle. 以下示例将图像调整为任何所需的矩形(desWidth和desHeight),并将图像置于该矩形内。

static Image ResizeImage(Image image, int desWidth, int desHeight)
{
    int x, y, w, h;

    if (image.Height > image.Width)
    {
        w = (image.Width * desHeight) / image.Height;
        h = desHeight;
        x = (desWidth - w) / 2;
        y = 0;
    }
    else
    {
        w = desWidth;
        h = (image.Height * desWidth) / image.Width;
        x = 0;
        y = (desHeight - h) / 2;
    }

    var bmp = new Bitmap(desWidth, desHeight);

    using (Graphics g = Graphics.FromImage(bmp))
    {
        g.CompositingQuality = CompositingQuality.HighQuality;
        g.InterpolationMode = InterpolationMode.HighQualityBicubic;
        g.DrawImage(image, x, y, w, h);
    }

    return bmp;
}

I did something similar for Bitmaps, but idea is same: 我为Bitmaps做了类似的事情,但想法是一样的:

1. get image height and width
2. get current screen resolution
3. calculate aspect ratio (ASR) from image size

Handle following cases:

4. if ASR >=1 and image width > image height
    if image width > screen width {}
        if image height > screen height {}
        else if image width > screen width {}
    else {}
   else
    if image height > screen height {}
    else if image width > screen width {}
    else {}

//SCREEN_SIZE is configurable; // SCREEN_SIZE是可配置的; Defs.SCREEN_SIZE = 100; Defs.SCREEN_SIZE = 100; // and boolPixelAR is true; //并且boolPixelAR为true;

Try following code: 请尝试以下代码:

            // PERCENTAGE OF IMAGE -> TODO: Configurable? IMAZE ZOOM / SCREEN PERCENTAGE
            Double HScale = __bmp.Width;// *Defs.SCREEN_SIZE / 100;
            Double VScale = __bmp.Height;// *Defs.SCREEN_SIZE / 100;
            Double __aspectRatio;
            Double __screenRatio = _currentScreenSize.Width / _currentScreenSize.Height;

            // PERCENTAGE OF SCREEN
            if (!_boolPixelAR) {
                HScale = _currentScreenSize.Width * Defs.SCREEN_SIZE / 100;
                VScale = _currentScreenSize.Height * Defs.SCREEN_SIZE / 100;
            }
            else {
                __aspectRatio = HScale / VScale;
                if( __aspectRatio >= 1)
                    if (HScale >= _currentScreenSize.Width) {  // Long Edge is WIDTH. For 100%, HScale = WIDTH
                        VScale = ((VScale * _currentScreenSize.Width) / HScale) * Defs.SCREEN_SIZE / 100;
                        HScale = _currentScreenSize.Width * Defs.SCREEN_SIZE / 100;

                        if (VScale > _currentScreenSize.Height) {                  // Long Edge is HEIGHT. For 100%, VScale = HEIGHT
                            //__aspectRatio = VScale / HScale;
                            HScale = ((HScale * _currentScreenSize.Height) / VScale) * Defs.SCREEN_SIZE / 100;
                            VScale = _currentScreenSize.Height * Defs.SCREEN_SIZE / 100;
                        }
                    }
                    else if (VScale > _currentScreenSize.Height) {                  // Long Edge is HEIGHT. For 100%, VScale = HEIGHT
                        //__aspectRatio = VScale / HScale;
                        HScale = ((HScale * _currentScreenSize.Height) / VScale) * Defs.SCREEN_SIZE / 100;
                        VScale = _currentScreenSize.Height * Defs.SCREEN_SIZE / 100;
                    } 
                    else {
                        //Do nothing... Just set Zoom.
                        HScale = HScale * Defs.SCREEN_SIZE / 100;
                        VScale = VScale * Defs.SCREEN_SIZE / 100;
                    }
                else 
                    if (VScale > _currentScreenSize.Height) {                  // Long Edge is HEIGHT. For 100%, VScale = HEIGHT
                        //__aspectRatio = VScale / HScale;
                        HScale = ((HScale * _currentScreenSize.Height) / VScale) * Defs.SCREEN_SIZE / 100;
                        VScale = _currentScreenSize.Height * Defs.SCREEN_SIZE / 100;
                    }
                    else if (HScale >= _currentScreenSize.Width) {  // Long Edge is WIDTH. For 100%, HScale = WIDTH
                        VScale = ((VScale * _currentScreenSize.Width) / HScale) * Defs.SCREEN_SIZE / 100;
                        HScale = _currentScreenSize.Width * Defs.SCREEN_SIZE / 100;
                    } 
                    else {
                        //Do nothing... Just set Zoom.
                        HScale = HScale * Defs.SCREEN_SIZE / 100;
                        VScale = VScale * Defs.SCREEN_SIZE / 100;
                    }

                ////__aspectRatio = VScale / HScale;
                //HScale = ((HScale * _currentScreenSize.Height) / VScale) * Defs.SCREEN_SIZE / 100;
                //VScale = _currentScreenSize.Height * Defs.SCREEN_SIZE / 100;
            }

            Bitmap scaledBmp = GraphicsFactory.ResizeImage(
                                        __bmp,
                                        Convert.ToInt32(HScale),
                                        Convert.ToInt32(VScale));

Fitting an image to new size requires two operations: 将图像拟合为新尺寸需要两个操作:

  1. Resize - resize the source image to fit exactly one dimension (width or height - the one with the smaller ratio) 调整大小 - 调整源图像的大小以恰好适合一个维度(宽度或高度 - 具有较小比率的维度)

  2. Crop - crop the result of the previous operation to the target dimensions 裁剪 - 将上一操作的结果裁剪为目标尺寸

Here is a small sample: 这是一个小样本:

    private static Image Resize(Image img, int width, int height)
    {
        Bitmap b = new Bitmap(width, height);
        using (Graphics g = Graphics.FromImage((Image)b))
        {
            g.DrawImage(img, 0, 0, width, height);
        }

        return (Image)b;
    }

    public static Image Crop(Image image, int width, int height)
    {
        int cropx = image.Width > width ? image.Width / 2 - width / 2 : 0;
        int cropy = image.Height > height ? image.Height / 2 - height / 2 : 0;
        width = image.Width > width ? width : image.Width;
        height = image.Height > height ? height : image.Height;

        Rectangle cropRect = new Rectangle(cropx, cropy, width, height);

        var target = new Bitmap(cropRect.Width, cropRect.Height);

        using (Graphics g = Graphics.FromImage(target))
        {
            g.DrawImage(image, new Rectangle(0, 0, target.Width, target.Height), cropRect, GraphicsUnit.Pixel);
        }

        return target;
    }

    public static Image FitToSize(Image image, int width, int height)
    {
        var wratio = 1.0 * image.Width / width;
        var hratio = 1.0 * image.Height / height;

        int wresize;
        int hresize;

        if (wratio >= hratio && wratio > 1)
        {
            wresize = (int)Math.Round((double)image.Width / hratio);
            hresize = height;

            image = Resize(image, wresize, hresize);
            image = Crop(image, width, height);  
        }
        else if (hratio >= wratio && hratio > 1)
        {
            hresize = (int)Math.Round((double)image.Height / wratio);
            wresize = width;

            image = Resize(image, wresize, hresize);
            image = Crop(image, width, height);
        }
        return image;

    }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM