C# 调整大小后在图像周围鬼影般地勾勒出轮廓

C# 调整大小后在图像周围鬼影般地勾勒出轮廓,c#,.net,image,resize,scale,C#,.net,Image,Resize,Scale,我在一个网站上工作,将出售手工制作的珠宝,我正在完成图像编辑器,但它的行为不太正确 基本上,用户上传一个图像,该图像将保存为源文件,然后调整大小以适应用户的屏幕,并保存为临时文件。然后,用户将进入一个屏幕,允许他们裁剪图像,然后将其保存到最终版本 所有这些都很好,除了最终版本有3个bug。首先是图像底部的一些黑色水平线。第二种是沿着边缘排列的轮廓。我认为这是因为我降低了质量,但即使在100%的情况下,它仍然显示出来。。。最后,我注意到裁剪后的图像总是比我指定的低几个像素 不管怎么说,我希望有经验

我在一个网站上工作,将出售手工制作的珠宝,我正在完成图像编辑器,但它的行为不太正确

基本上,用户上传一个图像,该图像将保存为源文件,然后调整大小以适应用户的屏幕,并保存为临时文件。然后,用户将进入一个屏幕,允许他们裁剪图像,然后将其保存到最终版本

所有这些都很好,除了最终版本有3个bug。首先是图像底部的一些黑色水平线。第二种是沿着边缘排列的轮廓。我认为这是因为我降低了质量,但即使在100%的情况下,它仍然显示出来。。。最后,我注意到裁剪后的图像总是比我指定的低几个像素

不管怎么说,我希望有经验的人可以看看代码,看看我可能会偏离正确的方向

哦,顺便说一下,这是在ASP.NETMVC应用程序中实现的

代码如下:

这是它生成的图像之一:

更新

所以,在我发布、验证我的代码后,我坐下来阅读了MSDN大约2个小时。据我所知,我在执行操作时使用了尽可能高的质量设置

不管怎样,我最终还是对代码进行了一些清理和简化。我考虑了对源文件的需要,并决定删除它,因为它需要我做额外的工作,以确定基于临时文件的裁剪尺寸。所以,它不见了

此外,在流线型的某个地方,黑线消失了,所以我只能假设纵横比问题应该按照@StefanE所说的那样得到纠正

另外,正如@VinayC所说,重定尺寸器生成的高度值为479,我仍然不知道如何得到,但不管怎样…,但当我一直使用System.Drawing.Size和System.Drawing.Rectangle类,而不是使用我自己的类时,这似乎已经得到了纠正,因为我自己的类基本上做了相同的事情

下面是更新后的代码。剩下的两个bug仍然存在,所以我在图像周围有一个重影轮廓。请看第二个附件,我可以缩小到调整大小,因为它显示在第一次重新调整大小时,没有发生裁剪。第二个错误是被裁剪的版本在y轴上的位置总是低于我作为裁剪器传入的位置。我估计这比我说的要低5%-8%,所以我不确定这一点,位置不应该改变。。。除非我从jQuery传递了一个错误的数字,否则我必须检查一下

我的第二个错误导致我首先发送了错误的值,裁剪div是基于主内容div而不是图像容器div获取其相对位置的。所有这些都已修复

using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.IO;
using System.Linq;
using System.Web;

namespace Website.Models.Providers {
    public class ImageProvider {
        private readonly ProductProvider ProductProvider = null;

        private readonly EncoderParameters DefaultQualityEncoder = new EncoderParameters();
        private readonly EncoderParameters HighQualityEncoder = new EncoderParameters();
        private readonly ImageCodecInfo JpegCodecInfo = ImageCodecInfo.GetImageEncoders().Single(
            c =>
                (c.MimeType == "image/jpeg"));
        private readonly Size[] Sizes = new Size[3] {
            new Size(640, 0),
            new Size(240, 0),
            new Size(80, 0)
        };

        private readonly string Path = HttpContext.Current.Server.MapPath("~/Resources/Images/Products");

        public ImageProvider(
            ProductProvider ProductProvider) {
            this.ProductProvider = ProductProvider;

            this.DefaultQualityEncoder.Param[0] = new EncoderParameter(Encoder.Quality, 80L);
            this.HighQualityEncoder.Param[0] = new EncoderParameter(Encoder.Quality, 100L);
        }

