C# 如何裁剪具有平滑边界的图像的椭圆区域

C# 如何裁剪具有平滑边界的图像的椭圆区域,c#,image,winforms,graphics,smoothing,C#,Image,Winforms,Graphics,Smoothing,我的代码打开一个图像,调整其大小,然后裁剪一个圆形区域。 我想要的是更平滑的边界,因为裁剪后的图像显示粗糙、无抗锯齿的边缘 图像大小为60x60 我曾尝试使用Graphics.SmoothingMode属性,但没有成功 到目前为止,我在我的项目中有: private void Recorte_Click(object sender, EventArgs e) { OpenFileDialog open = new OpenFileDialog(); // Filter for

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

图像大小为
60x60

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

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

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;
}
private void Recorte\u单击(对象发送方,事件参数e)
{
OpenFileDialog open=新建OpenFileDialog();
//图像文件的过滤器
打开.Filter=“图像文件(*.jpg;*.jpeg;*.gif;*.bmp;*.png)|*.jpg;*.jpeg;*.gif;*.bmp;*.png”;
if(open.ShowDialog()==DialogResult.OK)
{
//在图片框中显示图像
点F p=新点F(1,1);
位图组织=新位图(open.FileName);
Image srcImage=Bitmap.FromFile(open.FileName);
//在60x60中调整图像大小
Image resized=ResizeImage(srcImage,新大小(60,60),false);
MemoryStream memStream=新的MemoryStream();
//圆形作物
图像裁剪=CropToCircle(大小调整,颜色.透明);
shopped.Save(@.\..\Cortada.png),System.Drawing.Imaging.ImageFormat.png);
pictureBox1.Image=裁剪;
}
}
公共静态图像CropToCircle(图像、彩色背景)
{
Image dstImage=新位图(srcImage.Width、srcImage.Height、srcImage.PixelFormat);
Graphics g=Graphics.FromImage(dstImage);
使用(笔刷br=新的SolidBrush(背景))
{
g、 FillRectangle(br,0,0,dstImage.Width,dstImage.Height);
}
浮动半径=25;
点F中心=新点(60,60);
GraphicsPath路径=新的GraphicsPath();
路径加法(7,7,半径*2,半径*2);
g、 SetClip(路径);
g、 SmoothingMode=SmoothingMode.AntiAlias;
g、 插值模式=插值模式。高质量双线性;
g、 SmoothingMode=SmoothingMode.AntiAlias;
g、 DrawImage(srcImage,0,0);
返回图像;
}
公共静态图像大小图像(图像图像,大小,布尔保留Aspectratio=true)
{
int newWidth;
int newHeight;
如果(保留Aspectratio)
{
int originalWidth=image.Width;
int originalHeight=image.Height;
浮动百分比宽度=(浮动)size.Width/(浮动)原始宽度;
浮动百分比高度=(浮动)大小.高度/(浮动)原始高度;
浮动百分比=百分比高度<百分比宽度?百分比高度:百分比宽度;
新宽度=(整数)(原始宽度*百分比);
新高度=(整数)(原始高度*百分比);
}
其他的
{
newWidth=大小。宽度;
newHeight=大小。高度;
}
Image newImage=新位图(newWidth、newHeight);
使用(Graphics graphicsHandle=Graphics.FromImage(newImage))
{
graphicsHandle.InterpolationMode=InterpolationMode.HighQualityBicubic;
graphicsHandle.DrawImage(图像,0,0,新宽度,新高度);
}
返回新图像;
}

要使其按预期工作,需要进行很多更改:

  • 不要使用
    Image.Fromfile()
    :如果出于某种原因必须使用,请始终使用重载(
    字符串,bool
    )来保留嵌入的颜色管理信息。由于可以避免这种情况,请采用此处所示的方法,使用
    File.ReadAllBytes()
    MemoryStream
    (或使用
    块在
    中使用
    FileStream

  • 不允许反走样。这也适用于区域(至少无需进一步调整)。这里,我使用的是一个特殊的笔刷,它是从位图(调整大小的位图)构建的,然后用来填充修剪调整大小的图像的椭圆

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

  • 不要使用此表单中的路径:
    @.\..\Image.png“
    :当您将可执行文件移动到其他地方时,此路径将不存在(或无法访问,或根本错误)。
    始终使用(如图所示)构建完整路径。
    这里的示例将图像保存在可执行路径中的一个子文件夹中。
    但是,此主题非常广泛(例如,可能不允许您在可执行路径中存储数据,因此您可能需要在用户
    AppData
    文件夹或
    ProgramData
    目录中使用专用路径)

  • 所有的计算都需要重新审视,看看我在这里发布的内容

croptocycle
方法被重写,我添加了一个重载,用于指定笔的颜色。
然后将使用该笔在裁剪的椭圆区域周围绘制边界

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

public static Image CropToCircle(Image srcImage, Color backColor, Color borderColor)
{
    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(borderColor, 2))
    using (var g = Graphics.FromImage(cropped)) {
        g.SmoothingMode = SmoothingMode.AntiAlias;
        if (backColor != Color.Transparent) g.Clear(backColor);
        g.FillEllipse(tBrush, rect);
        if (borderColor != Color.Transparent) {
            rect.Inflate(-1, -1);
            g.DrawEllipse(pen, rect);
        }
        return cropped;
    }
}
简化了调整图像大小的方法

  • 使用比例时,将采用指定的新大小的最大值,并调整图像大小以适应此大小边界
  • 图形设置为PixelOffsetMode.Half
public static Image CropToCircle(Image srcImage, Color backColor)
{
    return CropToCircle(srcImage, backColor, Color.Transparent);
}

public static Image CropToCircle(Image srcImage, Color backColor, Color borderColor)
{
    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(borderColor, 2))
    using (var g = Graphics.FromImage(cropped)) {
        g.SmoothingMode = SmoothingMode.AntiAlias;
        if (backColor != Color.Transparent) g.Clear(backColor);
        g.FillEllipse(tBrush, rect);
        if (borderColor != Color.Transparent) {
            rect.Inflate(-1, -1);
            g.DrawEllipse(pen, rect);
        }
        return cropped;
    }
}
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;
}