Graphics.DrawImage在x86和x64上创建不同的图像数据

时间:2011-11-29 23:09:12

标签: c# 32bit-64bit system.graphics

嘿那里!

这是我的设置
我有一个c#应用程序,它从一系列图像中提取特征。由于数据集的大小(数千个图像),它被大量并行化,这就是为什么我们拥有一台运行在Windows7 x64(.NET4运行时)上的ssd的高端机器,以解除繁重的工作。我在带有Windows窗体的Visual Studio 2008(.NET3.5)下的Windows XP SP3 x86机器上开发它 - 顺便说一句,没有机会转向WPF。

EDIT3: 这很奇怪,但我想我终于找到了正在发生的事情。似乎是图像格式的编解码器,在两台机器上产生不同的结果!我不确切知道那里发生了什么,但xp机器上的解码器产生比win7更健全的结果。遗憾的是更好的版本仍然在x86 XP系统中:(。我想这个问题的唯一解决方案是将输入图像格式改为像png或bmp一样无损的东西(愚蠢的我不考虑文件格式首先:))。

EDIT2: 感谢你付出的努力。我想我会坚持自己实现一个转换器,它不是我想要的,但我必须以某种方式解决它:)。如果有人正在阅读这个有我想法的人,请告诉我。

修改 在评论中,我建议使用第三方库。我认为我并没有让自己清楚,因为我并不是真的想要使用DrawImage方法 - 它只是一个有缺陷的快速方法来获得实际工作new Bitmap(tmp, ... myPixelFormat)希望使用一些插值。我想要实现的仅仅是通过一些标准插值将输入图像转换为普通的PixelFormat。

我的问题如下。一些源图像采用 Indexed8bpp jpg 格式,与WinForms成像内容不能很好地相处。因此,在我的图像加载逻辑中,检查索引图像,将图像转换为我的应用程序默认格式(例如Format16bpp),如下所示:

Image GetImageByPath(string path)
{
    Image result = null;

    using (FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read))
    {
        Image tmp = Image.FromStream(fs); // Here goes the same image ...

        if (tmp.PixelFormat == PixelFormat.Format1bppIndexed ||
            tmp.PixelFormat == PixelFormat.Format4bppIndexed ||
            tmp.PixelFormat == PixelFormat.Format8bppIndexed ||
            tmp.PixelFormat == PixelFormat.Indexed)
        {
            // Creating a Bitmap container in the application's default format
            result = new Bitmap(tmp.Width, tmp.Height, DefConf.DefaultPixelFormat);
            Graphics g = Graphics.FromImage(result);
            g.InterpolationMode = InterpolationMode.HighQualityBicubic;

            // We need not to scale anything in here
            Rectangle drawRect = new Rectangle(0, 0, tmp.Width, tmp.Height);

            // (*) Here is where the strange thing happens - I know I could use
            // DrawImageUnscaled - that isn't working either
            g.DrawImage(tmp, drawRect, drawRect, GraphicsUnit.Pixel);

            g.Dispose();
        }
        else 
        {
            result = new Bitmap(tmp); // Just copying the input stream
        }

        tmp.Dispose();
    }

    // (**) At this stage the x86 XP memory image differs from the 
    // the x64 Win7 image despite having the same settings
    // on the very same image o.O
    result.GetPixel(0, 0).B; // x86: 102, x64: 102
    result.GetPixel(1, 0).B; // x86: 104, x64: 102
    result.GetPixel(2, 0).B; // x86:  83, x64:  85
    result.GetPixel(3, 0).B; // x86: 117, x64: 121
    ...
    return result;
}

我将问题追溯到(*)。我认为InterpolationMode与它有关,但是我选择的哪一个结果在两个系统的(**)上都是不同的。我一直在用一些愚蠢的复制和粘贴行调查测试图像数据,以确保它不会以错误的方式访问数据。

所有图像都像Electron Backscatter Diffraction Pattern一样。实际的颜色值略有不同,但它们带有大量信息 - 插值甚至可以增强它。看起来x86机器上的合成算法使用InterpolationMode属性,而x64 thingy只是将调色板值扩展出来而不考虑任何插值。

直到我在应用程序中的数据上实现直方图视图功能的那一天,我才注意到两台机器的输出之间没有任何区别。在x86机器上,它是平衡的,因为人们期望它能够观看图像。另一方面,x64机器宁愿提供某种稀疏条形图,这是索引图像数据的指示。它甚至会影响整个应用程序的整体输出数据 - 两台机器上的输出在数据相同时都不同,这不是一件好事。

对我而言,它看起来像x64实现中的一个错误,但那只是我:-)。我只是希望x64机器上的图像具有与x86相同的值。

如果有人有想法,我会非常高兴。我已经在网上寻找类似的行为多年,但阻力似乎是徒劳的:)

哦,请注意......鲸鱼!

4 个答案:

答案 0 :(得分:1)

如果您想确保始终以相同的方式完成此操作,则必须编写自己的代码来处理它。幸运的是,这并不太难。

您的8bpp图像有一个包含实际颜色值的调色板。您需要读取该调色板并将颜色值(如果我没记错,是24位)转换为16位颜色值。您将在转换中丢失信息,但您的转化信息已经丢失。至少这样,你会以可预测的方式丢失信息。

将转换后的颜色值(不会超过256个)放入可用于查找的数组中。然后......

创建目标位图并调用LockBits以获取指向实际位图数据的指针。调用LockBits以获取指向源位图的位图数据的指针。然后,对于每个像素:

read the source bitmap pixel (8 bytes)
get the color value (16 bits) from your converted color array
store the color value in the destination bitmap

您可以使用GetPixelSetPixel执行此操作,但速度非常慢。

答案 1 :(得分:1)

我模糊地回想起.NET图形类依赖于GDI +。如果今天仍然如此,那么在使用不同视频驱动程序的不同64位系统上尝试您的应用程序毫无意义。您最好的选择是使用原始GDI操作(P / Invoke)进行插值,或者在软件中编写自己的像素插值例程。这两种选择都不是特别有吸引力。

答案 2 :(得分:0)

你真的应该使用OpenCV进行图像处理,它可以在C#中找到:OpenCVSharp

答案 3 :(得分:0)

我对图形对象使用标准方法,并且此设置的性能优于X86。在发布运行时计算性能,而不是调试。还要在项目属性的“构建”选项卡上检查优化代码。 Studio 2017,框架4.7.1

public static Graphics CreateGraphics(Image i)
{
    Graphics g = Graphics.FromImage(i);
    g.CompositingMode = CompositingMode.SourceOver;
    g.CompositingQuality = CompositingQuality.HighSpeed;
    g.InterpolationMode = InterpolationMode.NearestNeighbor;
    g.SmoothingMode = SmoothingMode.HighSpeed;
    return g;
}