请查看测试#2的结果。当我在photoshop中稍微调整图像大小时,为什么输出(二进制图像)会歪斜?但是,如果我将相同图像的像素格式转换为Format32bppArgb,那么输出就可以了......
我已将其缩小为 GetPixel()像素方法(请参阅BitmapParser类)。我不明白为什么这种方法为调整大小的图像返回错误的像素颜色......
包含源代码。
测试:1 (有效输出)
来源图片:Test_200x200.jpg
宽度:200
身高:200
像素格式:Format24bppRgb
输出:
测试:2 (输出无效)
源图像:Test_203x203.jpg(相同的图像,在Photoshop中稍微调整大小)
宽度: 203
身高: 203
像素格式:Format24bppRgb
输出:
测试:3 (有效输出)
源图像:Test_203x203.jpg(与测试#2相同)
宽度:203
身高:203
像素格式:在内存中转换为Format32bppArgb
输出:
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