【问题标题】:Image is not drawn at the correct spot图像未在正确的位置绘制
【发布时间】:2018-07-21 06:45:19
【问题描述】:
Bitmap image = ReadBitmap("image.png");
Bitmap imageCopy = new Bitmap(image);
Bitmap canvas = new Bitmap(imageCopy.Width+100, imageCopy.Height);

// From this bitmap, the graphics can be obtained, because it has the right PixelFormat
using(Graphics g = Graphics.FromImage(canvas))
{
    // Draw the original bitmap onto the graphics of the new bitmap
    g.DrawImage(image, 0, 0);
}

// Use tempBitmap as you would have used originalBmp
InputPictureBox.Image = image;
OutputPictureBox.Image = canvas;   

我没看懂这段c#代码的输出。

原始图像未放置在正确的位置。它应该在(0, 0)。 另外,我需要黑色背景。

那么,发生了什么以及如何纠正这个问题?

【问题讨论】:

  • 此代码将源图像复制到新的图像容器中。目标容器的大小在其 X 维度上增加 100 像素。然后将源图像复制到新容器中。由于只指定了原点,源图像被扭曲以适合它的容器。源图像保持不变。两个图像都传递给 PictureBoxes。图像在控件上的呈现方式取决于控件的 SizeMode(缩放、拉伸、自动调整大小...)。 我需要黑色背景:在图像副本上?如果是这样,您在绘制时已经完全填满了画布)。
  • 此外,没有任何图像被释放,源图像将被 GDI+ 锁定(在图像(及其流)为Disposed() 之前,您无法触摸基础文件。您应该解释预期的结果是什么。是否可以在图像的左右两侧创建一个更大的 Canvas,作为图像的黑框?
  • @Jimi 由于只指定了原点,源图像被扭曲以适应它的容器。不。图像是用它的原件绘制的> 尺寸。如果他指定了宽度和高度 g.DrawImage(image, 0, 0, canvas.Width, canvas.Height)
  • 您的图片框中有 zoom。将其更改为正常
  • @γηράσκω δ' αεί πολλά διδασκόμε 是的,也许 distorted 不是首选词。放大(有点失真)。但也应该被裁剪(Y 维度)。

标签: c# .net winforms image-processing gdi+


【解决方案1】:

您正在加载图像,然后使用以下方式创建此源的副本:
Bitmap bitmap = new Bitmap();

当您以这种方式创建图像副本时,您会牺牲/更改一些细节:
Dpi Resolution:如果没有另外指定,分辨率将设置为 UI 分辨率。 96 Dpi,作为标准;不同的屏幕分辨率和缩放比例可能会有所不同。使用的系统也会影响此值(Windows 7 和 Windows 10 可能/可能会提供不同的值)
PixelFormat:如果不是直接从图像源复制或明确指定,@ 987654332@ 设置为PixelFormat.Format32bppArgb

从你所说的来看,你可能想要这样的东西:

using (Bitmap imageSource = (Bitmap)Image.FromFile(@"[SomeImageOfLena]"))
using (Bitmap imageCopy = new Bitmap(imageSource.Width + 100, imageSource.Height, imageSource.PixelFormat))
{
    imageCopy.SetResolution(imageSource.HorizontalResolution, imageSource.VerticalResolution);
    using (Graphics g = Graphics.FromImage(imageCopy))
    {
        g.Clear(Color.Black);
        g.CompositingMode = CompositingMode.SourceCopy;
        g.InterpolationMode = InterpolationMode.HighQualityBicubic;
        g.DrawImage(imageSource, (imageCopy.Width - imageSource.Width) / 2, 0);
        pictureBox1.Image = (Image)imageSource.Clone();
        pictureBox2.Image = (Image)imageCopy.Clone();
    }
}

结果如下:
(上/下框黑色其实就是Picturebox背景色)


当原始图像 Dpi 分辨率与使用 new Bitmap() 创建图像副本时使用的基本 Dpi 分辨率不同时,您的结果可能与预期不同。

这是在相同场景中使用 150、96 和 72 Dpi 的源图像时发生的情况:


另一个重要的细节是 Image 对象的IDisposable 性质。
当你创建一个时,你必须Dispose()它;显式调用Dispose 方法,或隐式调用Using statement 中的Image 构造函数。

另外,可能不要分配直接从FileStream 加载的Image 对象。
GDI+ 将锁定文件,您将无法复制、移动或删除它。
使用该文件,与图像相关的所有资源也将被锁定。

使用new Bitmap()(如果您不关心上述详细信息)或Image.Clone() 进行复制,这将保留图像Dpi ResolutionPixelFormat

【讨论】:

    【解决方案2】:

    我并不完全清楚您实际需要做什么。但无论如何,这是一个 WPF 友好的示例,说明如何在另一个图像内的特定位置绘制图像。

    请注意,如果您只想以不同的尺寸显示图像和/或在其周围添加黑色边框,则有更简单的方法可以做到这一点,而无需创建第二张图像,例如只是布局面板内的图像已经具有您想要的边框样式。

    请注意,我正在使用 System.Windows.Media 命名空间中的类,因为这是 WPF 使用的。这些不能与 System.Drawing 命名空间中的旧类轻松混合(一些类名冲突,并且 Microsoft 的 .Net 框架缺少用于在这些类型之间转换对象的内置方法),因此通常需要简单地决定是否使用一组或另一组绘图工具。我假设您一直在尝试使用 System.Drawing。每个都有自己的优点和缺点,在这里解释太长了。

    // using System.Windows.Media;
    // using System.Windows.Media.Imaging;
    private void DrawTwoImages()
    {
        // For InputPictureBox
        var file = new Uri("C:\\image.png");
        var inputImage = new BitmapImage(file);
            // If your image is stored in a Resource Dictionary, instead use:
            //     var inputImage = (BitmapImage) Resources["image.png"];
        InputPicture.Source = inputImage;
    
        // imageCopy isn't actually needed for this example.
        // But since you had it in yours, here is how it's done, anyway.
        var imageCopy = inputImage.Clone();
    
        // Parameters for setting up our output picture
        int leftMargin   = 50;
        int topMargin    = 5;
        int rightMargin  = 50;
        int bottomMargin = 5;
        int width  = inputImage.PixelWidth  + leftMargin + rightMargin;
        int height = inputImage.PixelHeight + topMargin  + bottomMargin;
        var backgroundColor = Brushes.Black;
        var borderColor = (Pen) null; 
    
        // Use a DrawingVisual and DrawingContext for drawing
        DrawingVisual dv = new DrawingVisual();
        using (DrawingContext dc = dv.RenderOpen())
        {
            // Draw the black background
            dc.DrawRectangle(backgroundColor, borderColor, new Rect(0, 0, width, height));
    
            // Copy input image onto output image at desired position
            dc.DrawImage(inputImage, new Rect(leftMargin, topMargin,
                         inputImage.PixelWidth, inputImage.PixelHeight));
        }
    
        // For displaying output image
        var rtb = new RenderTargetBitmap( width, height, 96, 96, PixelFormats.Pbgra32 );
        rtb.Render(dv);
        OutputPicture.Source = rtb;
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2018-02-15
      • 1970-01-01
      • 1970-01-01
      • 2018-04-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多