vb.net中的图像二值化

时间:2015-07-16 14:09:43

标签: vb.net image gdi+

请查看测试#2的结果。当我在photoshop中稍微调整图像大小时,为什么输出(二进制图像)会歪斜?但是,如果我将相同图像的像素格式转换为Format32bppArgb,那么输出就可以了......

我已将其缩小为 GetPixel()像素方法(请参阅BitmapParser类)。我不明白为什么这种方法为调整大小的图像返回错误的像素颜色......

包含源代码。

测试:1 (有效输出)
来源图片:Test_200x200.jpg
宽度:200
身高:200
像素格式:Format24bppRgb

Source Image Test_200x200.jpg

输出:
Binarized result of Test_200x200.jpg


测试:2 (输出无效)
源图像:Test_203x203.jpg(相同的图像,在Photoshop中稍微调整大小)
宽度: 203
身高: 203
像素格式:Format24bppRgb

Source Image Test_203x203.jpg

输出:
enter image description here

测试:3 (有效输出)
源图像:Test_203x203.jpg(与测试#2相同)
宽度:203
身高:203
像素格式:在内存中转换为Format32bppArgb

Source image Test_203x203.jpg
输出:
Binarized result of Test_203x203.jpg

 Public Class BitmapParser

        Private _bitmap As Bitmap = Nothing
        Private _ptr As IntPtr = IntPtr.Zero
        Private _bitmapData As BitmapData = Nothing
        Public Property Pixels As Byte()
        Public Property PixelFormatSize() As Integer

        Public ReadOnly Property Width As Integer
            Get
                Return _bitmap.Width
            End Get
        End Property

        Public ReadOnly Property Height As Integer
            Get
                Return _bitmap.Height
            End Get
        End Property


        Public Sub New(p_bitmap As Bitmap)
            _bitmap = p_bitmap
        End Sub

        ''' <summary>
        ''' Lock bitmap data
        ''' </summary>
        Public Sub LockBits()
            Dim _pixelCount As Integer = Width * Height
            Dim _rect As New Rectangle(0, 0, Width, Height)

            PixelFormatSize = System.Drawing.Bitmap.GetPixelFormatSize(_bitmap.PixelFormat)

            If PixelFormatSize <> 8 AndAlso PixelFormatSize <> 24 AndAlso PixelFormatSize <> 32 Then
                Throw New ArgumentException("Only 8, 24 and 32 bpp images are supported.")
            End If

            _bitmapData = _bitmap.LockBits(_rect, ImageLockMode.ReadWrite, _bitmap.PixelFormat)

            'pixel byte array 
            Dim _step As Integer = CInt(PixelFormatSize / 8)
            Pixels = New Byte(_pixelCount * _step - 1) {}

            'Create a pointer
            _ptr = _bitmapData.Scan0

            'Copy data from pointer to Pixels() arrary
            Marshal.Copy(_ptr, Pixels, 0, Pixels.Length)
        End Sub

        ''' <summary>
        ''' Unlock bitmap data
        ''' </summary>
        Public Sub UnlockBits()
            ' Copy data from byte array back to pointer
            Marshal.Copy(Pixels, 0, _ptr, Pixels.Length)
            _bitmap.UnlockBits(_bitmapData)
        End Sub

        ''' <summary>
        ''' Get Pixel
        ''' </summary>
        ''' <param name="p_x"></param>
        ''' <param name="p_y"></param>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public Function GetPixel(p_x As Integer, p_y As Integer) As Color
            Dim _color As Color = Color.Empty
            Dim _colorComponentsCnt As Integer = CInt(PixelFormatSize / 8)
            Dim _index As Integer = ((p_y * Width) + p_x) * _colorComponentsCnt

            If _index > Pixels.Length - _colorComponentsCnt Then Throw New IndexOutOfRangeException()

            Select Case PixelFormatSize 'bpp
                Case 32
                    Dim b As Byte = Pixels(_index)
                    Dim g As Byte = Pixels(_index + 1)
                    Dim r As Byte = Pixels(_index + 2)
                    Dim a As Byte = Pixels(_index + 3)
                    _color = Color.FromArgb(a, r, g, b)
                Case 24
                    Dim b As Byte = Pixels(_index)
                    Dim g As Byte = Pixels(_index + 1)
                    Dim r As Byte = Pixels(_index + 2)
                    _color = Color.FromArgb(r, g, b)
                Case 8
                    Dim c As Byte = Pixels(_index)
                    _color = Color.FromArgb(c, c, c)
            End Select

            Return _color
        End Function

        ''' <summary>
        ''' Set Pixel
        ''' </summary>
        ''' <param name="p_x"></param>
        ''' <param name="p_y"></param>
        ''' <param name="p_color"></param>
        ''' <remarks></remarks>
        Public Sub SetPixel(p_x As Integer, p_y As Integer, p_color As Color)
            Dim _colorCompCnt As Integer = CInt(PixelFormatSize / 8)

            Dim i As Integer = ((p_y * Width) + p_x) * _colorCompCnt

            Select Case PixelFormatSize 'bpp
                Case 32
                    Pixels(i) = p_color.B
                    Pixels(i + 1) = p_color.G
                    Pixels(i + 2) = p_color.R
                    Pixels(i + 3) = p_color.A
                Case 24
                    Pixels(i) = p_color.B
                    Pixels(i + 1) = p_color.G
                    Pixels(i + 2) = p_color.R
                Case 8
                    Pixels(i) = p_color.B
            End Select
        End Sub
Public Enum BinaryColor
    <EnumDescription("Black")>
    Black = 0
    <EnumDescription("White")>
    White = 255
End Enum
Public MustInherit Class AbstractBinaryImage
        Implements IDisposable
        Private _width As Integer
        Private _height As Integer

        ''' <summary>
        ''' Left to right, top to bottom
        ''' </summary>
        ''' <remarks></remarks>
        Private _index As Integer
        Private _pixels As Integer()

        Public ReadOnly Property Width As Integer
            Get
                Return _width
            End Get
        End Property

        Public ReadOnly Property Height As Integer
            Get
                Return _height
            End Get
        End Property

        Public ReadOnly Property PixelColor(p_x As Integer, p_y As Integer) As Integer
            Get
                Return PixelColor(p_y * Width + p_x)
            End Get
        End Property

        Public ReadOnly Property PixelColor(p_Index As Integer) As BinaryColor
            Get
                Return CType(Pixel(p_Index), BinaryColor)
            End Get
        End Property

        Public ReadOnly Property Pixel(p_x As Integer, p_y As Integer) As Integer
            Get
                Return Pixel(p_y * Width + p_x)
            End Get
        End Property

        Private ReadOnly Property Pixel(p_Index As Integer) As Integer
            Get
                Return _pixels(p_Index)
            End Get
        End Property

        Public Sub New(p_width As Integer, p_height As Integer)
            _width = p_width
            _height = p_height
            ReDim _pixels(p_width * p_height)
        End Sub

        Public Sub SetPixel(p_x As Integer, p_y As Integer, p_biColor As BinaryColor)
            _pixels(p_y * Width + p_x) = p_biColor
        End Sub

#Region "IDisposable Support"
        Private disposedValue As Boolean ' To detect redundant calls

        ' IDisposable
        Protected Overridable Sub Dispose(disposing As Boolean)
            If Not Me.disposedValue Then
                If disposing Then
                    _pixels = Nothing
                End If

                ' TODO: free unmanaged resources (unmanaged objects) and override Finalize() below.
                ' TODO: set large fields to null.
            End If
            Me.disposedValue = True
        End Sub

        ' TODO: override Finalize() only if Dispose(ByVal disposing As Boolean) above has code to free unmanaged resources.
        'Protected Overrides Sub Finalize()
        '    ' Do not change this code.  Put cleanup code in Dispose(ByVal disposing As Boolean) above.
        '    Dispose(False)
        '    MyBase.Finalize()
        'End Sub

        ' This code added by Visual Basic to correctly implement the disposable pattern.
        Public Sub Dispose() Implements IDisposable.Dispose
            ' Do not change this code.  Put cleanup code in Dispose(disposing As Boolean) above.
            Dispose(True)
            GC.SuppressFinalize(Me)
        End Sub
#End Region

    End Class
    Public Class BinaryImage
        Inherits AbstractBinaryImage
        Implements IDisposable

        Private Const _DefaultThresh As Integer = 1500
        Private _bgColor As Integer = BLACK

        Private _Thresh As Integer = _DefaultThresh

        Private Function GrayRGB(p_Color As Color) As Integer
            Return CInt(((p_Color.R * 11) + (p_Color.G * 16) + (p_Color.B * 5) / 32))
        End Function

        Private Function Luminance(p_color As Color) As Integer
            Dim _rgbRed As Double = Math.Pow(p_color.R, 2)
            Dim _rgbGreen As Double = Math.Pow(p_color.G, 2)
            Dim _rgbBlue As Double = Math.Pow(p_color.B, 2)

            Return CInt(Math.Sqrt((_rgbRed * 0.299) + (_rgbGreen * 0.587) + (_rgbBlue * 0.114)))
        End Function

        Public ReadOnly Property BackgroundColor As Integer
            Get
                Return _bgColor
            End Get
        End Property

        Private Sub ParseImage(p_Image As Image)
            Dim _bmpParser As New BitmapParser(CType(p_Image, Bitmap))
            _bmpParser.LockBits()

            Dim _blackCnt As Integer = 1
            Dim _whiteCnt As Integer = 0

            For y As Integer = 0 To _bmpParser.Height - 1
                For x As Integer = 0 To _bmpParser.Width - 1

                    Dim _grgb As Integer = GrayRGB(_bmpParser.GetPixel(x, y))

                    If _grgb >= _Thresh Then
                        SetPixel(x, y, BinaryColor.Black)
                    Else
                        SetPixel(x, y, BinaryColor.White)
                    End If
                Next
            Next
            _bmpParser.UnlockBits()
        End Sub

        Public Sub New(p_image As Image)
            MyBase.New(p_image.Width, p_image.Height)
            ParseImage(p_image)
        End Sub



        Public Function GetImage() As Bitmap
            Dim _index As Integer = 0
            Dim _bmp As New Bitmap(Width, Height, Imaging.PixelFormat.Format32bppArgb)
            Dim _bmpParser As New BitmapParser(_bmp)
            _bmpParser.LockBits()
            For y As Integer = 0 To _bmpParser.Height - 1
                For x As Integer = 0 To _bmpParser.Width - 1
                    Dim _color As Color = If(Pixel(x, y) > 0, Color.White, Color.Black)
                    _bmpParser.SetPixel(x, y, _color)
                Next
            Next
            _bmpParser.UnlockBits()
            Return _bmp
        End Function

#Region "IDisposable Support"
        Private disposedValue As Boolean ' To detect redundant calls

        ' IDisposable
        Protected Overridable Shadows Sub Dispose(disposing As Boolean)
            If Not Me.disposedValue Then
                If disposing Then
                    MyBase.Dispose()
                End If

                ' TODO: free unmanaged resources (unmanaged objects) and override Finalize() below.
                ' TODO: set large fields to null.
            End If
            Me.disposedValue = True
        End Sub

        ' TODO: override Finalize() only if Dispose(ByVal disposing As Boolean) above has code to free unmanaged resources.
        'Protected Overrides Sub Finalize()
        '    ' Do not change this code.  Put cleanup code in Dispose(ByVal disposing As Boolean) above.
        '    Dispose(False)
        '    MyBase.Finalize()
        'End Sub

        ' This code added by Visual Basic to correctly implement the disposable pattern.
        Public Shadows Sub Dispose()
            ' Do not change this code.  Put cleanup code in Dispose(disposing As Boolean) above.
            Dispose(True)
            GC.SuppressFinalize(Me)
        End Sub
#End Region

    End Class

测试#1和2

Private Sub ConvertJpegToBiIMage(p_fullFileName As String)
    Dim _BinaryImage As BinaryImage = Nothing
    Dim _outPutFolder As String = "C:\BinaryImages\"
    Dim _fileName As String = Path.GetFileNameWithoutExtension(p_fullFileName)
    Dim _outBoundFileName As String = _outPutFolder + "BI__" + _fileName + ".jpeg"

    _BinaryImage = New BinaryImage(Image.FromFile(p_fullFileName))
    _BinaryImage.GetImage.Save(_outBoundFileName, System.Drawing.Imaging.ImageFormat.Jpeg)
End Sub

测试#3

Private Sub ConvertJpegToBiIMage(p_fullFileName As String)
    Dim _BinaryImage As BinaryImage = Nothing
    Dim _outPutFolder As String = "C:\BinaryImages\"
    Dim _fileName As String = Path.GetFileNameWithoutExtension(p_fullFileName)
    Dim _outBoundFileName As String = _outPutFolder + "BI__" + _fileName + ".jpeg"

    Dim _bmp As Bitmap = Nothing

    Using _obmp = New Bitmap(p_fullFileName)
        _bmp = New Bitmap(_obmp.Width, _obmp.Height, Imaging.PixelFormat.Format32bppArgb)
        Using g As Graphics = Graphics.FromImage(_bmp)
            g.DrawImage(_obmp, New Rectangle(0, 0, _bmp.Width, _bmp.Height))
        End Using
    End Using

    _BinaryImage = New BinaryImage(_bmp)
    _BinaryImage.GetImage.Save(_outBoundFileName, System.Drawing.Imaging.ImageFormat.Jpeg)
End Sub

0 个答案:

没有答案