2011-11-29 33 views
4

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

這裏是我的設置
我已經得到了來自一系列圖像中提取特徵的C#應用​​程序。由於數據集的大小(數千個圖像),它大量並行化,這就是爲什麼我們有一臺運行在Windows7 x64(.NET4運行時)上的ssd的高端機器來解除艱苦的工作。我正在使用Windows Forms在Visual Studio 2008(.NET3.5)下的Windows XP SP3 x86計算機上開發它 - 順便提一下,沒有機會轉移到WPF。

編輯3: 這很奇怪,但我想我終於知道發生了什麼事情。似乎是在兩臺機器上產生不同結果的圖像格式的編解碼器!我不知道到底發生了什麼,但是xp機器上的解碼器產生比win7更好的結果。可悲的是更好版本仍然在x86 XP系統:(我想這個唯一的解決方案是將輸入圖像格式更改爲無損像png或bmp(愚蠢的我沒有考慮文件格式的第一位:))。

編輯2: 謝謝你的努力。我想我會堅持自己實現一個轉換器,這不是我想要的,但我必須以某種方式解決它:)。如果有人正在閱讀對我有一些想法的人,請告訴我。

編輯: 在評論中,我被建議使用第三方庫爲此。我想我沒有把自己弄清楚,因爲我真的不想使用DrawImage方法 - 這只是一個有缺陷的快速獲得實際工作的new Bitmap(tmp, ... myPixelFormat),希望使用一些插值。我想實現的目的僅僅是將輸入的圖像轉換爲具有一些標準插值的普通PixelFormat。

我的問題如下。某些源圖像格式與WinForms映像文件不兼容,格式爲Indexed8bpp jpg。因此,在我的圖像加載邏輯沒有用於將圖像轉換爲我的應用程序默認的格式(例如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; 
} 

我跟蹤這個問題到(*)。我認爲插值模式與它有關,但無論如何,我選擇的結果在(**)上都不相同。我一直在用一些愚蠢的副本&粘貼線來調查測試圖像數據,以確保它不是以錯誤的方式訪問數據的問題。

圖像都在一起看起來像這樣Electron Backscatter Diffraction Pattern。實際的顏色值有細微的差別,但它們帶有大量的信息 - 插值甚至可以增強它。它看起來像x86機器上的組合算法使用InterpolationMode屬性,而x64只是將調色板值分散出去,而沒有考慮任何插值。

我從來沒有注意到兩臺機器的輸出之間有任何差異,直到我對應用程序中的數據實現直方圖視圖功能爲止。在x86機器上,它是平衡的,正如人們期望它可以觀看圖像一樣。另一方面,x64機器寧願給出某種稀疏的條形圖,這是索引圖像數據的指示。它甚至會影響整個應用程序的整體輸出數據 - 兩臺機器上的輸出數據相同,但這不是一件好事。

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

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

哦,看看......鯨魚!

+0

是的,Graphics.DrawImage()需要一些快捷方式,導致像素顏色值的細微變化。太小而不能被人眼察覺。 64位算法將稍微有點不同。處理這個問題的一種可能的方法是聲明x86版本是錯誤的:) –

+0

哇,這是快速的...我明白你的觀點,但它不能解決我的問題,因爲它是x86版本,可以產生「更好」結果:) – mfeineis

+0

我不會建議依賴Graphics DrawImage方法的數字穩定性,因爲它的目的是顯示圖像,而不是保存信息。例如,它的實現有可能在未來發生變化。 –

回答

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做到這一點,但是這將是非常非常慢。

+0

感謝您的建議,我會考慮它。這實際上是我想通過使用框架的內置功能避免的,所以我不必關心這些事情。更何況這個功能是應用程序的頭號瓶頸。 – mfeineis

+0

現在我將堅持實施我自己的轉換器... – mfeineis

1

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

+0

System.Drawing確實基於GDI +,而不是GDI(主要的例外是TextRenderer,這不在此處播放)。所以關於視頻驅動程序的部分可能不相關。 –

+0

刪除懷舊背景。 :P – dthorpe

+0

+1對歷史矯枉過正,我很迷惑:) ...我沒有想到寫C#時低級別的東西......我也使用GDI讀了關於.NET的內容+ – mfeineis

0

您確實應該使用OpenCV進行圖像處理,它在C#中可用:OpenCVSharp

+0

謝謝你的鏈接,我沒有首先不瞭解OpenCV。我會進一步調查:) – mfeineis

+0

我現在回顧了OpenCV,它似乎非常強大。不幸的是,我不是這種功能檢測:)。 LGPL也不是這個項目的選擇,但是非常感謝你指點我這個! – mfeineis