        public void Crop(
            string FileName,
            Image Image,
            Crop Crop) {
            using (Bitmap Source = new Bitmap(Image)) {
                using (Bitmap Target = new Bitmap(Crop.Width, Crop.Height)) {
                    using (Graphics Graphics = Graphics.FromImage(Target)) {
                        Graphics.CompositingMode = CompositingMode.SourceCopy;
                        Graphics.CompositingQuality = CompositingQuality.HighQuality;
                        Graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
                        Graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
                        Graphics.SmoothingMode = SmoothingMode.HighQuality;

                        Graphics.DrawImage(Source, new Rectangle(0, 0, Target.Width, Target.Height), new Rectangle(Crop.Left, Crop.Top, Crop.Width, Crop.Height), GraphicsUnit.Pixel);
                    };

                    Target.Save(FileName, JpegCodecInfo, HighQualityEncoder);
                };
            };
        }

        public void CropAndResize(
            Product Product,
            Crop Crop) {
            using (Image Temp = Image.FromFile(String.Format("{0}/{1}.temp", Path, Product.ProductId))) {
                Img Img = new Img();

                this.ProductProvider.AddImageAndSave(Product, Img);

                this.Crop(String.Format("{0}/{1}.cropped", Path, Img.ImageId), Temp, Crop);

                using (Image Cropped = Image.FromFile(String.Format("{0}/{1}.cropped", Path, Img.ImageId))) {
                    this.Resize(String.Format("{0}/{1}-L.jpg", Path, Img.ImageId), this.Sizes[0], Cropped, HighQualityEncoder);
                    this.Resize(String.Format("{0}/{1}-T.jpg", Path, Img.ImageId), this.Sizes[1], Cropped, HighQualityEncoder);
                    this.Resize(String.Format("{0}/{1}-S.jpg", Path, Img.ImageId), this.Sizes[2], Cropped, HighQualityEncoder);
                };
            };

            this.Purge(Product);
        }

        public void QueueFor(
            Product Product,
            Size Size,
            HttpPostedFileBase PostedFile) {
            using (Image Image = Image.FromStream(PostedFile.InputStream)) {
                this.Resize(String.Format("{0}/{1}.temp", Path, Product.ProductId), Size, Image, HighQualityEncoder);
            };
        }

        private void Purge(
            Product Product) {
            string Temp = String.Format("{0}/{1}.temp", Path, Product.ProductId);

            if (File.Exists(Temp)) {
                File.Delete(Temp);
            };

            foreach (Img Img in Product.Imgs) {
                string Cropped = String.Format("{0}/{1}.cropped", Path, Img.ImageId);

                if (File.Exists(Cropped)) {
                    File.Delete(Cropped);
                };
            };
        }

        public void Resize(
            string FileName,
            Size Size,
            Image Image,
            EncoderParameters EncoderParameters) {
            if (Size.Height == 0) {
                Size.Height = (int)(Image.Height / ((float)Image.Width / (float)Size.Width));
            };

            using (Bitmap Bitmap = new Bitmap(Size.Width, Size.Height)) {
                using (Graphics Graphics = Graphics.FromImage(Bitmap)) {
                    Graphics.CompositingMode = CompositingMode.SourceCopy;
                    Graphics.CompositingQuality = CompositingQuality.HighQuality;
                    Graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
                    Graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
                    Graphics.SmoothingMode = SmoothingMode.HighQuality;

                    Graphics.DrawImage(Image, new Rectangle(0, 0, Size.Width, Size.Height));
                };

                Bitmap.Save(FileName, JpegCodecInfo, EncoderParameters);
            };
        }
    }
}
此图显示红色箭头指向的重影轮廓


我猜在您的输入/计算中,您可能提供了比实际存在的更大的源矩形-例如,源图像大小640 x 480,但在缩放DrawImage调用时,它被传递为640 x 479。另一种情况也可能是目标矩形比目标位图小,但通过查看代码,看起来不太可能。故障排除的最佳方法是放置断点并检查源图像大小,并比较它将源矩形大小裁剪参数

