【问题标题】:C# crop image from centerC#从中心裁剪图像
【发布时间】:2013-08-03 13:47:49
【问题描述】:

我正在使用 .NET(4.5) MVC(4.0) C#(5.0) 开发应用程序。 我想从我已经拥有的图像中生成图像缩略图。 现在的要求是它应该从图像中心生成最大正方形部分的缩略图,而不是拉伸整个图像,除非图像是正方形大小。

例如我的原始图像尺寸:578x700 我想为占位符大小生成缩略图:200x150、185x138、140x140、89x66、80x80、45x45、28x28

我已经创建了下面的代码,但没有得到确切的结果。 这是我生成缩略图的核心方法

    public string GenerateThumbnailFromImage(string imageFilePath, int thumbWidth, int thumbHeight)
    {
        try
        {
            //Check if file exist
            if (File.Exists(imageFilePath))
            {
                //bool preserveAspectRatio = true;
                string oldFilePath = imageFilePath;
                string folderPath = Path.GetDirectoryName(imageFilePath);
                string filename = Path.GetFileNameWithoutExtension(imageFilePath);

                //Rename file with thumbnail size
                filename = filename + "_" + thumbWidth.ToString() + Path.GetExtension(imageFilePath);
                imageFilePath = Path.Combine(folderPath, filename);


                using (Image image = Image.FromFile(oldFilePath))
                {
                    decimal originalWidth = image.Width;
                    decimal originalHeight = image.Height;
                    decimal requiredThumbWidth = thumbWidth;
                    decimal requiredThumbHeight = thumbHeight;
                    decimal startXPosition = 0;
                    decimal startYPosition = 0;
                    decimal screenWidth = originalWidth;
                    decimal screenHeight = originalHeight;
                    decimal ar = thumbWidth < thumbHeight 
                                     ? originalWidth / originalHeight
                                     : originalHeight / originalWidth;

                    //Define Starting Position for thumbnail generation
                    if (originalWidth > originalHeight)
                        startXPosition = (originalWidth - originalHeight) / 2;
                    else if (originalHeight > originalWidth)
                        startYPosition = (originalHeight - originalWidth) / 2;

                    if (thumbWidth>thumbHeight)
                    {
                        requiredThumbWidth = thumbWidth;
                        requiredThumbHeight = requiredThumbWidth*ar;
                    }
                    else if (thumbHeight>thumbWidth)
                    {
                        requiredThumbHeight = thumbHeight;
                        requiredThumbWidth = requiredThumbHeight*ar;
                    }
                    else
                    {
                        requiredThumbWidth = thumbWidth;
                        requiredThumbHeight = thumbWidth;
                    }

                    using (var bmp = new Bitmap((int)requiredThumbWidth, (int)requiredThumbHeight))
                    {
                        Graphics gr = Graphics.FromImage(bmp);
                        gr.SmoothingMode = SmoothingMode.HighQuality;
                        gr.CompositingQuality = CompositingQuality.HighQuality;
                        gr.InterpolationMode = InterpolationMode.High;
                        var rectDestination = new Rectangle(0, 0, (int)requiredThumbWidth, (int)requiredThumbHeight);

                        gr.DrawImage(image, rectDestination, (int)startXPosition, (int)startYPosition, (int)screenWidth, (int)screenHeight, GraphicsUnit.Pixel);
                        bmp.Save(imageFilePath);

                        return filename;
                    }
                }
            }
            return null;
        }
        catch (Exception ex)
        {
            GlobalUtil.HandleAndLogException(ex, this);
            throw ex;
        }
        finally
        {

        }
    }

【问题讨论】:

  • “没有得到确切的结果”描述性不是很好
  • this的可能重复
  • @Naren 该帖子用于裁剪为缩略图,如果此人需要为缩略图调整大小,那么我的答案如下。
  • 没有得到确切的结果意味着它产生了一些拉伸。
  • @ColinSteel 通过问题的标题,我认为他需要从图像的中心将图像裁剪为所需的大小..

标签: c# .net image-processing thumbnails


【解决方案1】:

