【问题标题】:How to crop an elliptical region of an Image with smooth borders如何裁剪具有平滑边框的图像的椭圆区域
【发布时间】:2020-08-16 13:26:16
【问题描述】:

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

图片大小为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;
}

【问题讨论】:

    标签: c# image winforms graphics smoothing


    【解决方案1】:

    需要进行一些更改才能使其按预期工作:

    • 不要使用Image.Fromfile():如果出于某种原因必须使用,请始终使用允许保留嵌入颜色管理信息的重载 (string, bool)。既然可以避免,请采用此处显示的方法,使用File.ReadAllBytes()MemoryStream(或using 块中的FileStream)。

    • Graphics.SetClip() 不允许抗锯齿。这也适用于区域(至少无需进一步调整)。在这里,我使用TextureBrush特殊笔刷 从位图(您调整大小的位图)构建,然后用于填充 裁剪 调整大小的椭圆图片。

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

    • 不要使用这种形式的路径:@"..\..\Image.png":当您将可执行文件移动到其他地方时,该路径将不存在(或者它不会不可访问,或者只是错误)。 始终使用Path.Combine()(如此处所示)构建完整路径。
      此处的示例将图像保存在可执行路径中的 CroppedImages 子文件夹中。
      不过,这个主题相当广泛(例如,您可能不允许将数据存储在可执行路径中,因此您可能需要使用 User AppData 文件夹或 ProgramData 目录中的专用路径)。

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

    private void Recorte_Click(object sender, EventArgs e)
    {
        using (var ofd = new OpenFileDialog()) {
            ofd.Filter = "Image Files (*.jpg; *.jpeg; *.gif; *.bmp; *.png)|*.jpg; *.jpeg; *.gif; *.bmp; *.png";
            ofd.RestoreDirectory = true;
            if (ofd.ShowDialog() != DialogResult.OK) return;
    
            Image croppedImage = null;
            using (var sourceImage = Image.FromStream(new MemoryStream(File.ReadAllBytes(ofd.FileName)))) 
            using (var resizedImage = ResizeImage(sourceImage, new Size(100, 300), false)) {
                croppedImage = CropToCircle(resizedImage, Color.Transparent, Color.Turquoise);
                pictureBox1.Image?.Dispose();
                pictureBox1.Image = croppedImage;
                string destinationPath = Path.Combine(Application.StartupPath, @"CroppedImages\Cortada.png");
                croppedImage.Save(destinationPath, ImageFormat.Png);
            }
        }
    }
    

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

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

    ResizeImage 方法已简化。

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

    【讨论】:

      猜你喜欢
      • 2012-06-20
      • 2014-05-01
      • 1970-01-01
      • 2016-01-20
      • 1970-01-01
      • 1970-01-01
      • 2020-09-01
      • 2013-04-04
      相关资源
      最近更新 更多