我正在寫一個圖像效果庫,它使用流利的表示法公開功能。優化手動圖像處理(.Net 4)
一些簡單的效果是快速(邊框,陰影等),但一些更多的CPU調用都是慢(模糊,我看着你)現在
,以模糊爲例,我「已經得到了下面的方法:
Public Function Process(ByRef ImageEffect As Interfaces.IImageEffect) As Interfaces.IImageEffect Implements Interfaces.IEffect.Process
Dim Image As Bitmap = CType(ImageEffect.Image, Bitmap)
Dim SourceColors As New List(Of Drawing.Color)
For X = 0 To ImageEffect.Image.Width - 1
For Y = 0 To ImageEffect.Image.Height - 1
SourceColors.Clear()
For ScanX = Math.Max(0, X - Strength) To Math.Min(Image.Width - 1, X + Strength)
For ScanY = Math.Max(0, Y - Strength) To Math.Min(Image.Height - 1, Y + Strength)
SourceColors.Add(Image.GetPixel(ScanX, ScanY))
Next
Next
Dim NewColor = Color.FromArgb(
CInt(SourceColors.Average(Function(Z) Z.A)),
CInt(SourceColors.Average(Function(Z) Z.R)),
CInt(SourceColors.Average(Function(Z) Z.G)),
CInt(SourceColors.Average(Function(Z) Z.B))
)
Image.SetPixel(X, Y, NewColor)
Next
Next
Return ImageEffect
End Function
我知道,我的代碼可以改進(數組不保存顏色列表等),但迄今爲止最CPU密集型的方法調用是Image.GetPixel
- 而我更願意在接觸我的其他代碼之前修復此問題。
目前擊穿是:
- Image.GetPixel:47%
- Image.SetPixel:13%
- LINQ的平均:11%
- 其他:29%
假設模糊強度爲1,例如每個設置像素的讀數爲< = 9像素。
現在用其他語言,我已經從磁盤讀取圖像,並通過執行如下操作跳到適當的像素:(Y*Width+X)*PixelBytes
這已經非常快。 .Net中是否有相同的內容(記住我的圖像可能只在內存中)。 GetPixel
已經這樣做了嗎?如果是這樣,我該如何改進我的方法?
我錯過了一個明顯的技巧來優化這個?
解決方案:
Public Function Process(ByRef ImageEffect As Interfaces.IImageEffect) As Interfaces.IImageEffect Implements Interfaces.IEffect.Process
Dim bmp = DirectCast(ImageEffect.Image, Bitmap)
'' Lock the bitmap's bits.
Dim Dimensions As New Rectangle(0, 0, bmp.Width, bmp.Height)
Me.Dimensions = Dimensions
Dim bmpData As System.Drawing.Imaging.BitmapData = bmp.LockBits(Dimensions, Drawing.Imaging.ImageLockMode.ReadWrite, bmp.PixelFormat)
'' Get the address of the first line.
Dim ptr As IntPtr = bmpData.Scan0
'' Declare an array to hold the bytes of the bitmap.
'' This code is specific to a bitmap with 24 bits per pixels.
Dim bytes As Integer = Math.Abs(bmpData.Stride) * bmp.Height
Dim ARGBValues(bytes - 1) As Byte
'' Copy the ARGB values into the array.
System.Runtime.InteropServices.Marshal.Copy(ptr, ARGBValues, 0, bytes)
'' Call the function to actually manipulate the data (next code block)
ProcessRaw(bmpData, ARGBValues)
System.Runtime.InteropServices.Marshal.Copy(ARGBValues, 0, ptr, bytes)
bmp.UnlockBits(bmpData)
Return ImageEffect
End Function
而且實際操作圖像(我知道這是冗長,但它的快速)功能:
Protected Overrides Sub ProcessRaw(ByVal BitmapData As System.Drawing.Imaging.BitmapData, ByRef ARGBData() As Byte)
Dim SourceColors As New List(Of Byte())
For Y = 0 To Dimensions.Height - 1
For X = 0 To Dimensions.Width - 1
Dim FinalA = 0.0
Dim FinalR = 0.0
Dim FinalG = 0.0
Dim FinalB = 0.0
SourceColors.Clear()
Dim SamplesCount =
(Math.Min(Dimensions.Height - 1, Y + Strength) - Math.Max(0, Y - Strength) + 1) *
(Math.Min(Dimensions.Width - 1, X + Strength) - Math.Max(0, X - Strength) + 1)
For ScanY = Math.Max(0, Y - Strength) To Math.Min(Dimensions.Height - 1, Y + Strength)
For ScanX = Math.Max(0, X - Strength) To Math.Min(Dimensions.Width - 1, X + Strength)
Dim StartPos = CalculatePixelPosition(ScanX, ScanY)
FinalB += ARGBData(StartPos + 0)/SamplesCount
FinalG += ARGBData(StartPos + 1)/SamplesCount
FinalR += ARGBData(StartPos + 2)/SamplesCount
FinalA += ARGBData(StartPos + 3)/SamplesCount
Next
Next
Dim OutputPos = CalculatePixelPosition(X, Y)
ARGBData(OutputPos + 0) = CByte(CInt(FinalB))
ARGBData(OutputPos + 1) = CByte(CInt(FinalG))
ARGBData(OutputPos + 2) = CByte(CInt(FinalR))
ARGBData(OutputPos + 3) = CByte(CInt(FinalA))
Next
Next
End Sub
性能的提高是巨大的 - 至少30快40倍。最CPU密集現在的口號是計算陣列中的位置進行修改:
Protected Function CalculatePixelPosition(ByVal X As Integer, ByVal Y As Integer) As Integer
Return ((Dimensions.Width * Y) + X) * 4
End Function
這似乎相當優化,我:)
我現在可以處理20×20模糊下3秒一個800x600圖像:)
謝謝。我不確定如何從System.Drawing.Image獲取一個字節數組 - 另外,你會推薦什麼:池/「緩存」/?正如你所看到的,我重新使用相同的List來避免在內存中創建數千個。你認爲對GC.Collect()的先發制人的調用在這種方法的結尾可能是有益的嗎? – Basic 2010-11-08 12:04:26
添加了一些關於複製的內容。我不認爲在這裏使用GC.Collect是個好主意。爲了組合,您需要一些全局池,它將WeakReference保存到您不再需要的像素中,並在需要時重用它們。 – CodesInChaos 2010-11-08 12:27:07
本文介紹如何使用LockBits和圖形調用將8bpp圖像轉換爲1bpp。源代碼很容易遵循。 http://www.codeproject.com/KB/GDI-plus/BitonalImageConverter.aspx – 2010-11-08 12:51:18