我猜在输入/计算中,您可能提供比实际存在的源矩形更大的源矩形-例如,源图像大小640 x 480,但在缩放DrawImage调用时,它将以640x479的形式通过。另一种情况也可能是目标矩形比目标位图小,但通过查看代码,看起来不太可能。故障排除的最佳方法是放置断点并检查源图像大小,然后比较它将源矩形大小裁剪参数

关于底部的黑线是因为您不处理纵横比

double aspectRatio = imageWidth/imageHeight;
double boxRatio = maxWidth/maxHeight;
double scaleFactor = 0;
if (boxRatio > aspectRatio)
 //Use height, since that is the most restrictive dimension of box.
 scaleFactor = maxHeight / imageHeight;
else
 scaleFactor = maxWidth / imageWidth;

double newWidth = imageWidth * scaleFactor;
double newHeight = imageHeight * scaleFactor;

来源:

关于底部的黑线是因为您没有处理纵横比

double aspectRatio = imageWidth/imageHeight;
double boxRatio = maxWidth/maxHeight;
double scaleFactor = 0;
if (boxRatio > aspectRatio)
 //Use height, since that is the most restrictive dimension of box.
 scaleFactor = maxHeight / imageHeight;
else
 scaleFactor = maxWidth / imageWidth;

double newWidth = imageWidth * scaleFactor;
double newHeight = imageHeight * scaleFactor;

来源:

各位,好消息!我把它修好了,我不得不说这个解决方案非常简单,它让我咕哝了一声,然后叹了一口气。显然,在调整大小的方法中,我所做的工作远远超过了调整图像大小所需的工作量。整个系统使用图形。。。这就是问题所在。您只需使用位图位图=新位图源\图像、新\大小即可

干净、简单,这让我想知道为什么我在其他项目中使用的web教程和代码在不需要图形类的时候却强制使用图形类

因此,无需进一步说明,这里是我的代码的最终版本,供任何可能发现它有用的人使用。请记住,CropAndResize、QueueFor和Purge方法专门用于处理我的域模型,但是 整个类最终涉及的裁剪和调整大小方法可以轻松地适应任何其他应用程序

享受:

public class ImageProvider {
    private readonly ProductProvider ProductProvider = null;

    private readonly EncoderParameters DefaultQualityEncoder = new EncoderParameters();
    private readonly EncoderParameters HighQualityEncoder = new EncoderParameters();
    private readonly ImageCodecInfo JpegCodecInfo = ImageCodecInfo.GetImageEncoders().Single(
        c =>
            (c.MimeType == "image/jpeg"));
    private readonly Size[] Sizes = new Size[3] {
        new Size(640, 0),
        new Size(280, 0),
        new Size(80, 0)
    };

    private readonly string Path = HttpContext.Current.Server.MapPath("~/Resources/Images/Products");

    public ImageProvider(
        ProductProvider ProductProvider) {
        this.ProductProvider = ProductProvider;

        this.DefaultQualityEncoder.Param[0] = new EncoderParameter(Encoder.Quality, 90L);
        this.HighQualityEncoder.Param[0] = new EncoderParameter(Encoder.Quality, 100L);
    }

    private void Crop(
        string FileName,
        Image Image,
        Crop Crop) {
        using (Bitmap Source = new Bitmap(Image)) {
            Source.SetResolution(Image.HorizontalResolution, Image.VerticalResolution);

            using (Bitmap Target = new Bitmap(Crop.Width, Crop.Height, Image.PixelFormat)) {
                Target.SetResolution(Image.HorizontalResolution, Image.VerticalResolution);

                using (Graphics Graphics = Graphics.FromImage(Target)) {
                    Graphics.CompositingMode = CompositingMode.SourceCopy;
                    Graphics.CompositingQuality = CompositingQuality.HighQuality;
                    Graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
                    Graphics.PageUnit = GraphicsUnit.Pixel;
                    Graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
                    Graphics.SmoothingMode = SmoothingMode.HighQuality;

                    Graphics.DrawImage(Source, new Rectangle(0, 0, Target.Width, Target.Height), new Rectangle(Crop.Left, Crop.Top, Crop.Width, Crop.Height), GraphicsUnit.Pixel);
                };

                Target.Save(FileName, JpegCodecInfo, HighQualityEncoder);
            };
        };
    }

