Как обрезать эллиптическую область изображения с гладкими границами - PullRequest
1 голос
/ 02 мая 2020

Мой код открывает изображение, изменяет его размер, затем обрезает круговую область.
Мне нужны более плавные границы, поскольку на обрезанном изображении отображаются неровные, не сглаженные края.

the image i want the image i has getting

Размер изображения 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;
}

1 Ответ

2 голосов
/ 02 мая 2020

Требуется несколько изменений, чтобы это работало должным образом:

  • Не используйте Image.Fromfile(): если вам по какой-то причине необходимо всегда использовать перегрузку (string, bool), что позволяет сохранить встроенную информацию управления цветом. Поскольку вы можете избежать этого, примите метод, показанный здесь, используя File.ReadAllBytes() и MemoryStream (или FileStream в блоке using).

  • Graphics.SetClip () не разрешает сглаживание. Это также относится к регионам (по крайней мере, без дополнительных корректировок). Здесь я использую TextureBru sh, специальную сборку bru sh из растрового изображения (вашего измененного растрового растрового изображения), которое затем используется для заполнения эллипса, который кроп изображение с измененным размером.

  • Утилизация создаваемых вами одноразовых объектов очень важна, особенно когда вы имеете дело с графическими объектами. Конечно, вам нужно избавиться от всех одноразовых объектов, которые вы создаете (объекты, которые предоставляют метод Dispose()). Это включает в себя объект OpenFileDialog.

  • Не используйте пути в этой форме: @"..\..\Image.png": этот путь не будет существовать (или он не будет недоступен, или просто неправильно ), когда Вы перемещаете свой исполняемый файл куда-то еще.
    Всегда используйте Path.Combine () (как показано здесь), чтобы построить полный путь.
    В этом примере изображение сохраняется внутри CroppedImages подпапка в пути к исполняемому файлу.
    Эта топи c довольно широка, хотя (например, вам может быть запрещено хранить данные в пути к исполняемому файлу, поэтому вам может потребоваться использовать выделенный путь в Пользовательская папка AppData или каталог ProgramData).

  • Все расчеты должны быть пересмотрены , посмотрите, что я разместил здесь.

Cropped Rounded Image 1 Cropped Rounded Image 2 Cropped Rounded Image 4 Cropped Rounded Image 3

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 переписан, и я добавил перегрузку, позволяющую указать цвет пера.
Затем перо будет использоваться для рисования границы вокруг обрезанная эллиптическая область.

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 упрощен.

  • Коэффициент масштабирования, когда он используется, принимает максимальное значение нового указанного размера и изменяет размер изображения в соответствии с границами этого размера.
  • Графика PixelOffsetMode установлена ​​на PixelOffsetMode.Half. Примечания здесь объясняют, почему .

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