调整大小后图像周围出现幽灵般的轮廓

发布于 2024-10-08 06:12:49 字数 11597 浏览 1 评论 0原文

我正在一个销售手工珠宝的网站上工作,我正在完成图像编辑器,但它的表现不太正确。

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

所有这些都运行良好,除了最终版本有 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);
        }
    }
}

这是生成的图像之一:

https://i. sstatic.net/GJX97.jpg

更新

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

不管怎样,我最终清理并简化了代码。我考虑到了对源文件的需求,并决定将其删除,因为它需要我做额外的工作来找出基于临时文件的裁剪尺寸。所以,它消失了。

另外,在精简过程中的某个地方,黑线消失了,所以我只能假设长宽比问题应该按照 @StefanE 的说明得到纠正。

另外,正如 @VinayC 所说,重新调整大小器生成的高度值为 479(我仍然不知道如何实现,但无论如何......),但是当我切换到使用System.Drawing.Size 和 System.Drawing.Rectangle 类一直贯穿始终,而不是使用我自己的类,它们本质上做同样的事情。

所以,这是更新后的代码。剩下的两个错误错误仍然存​​在,所以我在图像周围有一个“重影”轮廓(参见第二个附件),我可以缩小调整大小的范围,因为它出现在第一个重新调整中- 未发生裁剪的尺寸。 第二个错误是,裁剪后的版本在 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);
            };
        }
    }
}

此图像显示了红色箭头所指的“重影”轮廓。

替代文本

I'm working on a website that will sell hand made jewelry and I'm finishing the image editor, but it's not behaving quite right.

Basically, the user uploads an image which will be saved as a source and then it will be resized to fit the user's screen and saved as a temp. The user will then go to a screen that will allow them to crop the image and then save it to it's final versions.

All of that works fine, except, the final versions have 3 bugs. First is some black horizontal line on the very bottom of the image. Second is an outline of sorts that follows the edges. I thought it was because I was reducing the quality, but even at 100% it still shows up... And lastly, I've noticed that the cropped image is always a couple of pixels lower than what I'm specifying...

Anyway, I'm hoping someone whose got experience in editing images with C# can maybe take a look at the code and see where I might be going off the right path.

Oh, by the way, this in an ASP.NET MVC application.

Here's the code:

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);
        }
    }
}

Here's one of the images this produces:

https://i.sstatic.net/GJX97.jpg

UPDATE

So, I sat down and read through MSDN for like 2 hours after I posted, verified the code I had. As far as I can tell, I'm using the highest possible quality settings when performing the manipulation.

Either way, I ended up cleaning up and streamlining the code a bit. I contemplated the need for the source file and decided to remove it because it was requiring me to do additional work to figure out the cropping dimensions that are based on the temp file. So, it's gone.

Also, somewhere along the streamlining, the black line disappeared, so I can only assume that the aspect ratio issues should have been corrected as @StefanE stated.

Also, as @VinayC said, the re-sizer was generating a value of 479 for the height (which I still don't get how, but whatever...), but that seems to have been corrected when I switched to using the System.Drawing.Size and System.Drawing.Rectangle classes all the way through instead of using my own classes which essentially do the same thing.

So, here's the updated code. The remaining two bugs bug still stand, so I have a "ghosting" outline all the way around the image (see second attachment) which I can narrow down to the resizing because it shows up in the first re-size where no cropping has occurred. And the second bug is that the cropped versions are always positioned lower on the y axis than what I pass in as the cropper. I'd estimate it's a good 5%-8% lower than what I tell it, so not sure about that one (the positions shouldn't be changing... unless I'm passing in a bad number from jQuery, gotta check on that...).

(My second bug ended up me sending bad values to begin with, the cropping div was getting its relative position based on the master content div, not the image container div. All fixed now.)

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);
            };
        }
    }
}

This image shows the "ghosting" outline pointed to by red arrows.

alt text

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

灯角 2024-10-15 06:12:49

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

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;

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

Regarding the black lines in the bottom is because you don't handle the Aspect Ratio.

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;

Source: http://nathanaeljones.com/163/20-image-resizing-pitfalls/

谁把谁当真 2024-10-15 06:12:49

我猜想在您的输入/计算中,您可能提供比实际存在的更大的源矩形 - 例如,源图像大小 640 x 480 但在缩放(DrawImage 调用)时,它被传递为 640 x 479。其他方法是目标矩形也可能小于目标位图 - 但通过查看您的代码似乎不太可能。解决问题的最佳方法是设置断点并检查源图像大小并比较源矩形大小(裁剪参数)

I guess is that in your input/calculations, you are probably providing larger source rectangle than actually exists - for example, source image size 640 x 480 but while scaling (DrawImage call), it is getting passed as 640 x 479. Other way is also possible that destination rectangle is smaller than target bitmap - but it appears unlikely by looking at your code. Best way to trouble shoot is to put break-point and inspect source image size and compare it will source rectangle size (crop parameter)

渔村楼浪 2024-10-15 06:12:49

大家好消息!我修好了它,我不得不说解决方案是如此简单,它只是让我非常痛苦地咕哝和叹息。显然,在 Resize 方法中,我所做的工作比调整图像大小所需的工作要多。整个使用(图形...)就是问题所在。您只需使用 (Bitmap Bitmap = new Bitmap(SOURCE_IMAGE, NEW_SIZE)) 就可以了。

干净简单,这让我想知道为什么网络上的教程(以及我迄今为止在其他项目中使用的代码)在不需要时强制使用 Graphics 类?

因此,无需进一步说明,这是我的代码的最终版本,供任何可能觉得有用的人使用。请记住,CropAndResizeQueueForPurge 方法专门用于我的域模型,但 Crop > 和 Resize 方法是整个类最终涉及的方法,可以轻松地适应任何其他应用程序。

享受:

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);
        };
    }
}

Good news, everyone! I fixed it, and I have to say the solution was so simple it just made me grunt and sigh really hard. Apperantly, in the Resize method I was doing way more work than was needed to resize an image. The whole using (Graphics ...) was the problem. You can just do using (Bitmap Bitmap = new Bitmap(SOURCE_IMAGE, NEW_SIZE)) and it works.

Clean and simple and it makes me wonder why tutorials on the web (and code I've been using up until now on other projects) force the use of the Graphics class when it's not needed?

So, without further a due, here is the final version of my code for anyone who may find it useful. Keep in mind that the CropAndResize, QueueFor and Purge methods are specialized to work with my domain model, but the Crop and Resize methods which this whole class is ultimately about can easily be adapted to any other application.

Enjoy:

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);
        };
    }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文