    public void CropAndResize(
        Product Product,
        Crop Crop) {
        using (Image Temp = Image.FromFile(String.Format("{0}/{1}.temp", Path, Product.ProductId))) {
            Img Img = new Img();

            this.ProductProvider.AddImageAndSave(Product, Img);

            this.Crop(String.Format("{0}/{1}.cropped", Path, Img.ImageId), Temp, Crop);

            using (Image Cropped = Image.FromFile(String.Format("{0}/{1}.cropped", Path, Img.ImageId))) {
                this.Resize(String.Format("{0}/{1}-L.jpg", Path, Img.ImageId), this.Sizes[0], Cropped, DefaultQualityEncoder);
                this.Resize(String.Format("{0}/{1}-T.jpg", Path, Img.ImageId), this.Sizes[1], Cropped, DefaultQualityEncoder);
                this.Resize(String.Format("{0}/{1}-S.jpg", Path, Img.ImageId), this.Sizes[2], Cropped, DefaultQualityEncoder);
            };
        };

        this.Purge(Product);
    }

    public void QueueFor(
        Product Product,
        Size Size,
        HttpPostedFileBase PostedFile) {
        using (Image Image = Image.FromStream(PostedFile.InputStream)) {
            this.Resize(String.Format("{0}/{1}.temp", Path, Product.ProductId), Size, Image, HighQualityEncoder);
        };
    }

    private void Purge(
        Product Product) {
        string Temp = String.Format("{0}/{1}.temp", Path, Product.ProductId);

        if (File.Exists(Temp)) {
            File.Delete(Temp);
        };

        foreach (Img Img in Product.Imgs) {
            string Cropped = String.Format("{0}/{1}.cropped", Path, Img.ImageId);

            if (File.Exists(Cropped)) {
                File.Delete(Cropped);
            };
        };
    }

    private void Resize(
        string FileName,
        Size Size,
        Image Image,
        EncoderParameters EncoderParameters) {
        if (Size.Height == 0) {
            Size.Height = (int)(Image.Height / ((float)Image.Width / (float)Size.Width));
        };

        using (Bitmap Bitmap = new Bitmap(Image, Size)) {
            Bitmap.Save(FileName, JpegCodecInfo, EncoderParameters);
        };
    }
}

各位,好消息!我把它修好了,我不得不说这个解决方案非常简单,它让我咕哝了一声,然后叹了一口气。显然,在调整大小的方法中,我所做的工作远远超过了调整图像大小所需的工作量。整个系统使用图形。。。这就是问题所在。您只需使用位图位图=新位图源\图像、新\大小即可

干净、简单,这让我想知道为什么我在其他项目中使用的web教程和代码在不需要图形类的时候却强制使用图形类

因此,无需进一步说明,这里是我的代码的最终版本,供任何可能发现它有用的人使用。请记住,CropAndResize、QueueFor和Purge方法专门用于处理我的域模型,但是这个类最终涉及的裁剪和调整大小方法可以轻松地适应任何其他应用程序

享受:

public class ImageProvider {
    private readonly ProductProvider ProductProvider = null;

    private readonly EncoderParameters DefaultQualityEncoder = new EncoderParameters();
    private readonly EncoderParameters HighQualityEncoder = new EncoderParameters();
    private readonly ImageCodecInfo JpegCodecInfo = ImageCodecInfo.GetImageEncoders().Single(
        c =>
            (c.MimeType == "image/jpeg"));
    private readonly Size[] Sizes = new Size[3] {
        new Size(640, 0),
        new Size(280, 0),
        new Size(80, 0)
    };

    private readonly string Path = HttpContext.Current.Server.MapPath("~/Resources/Images/Products");

    public ImageProvider(
        ProductProvider ProductProvider) {
        this.ProductProvider = ProductProvider;

        this.DefaultQualityEncoder.Param[0] = new EncoderParameter(Encoder.Quality, 90L);
        this.HighQualityEncoder.Param[0] = new EncoderParameter(Encoder.Quality, 100L);
    }

