Я пишу библиотеку эффектов изображений, которая предоставляет функциональные возможности с использованием беглой записи.
Некоторые простые эффекты бывают быстрыми (границы, тени и т. Д.), Но некоторые из более интенсивных вызовов ЦП медленные (размытие, я смотрю на вас)
Теперь, взяв размытие в качестве примера, я получил следующий метод:
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
Я знаю, что мой код можно улучшить (массив, а не список для хранения цветов и т. Д.), Но с помощью FAR наиболее ресурсоемким вызовом метода является Image.GetPixel
- и я бы предпочел исправить это раньше касаясь остальной части моего кода.
В настоящее время разбивка:
- Image.GetPixel: 47%
- Image.SetPixel: 13%
- Средний балл: 11%
- Разное: 29%
Предполагается, что сила размытия равна 1, например, чтение <= 9 пикселей для каждого установленного пикселя. </p>
Теперь с другими языками я прочитал изображения с диска и перешел к соответствующему пикселю, выполнив что-то вроде: (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 раз быстрее. Наиболее требовательным к процессору вызовом сейчас является вычисление позиции в массиве для изменения:
Protected Function CalculatePixelPosition(ByVal X As Integer, ByVal Y As Integer) As Integer
Return ((Dimensions.Width * Y) + X) * 4
End Function
Что мне кажется довольно оптимизированным:)
Теперь я могу обработать 20x20 размытия менее чем за 3 секунды для изображения 800x600:)