2014-02-27 40 views
3

我的應用程序的這部分顯示來自IP攝像機的圖片框中的實時圖像流。該流是一種典型的運動JPEG,格式爲多部分HTTP響應。重新使用位圖解碼JPEG數據

所有的代碼都被寫入,我的應用程序可以正常工作,CPU佔用率非常低(1-2%),並且內存佔用空間很大(逐漸增加,直到GC被觸發,然後全部回收回正常)。所以這是更多的優化問題,最好的實踐等,這不是必需的。

現在,我正在以正常的Bitmap.FromStream()方式執行此操作,它會以60Hz爲每個幀生成一個全新的位圖。即使從運行時的角度來看它也沒問題,但作爲一名程序員,我感到很震驚。

如果我使用更多的手動方法,我會預先分配已知大小(320x240或640x480,取決於選項)的位圖,並將流解碼爲位圖,但我沒有看到a。 NET功能來做到這一點。我不得不使用我自己的JPEG解碼器,這是太多的工作,更多的二進制大小(這個東西已經是55MB的編譯代碼)。

所以我的問題是,我在這裏錯過了什麼?有沒有最好的方法來做這樣的事情?我提到的功能是完美的,但如果不可用,我會如何改進?

+1

你在每個位圖對象上調用dispose()嗎?它將有助於內存管理,因爲Bitmap使用託管的非託管內存。 –

+0

與我的問題無關,但沒有。自己處理它會把它帶到我的UI線程上,而不是讓GC在它自己的終結器線程上執行它。它沒有真正的收益,GC處理30-40mb英寸的位圖。 – Blindy

回答

3

請記住,從60 fps jpegs獲得2%的cpu負載是一個很好的結果。你可以做的很少,以使這種效果更好。確保你的基準是現實的,當它變得很低時,你不能對位圖數據做很多事情。潛在的陷阱是編解碼器懶散地轉換壓縮的像素數據。如果你從來沒有真正說過,調用Graphics.DrawImage()或Bitmap.LockBits(),那麼編解碼器不會做更多的事情,但解析標題。你得到的.NET對象也是非常適合,只需要20個字節的GC堆。

沒有理由擔心底層的內存管理。它在GDI +中非常不透明,它完全照顧它本身而不會暴露任何東西而不是指針。 Image.FromFile()遠遠優越,GDI +使用內存映射文件來訪問原始像素數據。當然,在攝像頭應用程序中沒有用處,因爲GC和本機內存之間的阻抗不匹配,Image.FromStream()更爲複雜,因此有一個Marshal.Copy()用於剷除字節。編解碼器可能會分配緩衝區/緩存解碼像素,您無法控制這一點。沒有理由擔心,因爲你重複使用完全相同的分配,Windows內存管理器很喜歡這一點。

你在問什麼可能。兩種基本的方法來做到這一點。首先是需要一個IntPtr的Bitmap constructor,它需要指向已解碼的像素數據。這可讓您重新使用原始像素緩衝區。第二種方法是Bitmap.LockBits(),你可以直接寫入GDI +分配的像素緩衝區。兩者之間沒有根本的區別。

當然你需要自帶JPEG解碼器。 libjpeg library是最常用的一個。用C編寫的,最好的方法是使用C++/CLI包裝器。 libjpeg-turbo庫值得注意,其解碼速度提高了2-4倍。沒有真正的想法,這些庫如何與微軟的編解碼器進行比較。注意你解碼的像素格式,24bppRgb是一種自然的JPEG匹配,很可能你會得到一個編解碼器,但如果你顯示圖像,32bppPArgb是優質格式(x10)。

+0

這基本上就是我說的,我只是在GDI +本身尋找一些東西,以避免重新發明另一個庫進行解碼。編解碼器本身是可以互換的(因此保存JPEG的10行代碼),你會認爲我可以告訴它只是在我已經創建的位圖對象中解碼。 – Blindy

+0

嗯,不,你不能使用像「位圖對象」這樣的詞,如果你想把它帶到一個好的結局。我鏈接到的位圖構造函數需要*指針*,而不是對象。沒有什麼魔法咒語可以用來讓GDI +包裝的編解碼器做別的事情,而不是你已經知道的。重新使用MemoryStream當然是微不足道的。 –

+0

你很迂腐,但是好吧,它爲我解碼的每一個流分配一個位圖形的內存塊(大小爲width * height * bpp + some stride +一個類頭)。光線與否,當我每秒達到30 jpegs並且它們都是相同的形狀時,這是無用的步驟。 – Blindy

0

首先,你可以嘗試使用WPF,它的圖像組件&渲染能力。 AFAIK,有幾處提到WPF Image類比WinForm的基於GDI +的類具有更好的性能。

,如果你仍然想使用的WinForm作爲UI,如果你允許包括參考Systems.Windows.Media.Imaging(PresentationCore.dll中),那麼你可以嘗試使用JpegBitmapDecoder類解碼圖像,然後將解碼像素設置爲您的預分配Bitmap實例。

上有JpegBitmapDecoder構造幾個選項,你可以調整的(可能)更好的性能,即創建選項和緩存選項 - >http://msdn.microsoft.com/en-us/library/ms602494.aspx

然後拿到BitmapFrame實例作爲

BitmapFrame bitmapFrame = jpegBitmapDecoder.Frames[0]; 

BitmapFrame對於CopyPixels method有幾個重載。該方法可以複製到Array或IntPtr。我相信除了我之外的其他答案已經提到IntPtr作爲位圖中的原始像素緩衝區,因此您可以將BitmapFrame中的像素直接複製到預分配的位圖中。

+0

我也可以用GDI +做到這一點。基本上'JpegBitmapDecoder'爲每個圖像流*分配一個完整的圖像*,與GDI +'Bitmap'構造函數一樣,這是我想要避免的步驟。當然,我知道我可以在另一個位圖上繪製一個位圖。 – Blindy

+0

嗯,只要澄清一下,你只需要創建/分配一個Bitmap實例,然後你可以用最新的圖片「更新」它的內容,這樣實例就是可重複使用,但不管t他選擇了API /類,我認爲您應該嘗試使用分析器來更好地說明應用程序中的瓶頸(最耗費資源)部分。 –

+0

注意到,但沒用的建議。它由我原來的帖子覆蓋! – Blindy