保存图像时,在大位图上绘制的线条不可见 [英] Lines drawn on a large Bitmap are not visible when the image is saved

查看:96
本文介绍了保存图像时,在大位图上绘制的线条不可见的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我创建了一个程序,可以在选定的图像上绘制正方形网格。它适用于分辨率较低的图像,但不适用于较大的图像。



将图像另存为文件时,所有网格线都不可见。

我正在测试的图像的分辨率为 3600x4320 ,并且可以显示在

解决方案

主要问题在以下行中:

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

this.Height 显然不是您要编写的内容,让我们将其替换为Image.Height



grp.DrawLine(Pens.Red,x,y); grp.DrawLine(new Pen(Color .Red,5f),x,y); 将绘制不同大小的线(1和5像素)。在示例代码中,这两种方法接受 Color float 参数来定义Pen的颜色和大小。



grp.SmoothingMode :我们在这里不需要任何平滑模式,不需要绘制直线和它将添加清晰可见的抗锯齿效果,尤其是在将图像保存为JPEG格式时(抗锯齿效果-某种程度上,它实际上会扭曲颜色-这些线条本身)。



►您不会丢弃创建的任何Graphics对象。这对于频繁的图形操作以及使用大型位图时都非常重要。

需要处理Pen和Graphics对象。






由于尚不清楚是否要生成在两个维度上都具有相同行数的网格,因此,最有可能是其单元格的宽度不等于高度的网格,或者是单元格平方的网格(这是示例图像似乎显示的内容,而不是代码),我发布了两种绘制两种网格类型的方法:



第一种方法,宽度和高度都相同的行数:

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

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

var gridSizeX =(float)image.Width /行;
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);
使用(var pen = new Pen(penColor,penSize)){
g.DrawLine(pen,pointX1,pointX2);
g.DrawLine(pen,pointY1,pointY2);
}
}
返回图片;
}
}



第二种方法,绘制一个正方形网格。整数值 gridSection 用于基于位图的最小尺寸来定义网格单元。

此尺寸用于确定如何在其他维度上绘制许多线。



网格大小是根据最小尺寸计算的:

  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;

 私有图像DrawSquaredGrid(int gridSection,字符串imgPath,颜色penColor,浮动penSize)
{
var image = Image.FromStream(new MemoryStream(File.ReadAllBytes(imgPath)),true);
使用(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 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 var pointX1 = new PointF(0,i * gridSize);
var pointX2 = new PointF(image.Width,i * gridSize);
使用(var pen = new Pen(penColor,penSize)){
g.DrawLine(pen,pointX1,pointX2);
}
}
返回图片;
}
}



SaveFileDialog被重构为允许多种图像格式。并基于选择调用一种绘制方法(在示例代码中,使用CheckBox( chkSquared )选择一个网格类型。)

您可以添加更多格式, ImageFormatFromFileName()方法选择 ImageFormat 类型根据 SaveFileDialog.FielName 扩展名。

 私有无效BtnExportClick(对象发送者,EventArgs e)
{
string imagePath = [Some Path] ;

使用(var sfd = new SaveFileDialog()){
sfd.Filter = PNG图片(* .png)| * .png | TIFF图片(* .tif)| *。 tif | JPEG图片(* .jpg)| * .jpg;
sfd.RestoreDirectory = true;
sfd.AddExtension = true;
if(sfd.ShowDialog()== DialogResult.OK){
图片图片= 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);
}


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.

解决方案

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

这篇关于保存图像时,在大位图上绘制的线条不可见的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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