Lines drawn on a large Bitmap are not visible when the image is saved

大兔子大兔子 提交于 2020-06-17 23:03:21

问题


I have created a program to draw square grids on a selected image. It works fine for images that has small resolution, but it doesn't work properly on large images.

The all grid lines are not visible seem when the image is saved as file.
The image I am testing has resolution 3600x4320 and can be shown in the link.

How can I fix this problem?

My code:

Image drawGrid(int n, string imgPath)
{
    Image img = Image.FromFile(imgPath);
    Graphics grp = Graphics.FromImage(img);
    grp.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;

    float m = img.Width * 1f / n;

    for (int i = 1; i < n; i++)
    {
        var x = new PointF(i * m, 0);
        var y = new PointF(i * m, img.Height);
        grp.DrawLine(Pens.Red, x, y);
    }

    for (int i = 1; i <= (int)(this.Height / m); i++)
    {
        var x = new PointF(0, i * m);
        var y = new PointF(img.Width, i * m);
        grp.DrawLine(new Pen(Color.Red, 5f), x, y);
    }
    return img;
}

void BtnExportClick(object sender, EventArgs e)
{
    if(saveFileDialog1.ShowDialog() == DialogResult.OK)
    {
        int n = (int)numericUpDown1.Value;
        drawGrid(n, txtImagePath.Text).Save(saveFileDialog1.FileName, System.Drawing.Imaging.ImageFormat.Jpeg);
        MessageBox.Show("Done");
    }
}

The result image is below (resolution reduced to upload)

The grid lines not shown correctly.


回答1:


The major problem is in this line:

for (int i = 1; i <= (int)(this.Height / m); i++)

this.Height is clearly not what you wanted to write, let's replace it with the Image.Height

grp.DrawLine(Pens.Red, x, y); and grp.DrawLine(new Pen(Color.Red, 5f), x, y); will draw lines of different size (1 and 5 pixels). In the sample code, the two methods accept Color and float arguments that define the Pen color and size.

grp.SmoothingMode: we don't want any smoothing mode here, not needed to draw straight lines and it will add anti-alias which will be clearly visible, especially when saving the Image in JPEG format (it will anti-alias - sort of, it actually mangles the colors - these lines by itself).

► You're not disposing of any of the Graphics object you create. This is quite important with both frequent graphics operations and when working with large Bitmaps.
The Pen and Graphics objects needs to be disposed.


Since it's not exactly clear if you want to generate a grid that has the same number of lines in both dimensions - hence, a grid with Cells in which the Width is not equal to the Height, most probably - or a grid with squared Cells (this is what the sample Image seems to show, not the code), I posted two method that draw both grid types:

First method, same number of lines in both width and height:

var gridSizeX = (float)image.Width / lines;
var gridSizeY = (float)image.Height / lines;

private Image DrawGridLines(int lines, string imgPath, Color penColor, float penSize)
{
    var image = Image.FromStream(new MemoryStream(File.ReadAllBytes(imgPath)), true);
    using (var g = Graphics.FromImage(image)) {
        g.PixelOffsetMode = PixelOffsetMode.Half;

        var gridSizeX = (float)image.Width / lines;
        var gridSizeY = (float)image.Height / lines;

        for (int i = 1; i < lines; i++) {
            var pointX1 = new PointF(0, i * gridSizeY);
            var pointX2 = new PointF(image.Width, i * gridSizeY);
            var pointY1 = new PointF(i * gridSizeX, 0);
            var pointY2 = new PointF(i * gridSizeX, image.Height);
            using (var pen = new Pen(penColor, penSize)) {
                g.DrawLine(pen, pointX1, pointX2);
                g.DrawLine(pen, pointY1, pointY2);
            }
        }
        return image;
    }
}

Second method, drawing a squared grid. The integer value, gridSection, is used to define a grid Cell based on the minimum dimension of the Bitmap.
This dimension is then used to determine how many lines to draw in the other dimension.

The grid size is calculated on the minimum dimension:

var gridSize = (float)Math.Min(image.Width, image.Height) / gridSection; 

And the Cell are determined as a consequence:

var gridStepMin = Math.Min(image.Width, image.Height) / gridSize;
var gridStepMax = Math.Max(image.Width, image.Height) / gridSize;

private Image DrawSquaredGrid(int gridSection, string imgPath, Color penColor, float penSize)
{
    var image = Image.FromStream(new MemoryStream(File.ReadAllBytes(imgPath)), true);
    using (var g = Graphics.FromImage(image)) {
        g.PixelOffsetMode = PixelOffsetMode.Half;

        var gridSize = (float)Math.Min(image.Width, image.Height) / gridSection;
        var gridStepMin = Math.Min(image.Width, image.Height) / gridSize;
        var gridStepMax = Math.Max(image.Width, image.Height) / gridSize;

        for (int i = 1; i < gridStepMin; i++) {
            var pointY1 = new PointF(i * gridSize, 0);
            var pointY2 = new PointF(i * gridSize, image.Height);
            using (var pen = new Pen(penColor, penSize)) {
                g.DrawLine(pen, pointY1, pointY2);
            }
        }

        for (int i = 1; i < gridStepMax; i++) {
            var pointX1 = new PointF(0, i * gridSize);
            var pointX2 = new PointF(image.Width, i * gridSize);
            using (var pen = new Pen(penColor, penSize)) {
                g.DrawLine(pen, pointX1, pointX2);
            }
        }
        return image;
    }
}

The SaveFileDialog is refactored to allow multiple Image formats. and to call one of the drawing methods based on a selection (in the sample code, a CheckBox (chkSquared) is used select one of the Grid types).
You can add more formats, the ImageFormatFromFileName() methods selects the ImageFormat type based on the SaveFileDialog.FielName extension.

private void BtnExportClick(object sender, EventArgs e)
{
    string imagePath = [Some Path];

    using (var sfd = new SaveFileDialog()) {
        sfd.Filter = "PNG Image (*.png)|*.png|TIFF Image (*.tif)|*.tif|JPEG Image (*.jpg)|*.jpg";
        sfd.RestoreDirectory = true;
        sfd.AddExtension = true;
        if (sfd.ShowDialog() == DialogResult.OK) {
            Image image = null;
            if (chkSquared.Checked) {
                image = DrawSquaredGrid((int)numericUpDown1.Value, imagePath, Color.Red, 5.0f);
            }
            else {
                image = DrawGridLines((int)numericUpDown1.Value, imagePath, Color.Red, 5.0f);
            }
            image.Save(sfd.FileName, ImageFormatFromFileName(sfd.FileName));
            MessageBox.Show("Done");
            image.Dispose();
        }
    }
}

private ImageFormat ImageFormatFromFileName(string fileName)
{
    string fileType = Path.GetExtension(fileName).Remove(0, 1);
    if (fileType.Equals("tif")) fileType = "tiff";
    if (fileType.Equals("jpg")) fileType = "jpeg";
    return (ImageFormat)new ImageFormatConverter().ConvertFromString(fileType);
}


来源:https://stackoverflow.com/questions/62203858/lines-drawn-on-a-large-bitmap-are-not-visible-when-the-image-is-saved

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!