调整大小后围绕图像的幽灵轮廓

Gup*_*R4c 1 .net c# resize image scale

我正在建立一个销售手工制作珠宝的网站,我正在完成图像编辑器,但它表现得并不是很正确.

基本上,用户上传将保存为源的图像,然后将其调整大小以适合用户的屏幕并保存为临时.然后,用户将转到允许他们裁剪图像的屏幕,然后将其保存到最终版本.

所有这些都很好,除了最终版本有3个错误.首先是图像底部的一些黑色水平线.第二个是边缘之后的各种轮廓.我认为这是因为我降低了质量,但即使是100%它仍然会出现......最后,我注意到裁剪后的图像总是比我指定的低几个像素......

无论如何,我希望有人用C#编辑图像的经验可以看看代码,看看我可能会走正路.

哦,顺便说一句,这是在ASP.NET MVC应用程序中.

这是代码:

public class ImageProvider {
    private readonly ProductProvider ProductProvider = null;

    private readonly EncoderParameters HighQualityEncoder = new EncoderParameters();
    private readonly ImageCodecInfo JpegCodecInfo = ImageCodecInfo.GetImageEncoders().Single(
        c => (c.MimeType == "image/jpeg"));

    private readonly string Path = HttpContext.Current.Server.MapPath("~/Resources/Images/Products");
    private readonly short[][] Dimensions = new short[3][] {
        new short[2] { 640, 480 },
        new short[2] { 240, 0 },
        new short[2] { 80, 60 }
    }

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

        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.InterpolationMode = InterpolationMode.HighQualityBicubic;
                    Graphics.SmoothingMode = SmoothingMode.HighQuality;
                    Graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
                    Graphics.CompositingQuality = CompositingQuality.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 Source = Image.FromFile(String.Format("{0}/{1}.source", Path, Product.ProductId))) {
            using (Image Temp = Image.FromFile(String.Format("{0}/{1}.temp", Path, Product.ProductId))) {
                float Percent = ((float)Source.Width / (float)Temp.Width);

                short Width = (short)(Temp.Width * Percent);
                short Height = (short)(Temp.Height * Percent);

                Crop.Height = (short)(Crop.Height * Percent);
                Crop.Left = (short)(Crop.Left * Percent);
                Crop.Top = (short)(Crop.Top * Percent);
                Crop.Width = (short)(Crop.Width * Percent);

                Img Img = new Img();

                this.ProductProvider.AddImageAndSave(Product, Img);

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

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

        this.Purge(Product);
    }

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

        PostedFile.SaveAs(String.Format("{0}/{1}.source", Path, Product.ProductId));
    }

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

        if (File.Exists(Source)) {
            File.Delete(Source);
        }
        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( short[] Dimensions, string FileName, Image Image, EncoderParameters EncoderParameters) {
        if (Dimensions[1] == 0) {
            Dimensions[1] = (short)(Image.Height / ((float)Image.Width / (float)Dimensions[0]));
        }

        using (Bitmap Bitmap = new Bitmap(Dimensions[0], Dimensions[1])) {
            using (Graphics Graphics = Graphics.FromImage(Bitmap)) {
                Graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
                Graphics.SmoothingMode = SmoothingMode.HighQuality;
                Graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
                Graphics.CompositingQuality = CompositingQuality.HighQuality;

                Graphics.DrawImage(Image, 0, 0, Dimensions[0], Dimensions[1]);
            };

            Bitmap.Save(FileName, JpegCodecInfo, EncoderParameters);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

这是其产生的图像之一:

http://i.stack.imgur.com/GJX97.jpg

UPDATE

所以,在我发布后,我坐下来阅读了MSDN 2小时,验证了我的代码.据我所知,我在执行操作时使用了最高质量的设置.

无论哪种方式,我最终清理并简化了代码.我考虑了源文件的需要,并决定删除它,因为它要求我做额外的工作来找出基于临时文件的裁剪尺寸.所以,它已经消失了.

此外,在精简的某个地方,黑线消失了,所以我只能假设宽高比问题应该已经纠正,正如@StefanE所述.

另外,正如@VinayC所说,重新分级器为高度产生了479的值(我仍然不知道如何,但无论如何......),但是当我切换到使用时,似乎已经纠正了System.Drawing.Size和System.Drawing.Rectangle类一直通过而不是使用我自己的类,它们基本上做同样的事情.

所以,这是更新的代码.剩下的两个bug 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);
            };
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

此图像显示红色箭头指向的"重影"轮廓.

替代文字

Ste*_*anE 6

关于底部的黑线是因为你没有处理宽高比.

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;
Run Code Online (Sandbox Code Playgroud)

资料来源:http://nathanaeljones.com/163/20-image-resizing-pitfalls/