簡體   English   中英

Graphics.DrawImage 在 x86 和 x64 上創建不同的圖像數據

[英]Graphics.DrawImage creates different image data on x86 and x64

嘿!

這是我的設置
我有一個從一系列圖像中提取特征的 ac# 應用程序。 由於數據集(數千張圖像)的大小,它被高度並行化,這就是為什么我們有一台在 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 與它有關,但是我選擇其中的哪一個沒有區別,無論如何,兩個系統上(**)處的結果是不同的。 我一直在用一些愚蠢的復制和粘貼行調查測試圖像數據,以確保以錯誤的方式訪問數據不是問題。

這些圖像一起看起來像這個 電子背散射衍射圖案 實際顏色值略有不同,但它們攜帶大量信息 - 插值甚至增強了它。 看起來 x86 機器上的合成算法使用 InterpolationMode 屬性,而 x64 thingy 只是將調色板值展開而不考慮任何插值。

直到我對應用程序中的數據實施直方圖視圖功能的那一天,我才注意到兩台機器的輸出之間有任何差異。 在 x86 機器上,它是平衡的,正如人們從觀看圖像時所期望的那樣。 另一方面,x64 機器寧願提供某種稀疏條形圖,表示索引圖像數據。 它甚至會影響整個應用程序的整體輸出數據 - 兩台機器上的輸出不同,數據相同,這不是一件好事。

對我來說,它看起來像是 x64 實現中的一個錯誤,但這只是我 :-)。 我只希望 x64 機器上的圖像與 x86 機器上的圖像具有相同的值。

如果有人有想法,我會很高興。 我一直在網上尋找類似的行為多年,但抵抗似乎是徒勞的:)

哦,當心……一條鯨魚!

如果您想確保始終以相同的方式執行此操作,則必須編寫自己的代碼來處理它。 幸運的是,這並不太難。

您的 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來做到這一點,但它會非常非常慢。

我依稀似乎記得 .NET 圖形類依賴於 GDI+。 如果今天仍然如此,那么在具有不同視頻驅動程序的不同 64 位系統上嘗試您的應用程序沒有意義。 您最好的選擇是使用原始 GDI 操作 (P/Invoke) 進行插值或在軟件中編寫您自己的像素插值例程。 這兩種選擇都不是特別有吸引力。

我使用圖形對象的標准方法,並且此設置優於 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;
}

你真的應該使用OpenCV來處理這樣的圖像,它在 C# 中可用: OpenCVSharp

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM