如何裁剪具有平滑边界的图像的椭圆区域 [英] How to crop an elliptical region of an Image with smooth borders

查看:75
本文介绍了如何裁剪具有平滑边界的图像的椭圆区域的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我的代码打开一个图像,调整其大小,然后裁剪一个圆形区域.
我想要的是更平滑的边框,因为裁剪后的图像显示出粗糙的,非抗锯齿的边缘.

My code opens an Image, resizes it, then crops a circular region.
What I want is smoother borders, since the cropped Image shows rough, non antialiased, edges.

图片大小为60x60

我尝试使用Graphics.SmoothingMode属性,但没有成功.

I have tried to use the Graphics.SmoothingMode property but without success.

到目前为止,我的项目中有什么:

What I have so far in my project:

private void Recorte_Click(object sender, EventArgs e)
{
    OpenFileDialog open = new OpenFileDialog();
    // Filter for image files
    open.Filter = "Image Files(*.jpg; *.jpeg; *.gif; *.bmp; *.png)|*.jpg; *.jpeg; *.gif; *.bmp; *.png";
    if (open.ShowDialog() == DialogResult.OK)
    {
        // display image in picture box 
        PointF p = new PointF(1, 1);
        Bitmap org = new Bitmap(open.FileName);
        Image srcImage = Bitmap.FromFile(open.FileName);
        // Resize image in 60x60
        Image resized = ResizeImage(srcImage, new Size(60, 60), false);
        MemoryStream memStream = new MemoryStream();
        // Crop in round shape
        Image cropped = CropToCircle(resized,Color.Transparent);
        cropped.Save(@"..\..\Cortada.png", System.Drawing.Imaging.ImageFormat.Png);
        pictureBox1.Image = cropped;
    }
}

public static Image CropToCircle(Image srcImage, Color backGround)
{
    Image dstImage = new Bitmap(srcImage.Width, srcImage.Height, srcImage.PixelFormat);
    Graphics g = Graphics.FromImage(dstImage);
    using (Brush br = new SolidBrush(backGround))
    {
        g.FillRectangle(br, 0, 0, dstImage.Width, dstImage.Height);
    }
    float radius = 25;
    PointF center = new Point(60, 60);
    GraphicsPath path = new GraphicsPath();
    path.AddEllipse(7, 7, radius * 2, radius * 2);
    g.SetClip(path);
    g.SmoothingMode = SmoothingMode.AntiAlias;
    g.InterpolationMode = InterpolationMode.HighQualityBilinear;
    g.SmoothingMode = SmoothingMode.AntiAlias;
    g.DrawImage(srcImage, 0, 0);

    return dstImage;
}

public static Image ResizeImage(Image image, Size size, bool preserveAspectRatio = true)
{
    int newWidth;
    int newHeight;
    if (preserveAspectRatio)
    {
        int originalWidth = image.Width;
        int originalHeight = image.Height;
        float percentWidth = (float)size.Width / (float)originalWidth;
        float percentHeight = (float)size.Height / (float)originalHeight;
        float percent = percentHeight < percentWidth ? percentHeight : percentWidth;
        newWidth = (int)(originalWidth * percent);
        newHeight = (int)(originalHeight * percent);
    }
    else
    {
        newWidth = size.Width;
        newHeight = size.Height;
    }
    Image newImage = new Bitmap(newWidth, newHeight);
    using (Graphics graphicsHandle = Graphics.FromImage(newImage))
    {
        graphicsHandle.InterpolationMode = InterpolationMode.HighQualityBicubic;
        graphicsHandle.DrawImage(image, 0, 0, newWidth, newHeight);
    }
    return newImage;
}

推荐答案

要使此功能按预期工作,需要进行一些更改:

Quite a few changes are required to get this working as expected:

  • 请勿使用Image.Fromfile():如果出于某些原因必须这样做,请始终使用允许保留嵌入式颜色管理信息的重载(string, bool).由于可以避免这种情况,因此请使用此处显示的方法,并使用File.ReadAllBytes()MemoryStream(或using块中的FileStream).

  • Don't use Image.Fromfile(): if you have to for some reason, always use the overload (string, bool) that allows to preserve the embedded color management information. Since you can avoid it, adopt the method shown here, using File.ReadAllBytes() and a MemoryStream (or a FileStream in a using block).