之前做过几次,诀窍是首先适合图像的高度,将宽度重新缩放到您必须减小高度的比例,然后如果仍然不适合宽度,则从宽度重复,并按该额外比例减少新的缩放高度。这样,您的缩略图总是合适的,可能在 X 或 Y 中有一些空白,但图像仍保持相同的比例,而不是拉伸。

int originalHeight;
int originalWidth;
int imageHeight;
int imageWidth;
int requiredHeight;
int requiredWidth;
double scale;

if(originalHeight > requiredHeight)
{
    scale = requiredHeight / originalHeight;
    imageHeight = requiredHeight;
    imageWidth = originalHeight * scale;
}

if(imageWidth > requiredWidth)
{
    scale = requiredWidth / imageWidth;
    imageWidth = requiredWidth;
    imageHeight = imageHeight * scale;
}

然后使用 Graphics 对象将 Image 绘制到此大小的新 Bitmap

【讨论】:

    【解决方案2】:

    试试这个:

    bool SaveCroppedImage(Image image, int targetWidth, int targetHeight, string filePath)
    {
        ImageCodecInfo jpgInfo = ImageCodecInfo.GetImageEncoders().Where(codecInfo => codecInfo.MimeType == "image/jpeg").First();
        Image finalImage = image;
        System.Drawing.Bitmap bitmap = null;
        try
        {
            int left = 0;
            int top = 0;
            int srcWidth = targetWidth;
            int srcHeight = targetHeight;
            bitmap = new System.Drawing.Bitmap(targetWidth, targetHeight);
            double croppedHeightToWidth = (double)targetHeight / targetWidth;
            double croppedWidthToHeight = (double)targetWidth / targetHeight;
    
            if (image.Width > image.Height)
            {
                srcWidth = (int)(Math.Round(image.Height * croppedWidthToHeight));
                if (srcWidth < image.Width)
                {
                    srcHeight = image.Height;
                    left = (image.Width - srcWidth) / 2;
                }
                else
                {
                    srcHeight = (int)Math.Round(image.Height * ((double)image.Width / srcWidth));
                    srcWidth = image.Width;
                    top = (image.Height - srcHeight) / 2;
                }
            }
            else
            {
                srcHeight = (int)(Math.Round(image.Width * croppedHeightToWidth));
                if (srcHeight < image.Height)
                {
                    srcWidth = image.Width;
                    top = (image.Height - srcHeight) / 2;
                }
                else
                {
                    srcWidth = (int)Math.Round(image.Width * ((double)image.Height / srcHeight));
                    srcHeight = image.Height;
                    left = (image.Width - srcWidth) / 2;
                }
            }
            using (Graphics g = Graphics.FromImage(bitmap))
            {
                g.SmoothingMode = SmoothingMode.HighQuality;
                g.PixelOffsetMode = PixelOffsetMode.HighQuality;
                g.CompositingQuality = CompositingQuality.HighQuality;
                g.InterpolationMode = InterpolationMode.HighQualityBicubic;
                g.DrawImage(image, new Rectangle(0, 0, bitmap.Width, bitmap.Height), new Rectangle(left, top, srcWidth, srcHeight), GraphicsUnit.Pixel);
            }
            finalImage = bitmap;
        }
        catch { }
        try
        {
            using (EncoderParameters encParams = new EncoderParameters(1))
            {
                encParams.Param[0] = new EncoderParameter(Encoder.Quality, (long)100);
                //quality should be in the range [0..100] .. 100 for max, 0 for min (0 best compression)
                finalImage.Save(filePath, jpgInfo, encParams);
                return true;
            }
        }
        catch { }
        if (bitmap != null)
        {
            bitmap.Dispose();
        }
        return false;
    }
    

    【讨论】:

      【解决方案3】:

      您需要获取目标尺寸与实际尺寸的比率。缩放较短的一侧,直到它接触到实际的图像尺寸。从中心开始裁剪并缩放到所需的大小。

      代码如下:

      public static Image ResizeImage(Image imgToResize, Size destinationSize) { var originalWidth = imgToResize.Width; var originalHeight = imgToResize.Height; //how many units are there to make the original length var hRatio = (float)originalHeight/destinationSize.Height; var wRatio = (float)originalWidth/destinationSize.Width; //get the shorter side var ratio = Math.Min(hRatio, wRatio); var hScale = Convert.ToInt32(destinationSize.Height * ratio); var wScale = Convert.ToInt32(destinationSize.Width * ratio); //start cropping from the center var startX = (originalWidth - wScale)/2; var startY = (originalHeight - hScale)/2; //crop the image from the specified location and size var sourceRectangle = new Rectangle(startX, startY, wScale, hScale); //the future size of the image var bitmap = new Bitmap(destinationSize.Width, destinationSize.Height); //fill-in the whole bitmap var destinationRectangle = new Rectangle(0, 0, bitmap.Width, bitmap.Height); //generate the new image using (var g = Graphics.FromImage(bitmap)) { g.InterpolationMode = InterpolationMode.HighQualityBicubic; g.DrawImage(imgToResize, destinationRectangle, sourceRectangle, GraphicsUnit.Pixel); } return bitmap; }

      这样称呼它:

      var thumbImage = ImageHelper.ResizeImage(image, new Size(45, 45)); thumbImage.Save(thumbFullPath);

      【讨论】:

      • 您似乎只是在调整图像大小。我尝试了你的函数,它产生的结果与我自己的函数相同,它只是调整了图像的大小。它不应该裁剪吗?
      • 不,只是尝试使用更大的图像,它会裁剪和缩放,以便保持比例。效果很好
      • 它工作得很好并且解释得很好。我希望我能投票两次
      • 工作正常。感谢这个有用的解决方案。好作品给你。 :*
      【解决方案4】:
      public Image ScaleImage(Image image, int maxWidth, int maxHeight)
      {
          var ratioX = (double)maxWidth / image.Width;
          var ratioY = (double)maxHeight / image.Height;
          var ratio = Math.Min(ratioX, ratioY);
      
          var newWidth = (int)(image.Width * ratio);
          var newHeight = (int)(image.Height * ratio);
      
          var newImage = new Bitmap(maxWidth, maxWidth);
          using (var graphics = Graphics.FromImage(newImage))
          {
              // Calculate x and y which center the image
              int y = (maxHeight/2) - newHeight / 2;
              int x = (maxWidth / 2) - newWidth / 2;
      
              // Draw image on x and y with newWidth and newHeight
              graphics.DrawImage(image, x, y, newWidth, newHeight);
          }
      
          return newImage;
      }
      

      【讨论】:

        【解决方案5】:

        在您的 NuGet 包中,通过 James South 添加此 ImageFactory 解决方案。你需要的一切都在那里。我希望我可以给詹姆斯买更多的啤酒。

        示例用法:

        using (ImageFactory imgf = new ImageFactory(preserveExifData: true)) {
            imgf
                .Load(img)
                .Crop(rect)
                .Format(new JpegFormat { Quality = 100 })
                .Save(destination)
        }
        
        // given that :
        // 'img' is your Image object, could be an Image.FromFile() object or the likes
        // 'rect' is the size of your crop and that you have already did the math
        // new JpegFormat { Quality = 70 } is part of the package
        // and 'destination' is the destination path of your new image in your disk
        

        【讨论】:

        • 这仅回答了一半的问题。 OP 要求在图像的中心进行裁剪。您必须先找到图像中心(在坐标中),然后执行您在此处所做的操作。
        【解决方案6】:

        另外使用 ImageFactory NuGet 包,我建议使用 Resize Crop 功能。

        查看here的示例

        using ImageProcessor;
        using ImageProcessor.Imaging;
        using System.Drawing;
        using System.IO;
        
        public static byte[] ResizeAndCrop(byte[] image, int width, int height)
            {
                using (var ms = new MemoryStream())
                {
                    using (var imgf = new ImageFactory(true))
                        imgf
                            .Load(image)
                            .Resize(new ResizeLayer(new Size(width, height), ResizeMode.Crop))
                            .Save(ms);
                    return ms.ToArray();
                }
            }
        

        这将采用byte[] 格式的任何图像并从中心裁剪。

        这是由 ResizeLayer 中的 anchorPosition 参数设置的,其默认值为 AnchorPosition.Center

        new ResizeLayer(new Size(width, height), ResizeMode.Crop/*, AnchorPosition.Center*/)
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2014-05-26
          • 2019-05-13
          • 1970-01-01
          • 2011-06-10
          • 2020-09-07
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多