    private void Crop(
        string FileName,
        Image Image,
        Crop Crop) {
        using (Bitmap Source = new Bitmap(Image)) {
            Source.SetResolution(Image.HorizontalResolution, Image.VerticalResolution);

            using (Bitmap Target = new Bitmap(Crop.Width, Crop.Height, Image.PixelFormat)) {
                Target.SetResolution(Image.HorizontalResolution, Image.VerticalResolution);

                using (Graphics Graphics = Graphics.FromImage(Target)) {
                    Graphics.CompositingMode = CompositingMode.SourceCopy;
                    Graphics.CompositingQuality = CompositingQuality.HighQuality;
                    Graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
                    Graphics.PageUnit = GraphicsUnit.Pixel;
                    Graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
                    Graphics.SmoothingMode = SmoothingMode.HighQuality;

                    Graphics.DrawImage(Source, new Rectangle(0, 0, Target.Width, Target.Height), new Rectangle(Crop.Left, Crop.Top, Crop.Width, Crop.Height), GraphicsUnit.Pixel);
                };

                Target.Save(FileName, JpegCodecInfo, HighQualityEncoder);
            };
        };
    }

    public void CropAndResize(
        Product Product,
        Crop Crop) {
        using (Image Temp = Image.FromFile(String.Format("{0}/{1}.temp", Path, Product.ProductId))) {
            Img Img = new Img();

            this.ProductProvider.AddImageAndSave(Product, Img);

            this.Crop(String.Format("{0}/{1}.cropped", Path, Img.ImageId), Temp, Crop);

            using (Image Cropped = Image.FromFile(String.Format("{0}/{1}.cropped", Path, Img.ImageId))) {
                this.Resize(String.Format("{0}/{1}-L.jpg", Path, Img.ImageId), this.Sizes[0], Cropped, DefaultQualityEncoder);
                this.Resize(String.Format("{0}/{1}-T.jpg", Path, Img.ImageId), this.Sizes[1], Cropped, DefaultQualityEncoder);
                this.Resize(String.Format("{0}/{1}-S.jpg", Path, Img.ImageId), this.Sizes[2], Cropped, DefaultQualityEncoder);
            };
        };

        this.Purge(Product);
    }

    public void QueueFor(
        Product Product,
        Size Size,
        HttpPostedFileBase PostedFile) {
        using (Image Image = Image.FromStream(PostedFile.InputStream)) {
            this.Resize(String.Format("{0}/{1}.temp", Path, Product.ProductId), Size, Image, HighQualityEncoder);
        };
    }

    private void Purge(
        Product Product) {
        string Temp = String.Format("{0}/{1}.temp", Path, Product.ProductId);

        if (File.Exists(Temp)) {
            File.Delete(Temp);
        };

        foreach (Img Img in Product.Imgs) {
            string Cropped = String.Format("{0}/{1}.cropped", Path, Img.ImageId);

            if (File.Exists(Cropped)) {
                File.Delete(Cropped);
            };
        };
    }

    private void Resize(
        string FileName,
        Size Size,
        Image Image,
        EncoderParameters EncoderParameters) {
        if (Size.Height == 0) {
            Size.Height = (int)(Image.Height / ((float)Image.Width / (float)Size.Width));
        };

        using (Bitmap Bitmap = new Bitmap(Image, Size)) {
            Bitmap.Save(FileName, JpegCodecInfo, EncoderParameters);
        };
    }
}

好吧,根据我上面更新的代码,黑线是自己修复的,但我还是很感激你的建议。另外,那篇文章实际上相当不错。@Alex:这篇文章在一个项目中为我节省了大量时间:嗯,根据我上面更新的代码,黑线是自己修复的,但我仍然感谢你的建议。而且那篇文章实际上相当不错。@Alex:这篇文章为我在一个项目中节省了大量时间:我想当我一直使用System.Drawing.Size和System.Drawing.Rectangle类而不是用我自己的类替换时,大小问题得到了纠正。我想当我切换到使用System.Drawing.Size和System.Drawing.Rectangle类时,大小问题得到了纠正System.Drawing.Size和System.Drawing.Rectangle类,而不是用我自己的类替换。