Graphics.SetClip()不允许抗锯齿.这也适用于地区(至少没有进一步的调整).在这里,我使用的是 TextureBrush 特殊笔刷从位图(您调整大小的位图)构建,然后将其用于填充裁剪调整大小后的图像的椭圆.

Graphics.SetClip() doesn't allow anti-aliasing. This also applies to Regions (without further adjustments, at least). Here, I'm using a TextureBrush, a special brush build from a Bitmap (your resized Bitmap), which is then used to fill the ellipse that crops the resized image.

处理创建的一次性对象非常重要,尤其是在处理图形对象时.当然,您需要处理创建的所有一次性对象(提供Dispose()方法的对象).这包括OpenFileDialog对象.

Disposing of the disposable objects you create is quite important, especially when you deal with graphics objects. Of course you need to dispose of all the disposable objects you create (objects that provide a Dispose() method). This includes the OpenFileDialog object.

请勿使用以下格式的路径:@"..\..\Image.png":当您将可执行文件移动到某处时,该路径将不存在(否则将无法访问,或者仅仅是错误)其他.
始终使用 Path.Combine()(如此处所示)构建完整路径.
此处的示例将映像保存在可执行路径的 CroppedImages 子文件夹中.
不过,该主题的内容非常广泛(例如,您可能不被允许在可执行文件路径中存储数据,因此您可能需要在User AppData文件夹或ProgramData目录中使用专用路径).

Don't use paths in this form: @"..\..\Image.png": this path won't exist (or it will not be inaccessible, or simply wrong) when you move your executable somewhere else.
Always use Path.Combine() (as shown here) to build a full path.
The example here saves the Image inside a CroppedImages subfolder in the executable path.
This topic is quite broad, though (e.g., you may not be allowed to store data in the executable path, so you may need to use a dedicated path in the User AppData folder or in the ProgramData directory).

所有计算都需要重新访问,看看我在这里发布的内容.

All calculations need to be revisited, take a look t what I posted here.

CropToCircle方法被重写,并且我添加了允许指定画笔颜色的重载.
然后将使用钢笔在裁剪的椭圆区域周围绘制边框.

The CropToCircle method is rewritten and I've added an overload that alllows to specify a Pen Color.
The Pen will then be used to draw a border around the cropped elliptical region.

public static Image CropToCircle(Image srcImage, Color backColor)
{
    return CropToCircle(srcImage, backColor, Color.Transparent);
}

public static Image CropToCircle(Image srcImage, Color backColor, Color penColor)
{
    var rect = new Rectangle(0, 0, srcImage.Width, srcImage.Height);
    var cropped = new Bitmap(srcImage.Width, srcImage.Height, PixelFormat.Format32bppArgb);
    using (var tBrush = new TextureBrush(srcImage))
    using (var pen = new Pen(penColor, 2))
    using (var g = Graphics.FromImage(cropped)) {
        g.SmoothingMode = SmoothingMode.AntiAlias;
        if (backColor != Color.Transparent) g.Clear(backColor);
        g.FillEllipse(tBrush, rect);
        if (penColor != Color.Transparent) {
            rect.Inflate(-1, -1);
            g.DrawEllipse(pen, rect);
        }
        return cropped;
    }
}

ResizeImage 方法得到了简化.

The ResizeImage method is simplified.

  • The scale ratio, when used, takes the maximum value of new Size specified and resizes the Image to fit this Size boundaries.
  • The Graphics PixelOffsetMode is set to PixelOffsetMode.Half. The notes here explain why.

public static Image ResizeImage(Image image, Size newSize, bool preserveAspectRatio = true)
{
    float scale = Math.Max(newSize.Width, newSize.Height) / (float)Math.Max(image.Width, image.Height);
    Size imageSize = preserveAspectRatio 
                   ? Size.Round(new SizeF(image.Width * scale, image.Height * scale)) 
                   : newSize;

    var resizedImage = new Bitmap(imageSize.Width, imageSize.Height);
    using (var g = Graphics.FromImage(resizedImage)) {
        g.PixelOffsetMode = PixelOffsetMode.Half;
        g.InterpolationMode = InterpolationMode.HighQualityBicubic;
        g.DrawImage(image, 0, 0, imageSize.Width, imageSize.Height);
    }
    return resizedImage;
}

这篇关于如何裁剪具有平滑边界的图像的椭圆区域的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