【问题标题】:Replace Bitmap.GetPixel usage in algorithm for LockBits替换 LockBits 算法中的 Bitmap.GetPixel 用法
【发布时间】:2017-04-07 02:17:14
【问题描述】:

我有这个从位图创建区域的函数:

Public Shared Function GetRegion(ByVal sender As Bitmap, ByVal transperancyKey As Color, ByVal tolerance As Integer) As Region

    ' Stores all the rectangles for the region.
    Using path As New GraphicsPath()

        ' Scan the image
        For x As Integer = 0 To (sender.Width - 1)
            For y As Integer = 0 To (sender.Height - 1)
                If Not ColorsMatch(sender.GetPixel(x, y), transperancyKey, tolerance) Then
                    path.AddRectangle(New Rectangle(x, y, 1, 1))
                End If
            Next
        Next

        Return New Region(path)
    End Using

End Function

Public Shared Function ColorsMatch(ByVal color1 As Color, ByVal color2 As Color, ByVal tolerance As Integer) As Boolean
    If (tolerance < 0) Then
        tolerance = 0
    End If

    Return Math.Abs(color1.R - color2.R) <= tolerance AndAlso
           Math.Abs(color1.G - color2.G) <= tolerance AndAlso
           Math.Abs(color1.B - color2.B) <= tolerance
End Function

我想使用Bitmap.LockBits 而不是Bitmap.GetPixel 来提高性能。

目前我所拥有的是下面这个不完整的代码。尝试以与原始函数相同(有效)的方式迭代像素/颜色,我有点困惑。

Public Shared Function GetRegion(ByVal sender As Bitmap, ByVal transperancyKey As Color, ByVal tolerance As Integer) As Region

    ' Stores all the rectangles for the region.
    Using path As New GraphicsPath()

        ' Lock the bitmap's bits.  
        Dim rect As New Rectangle(0, 0, sender.Width, sender.Height)
        Dim bmpData As BitmapData = sender.LockBits(rect, ImageLockMode.ReadWrite, sender.PixelFormat)

        ' Get the address of the first line.
        Dim ptr As IntPtr = bmpData.Scan0

        ' Declare an array to hold the bytes of the bitmap.
        ' Assume PixelFormat.Format32bppArgb (4 bytes per pixel)
        Dim bytes As Integer = Math.Abs(bmpData.Stride) * sender.Height
        ' Note that I'm not sure whether the above is the proper calculation for that assumption.
        Dim rgbValues(bytes - 1) As Byte

        ' Copy the RGB values into the array.
        Marshal.Copy(ptr, rgbValues, 0, bytes)

        ' Scan the image
        For x As Integer = 0 To (sender.Width - 1)
            For y As Integer = 0 To (sender.Height - 1)
                ' ...
            Next
        Next

        ' Unlock the bits.
        bmp.UnlockBits(bmpData)

        Return New Region(path)
    End Using

End Function

我该怎么做?

【问题讨论】:

  • 您是否正在寻找简化的GetPixelSetPixel 使用LockBits,如this post

标签: .net vb.net image-processing bitmap pixel


【解决方案1】:

这样的事情应该可以解决问题。我一次只锁定一个像素行,以避免使用大量内存和大位图,这使得逻辑易于遵循。

Public Shared Function GetRegion(ByVal bm As Bitmap, ByVal transperancyKey As Color, ByVal tolerance As Integer) As Region
    Using path As New GraphicsPath()
        Dim rect As New Rectangle(0, 0, bm.Width, 1)    ' one row high
        Dim pixelData As Int32() = New Int32(0 To bm.Width - 1) {}
        For row As Integer = 0 To (bm.Height - 1)
            ' get the bitmap data as 32bppArgb so that we can read Int32 values
            ' this does not need to match the bm.PixelFormat
            Dim bmData As BitmapData = bm.LockBits(rect, ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb)
            ' copy the pixel data for this row
            Marshal.Copy(bmData.Scan0, pixelData, 0, pixelData.Length)
            bm.UnlockBits(bmData)
            For col As Integer = 0 To (bm.Width - 1)
                Dim px As Color = Color.FromArgb(pixelData(col))
                ' do something with this pixel color
            Next col
            rect.Offset(0, 1)   ' offset to select next row
        Next row

        Return New Region(path)
    End Using
End Function

【讨论】:

  • 我在 bm.LockBits() 调用中收到一个 ArgumentException 消息信息:参数无效乙>。这是我正在测试的图像:i.imgur.com/Ng1QXLq.png
  • @ElektroStudios,当我重写您的原始代码时,我错过了翻转 For-Next 块中的 Width/Height 属性。这就是我没有对其进行简单测试而得到的结果。
  • 感谢您更新代码。我认为代码中仍然存在问题,或者可能是我和我试图填充/调整代码中缺失部分的错误方式,我应该使用 GraphicsPath.AddRectangle() 添加一个矩形b> 方法,就像我在问题中发布的代码中所做的那样。我尝试了几种方法,但也许我错过了一些东西。请你在你的代码中澄清这相当于 X,Y 坐标。像我在我的问题代码中那样添加矩形? (类似于 path.AddRectangle(New Rectangle(rect.X * row, rect.Y, 1, 1)) 这不起作用)
  • @ElektroStudios,col 对应于x,行对应于“y”。原点在左上角,y 从上到下增加,x 从左到右增加。我可能应该解释说我切换了循环顺序以适应一次提取一行数据。如果您需要按原始顺序处理值,则此解决方案无效。如果您需要,整个位图可以很容易地读入一个数组并由 pixel(x,y) 访问。如果您需要这种类型的解决方案,请告诉我;我有一个为此定义的类。
  • 在整个过程中锁定整个位图一次可能更有效,但一次只能复制一行。您可以使用new IntPtr((bmData.Scan0 + row * bmData.Stride) 在每一行获取Marshal.Copy 的起始指针。
猜你喜欢
  • 2016-02-07
  • 1970-01-01
  • 2012-12-16
  • 1970-01-01
  • 2019-05-05
  • 2023-04-05
  • 1970-01-01
  • 2010-09-23
  • 2013-05-17
相关资源
最近更新 更多