我正在编写一个图像效果库,它使用流畅的表示法公开功能。
一些简单的效果很快(边框、阴影等),但是一些CPU密集型的调用很慢(我在看你)
现在,以blur为例,我有以下方法:
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
这对我来说似乎很乐观。)
我现在可以在3秒钟内处理20x20模糊的800x600图像:)