所以这是我的问题。我有一个我需要缩小的图像。原始图像是灰度PNG,这不是一个大问题,除了当我将其缩小时,热敏标签打印机拾取工件并将其打印在标签上。所以,我所做的是将图像更改为黑色&调整大小之前的白色(Format1bppIndexed),如下所示:
Dim bte() As Byte = System.Convert.FromBase64String(imgStr)
Dim ms As New IO.MemoryStream(bte)
Dim bmp As New System.Drawing.Bitmap(ms)
Dim monoImage As Drawing.Bitmap = New Drawing.Bitmap(1200, 1800, Drawing.Imaging.PixelFormat.Format1bppIndexed)
Dim rect As New Drawing.Rectangle(0, 0, 1200, 1800)
monoImage = bmp.Clone(rect, Drawing.Imaging.PixelFormat.Format1bppIndexed)
然后我调整它的大小。这段代码在我的Windows 7机器上工作正常,但是当我在它调用home的Windows 2003 Server框上运行它时,它总是在它到达bmp.Clone行时抛出OutOfMemoryException。
有关正在发生的事情的任何想法,或者可能是将图像转换为B& W的更好解决方案吗?
答案 0 :(得分:2)
以下是this article的一些转换后的源代码:
Option Explicit On
Option Strict On
Public Class BitmapEncoder
''' <summary>
''' Copies a bitmap into a 1bpp bitmap of the same dimensions, fast
''' </summary>
''' <param name="b">original bitmap</param>
''' <returns>a 1bpp copy of the bitmap</returns>
Public Shared Function ConvertBitmapTo1bpp(ByVal b As System.Drawing.Bitmap) As System.Drawing.Bitmap
' Plan: built into Windows GDI is the ability to convert
' bitmaps from one format to another. Most of the time, this
' job is actually done by the graphics hardware accelerator card
' and so is extremely fast. The rest of the time, the job is done by
' very fast native code.
' We will call into this GDI functionality from C#. Our plan:
' (1) Convert our Bitmap into a GDI hbitmap (ie. copy unmanaged->managed)
' (2) Create a GDI monochrome hbitmap
' (3) Use GDI "BitBlt" function to copy from hbitmap into monochrome (as above)
' (4) Convert the monochrone hbitmap into a Bitmap (ie. copy unmanaged->managed)
Dim w As Integer = b.Width, h As Integer = b.Height
Dim hbm As IntPtr = b.GetHbitmap()
' this is step (1)
'
' Step (2): create the monochrome bitmap.
' "BITMAPINFO" is an interop-struct which we define below.
' In GDI terms, it's a BITMAPHEADERINFO followed by an array of two RGBQUADs
Dim bmi As New BITMAPINFO()
bmi.biSize = 40
' the size of the BITMAPHEADERINFO struct
bmi.biWidth = w
bmi.biHeight = h
bmi.biPlanes = 1
' "planes" are confusing. We always use just 1. Read MSDN for more info.
bmi.biBitCount = CShort(1)
' ie. 1bpp or 8bpp
bmi.biCompression = BI_RGB
' ie. the pixels in our RGBQUAD table are stored as RGBs, not palette indexes
bmi.biSizeImage = CUInt((((w + 7) And &HFFFFFFF8) * h / 8))
bmi.biXPelsPerMeter = 1000000
' not really important
bmi.biYPelsPerMeter = 1000000
' not really important
' Now for the colour table.
Dim ncols As UInteger = CUInt(1) << 1
' 2 colours for 1bpp; 256 colours for 8bpp
bmi.biClrUsed = ncols
bmi.biClrImportant = ncols
bmi.cols = New UInteger(255) {}
' The structure always has fixed size 256, even if we end up using fewer colours
bmi.cols(0) = MAKERGB(0, 0, 0)
bmi.cols(1) = MAKERGB(255, 255, 255)
'
' Now create the indexed bitmap "hbm0"
Dim bits0 As IntPtr
' not used for our purposes. It returns a pointer to the raw bits that make up the bitmap.
Dim hbm0 As IntPtr = CreateDIBSection(IntPtr.Zero, bmi, DIB_RGB_COLORS, bits0, IntPtr.Zero, 0)
'
' Step (3): use GDI's BitBlt function to copy from original hbitmap into monocrhome bitmap
' GDI programming is kind of confusing... nb. The GDI equivalent of "Graphics" is called a "DC".
Dim sdc As IntPtr = GetDC(IntPtr.Zero)
' First we obtain the DC for the screen
' Next, create a DC for the original hbitmap
Dim hdc As IntPtr = CreateCompatibleDC(sdc)
SelectObject(hdc, hbm)
' and create a DC for the monochrome hbitmap
Dim hdc0 As IntPtr = CreateCompatibleDC(sdc)
SelectObject(hdc0, hbm0)
' Now we can do the BitBlt:
BitBlt(hdc0, 0, 0, w, h, hdc, 0, 0, SRCCOPY)
' Step (4): convert this monochrome hbitmap back into a Bitmap:
Dim b0 As System.Drawing.Bitmap = System.Drawing.Bitmap.FromHbitmap(hbm0)
'
' Finally some cleanup.
DeleteDC(hdc)
DeleteDC(hdc0)
ReleaseDC(IntPtr.Zero, sdc)
DeleteObject(hbm)
DeleteObject(hbm0)
'
Return b0
End Function
Private Shared SRCCOPY As Integer = &HCC0020
Private Shared BI_RGB As UInteger = 0
Private Shared DIB_RGB_COLORS As UInteger = 0
<System.Runtime.InteropServices.DllImport("gdi32.dll")> _
Private Shared Function DeleteObject(ByVal hObject As IntPtr) As Boolean
End Function
<System.Runtime.InteropServices.DllImport("user32.dll")> _
Private Shared Function GetDC(ByVal hwnd As IntPtr) As IntPtr
End Function
<System.Runtime.InteropServices.DllImport("gdi32.dll")> _
Private Shared Function CreateCompatibleDC(ByVal hdc As IntPtr) As IntPtr
End Function
<System.Runtime.InteropServices.DllImport("user32.dll")> _
Private Shared Function ReleaseDC(ByVal hwnd As IntPtr, ByVal hdc As IntPtr) As Integer
End Function
<System.Runtime.InteropServices.DllImport("gdi32.dll")> _
Private Shared Function DeleteDC(ByVal hdc As IntPtr) As Integer
End Function
<System.Runtime.InteropServices.DllImport("gdi32.dll")> _
Private Shared Function SelectObject(ByVal hdc As IntPtr, ByVal hgdiobj As IntPtr) As IntPtr
End Function
<System.Runtime.InteropServices.DllImport("gdi32.dll")> _
Private Shared Function BitBlt(ByVal hdcDst As IntPtr, ByVal xDst As Integer, ByVal yDst As Integer, ByVal w As Integer, ByVal h As Integer, ByVal hdcSrc As IntPtr, _
ByVal xSrc As Integer, ByVal ySrc As Integer, ByVal rop As Integer) As Integer
End Function
<System.Runtime.InteropServices.DllImport("gdi32.dll")> _
Private Shared Function CreateDIBSection(ByVal hdc As IntPtr, ByRef bmi As BITMAPINFO, ByVal Usage As UInteger, ByRef bits As IntPtr, ByVal hSection As IntPtr, ByVal dwOffset As UInteger) As IntPtr
End Function
<System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Sequential)> _
Private Structure BITMAPINFO
Public biSize As UInteger
Public biWidth As Integer, biHeight As Integer
Public biPlanes As Short, biBitCount As Short
Public biCompression As UInteger, biSizeImage As UInteger
Public biXPelsPerMeter As Integer, biYPelsPerMeter As Integer
Public biClrUsed As UInteger, biClrImportant As UInteger
<System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.ByValArray, SizeConst:=256)> _
Public cols As UInteger()
End Structure
Private Shared Function MAKERGB(ByVal r As Integer, ByVal g As Integer, ByVal b As Integer) As UInteger
Return CUInt((b And 255)) Or CUInt(((r And 255) << 8)) Or CUInt(((g And 255) << 16))
End Function
Private Sub New()
End Sub
End Class
使用以下方式调用:
Dim myFile = "c:\test.jpg"
Using Input As New Bitmap(myFile)
Using Output = BitmapEncoder.ConvertBitmapTo1bpp(Input)
Output.Save("c:\test.bmp")
End Using
End Using
答案 1 :(得分:1)
GDI +异常消息非常悲惨,如果您传递的矩形位于图像边界之外,则可以在Clone()方法中引发OutOfMemoryException。与内存耗尽无关。这可能很容易发生,源位图的可能性不大于1200 x 1800.使您的代码看起来像这样:
Dim bte() As Byte = System.Convert.FromBase64String(imgStr)
Dim ms As New IO.MemoryStream(bte)
Dim monoImage As Drawing.Bitmap
Using bmp As New Drawing.Bitmap(ms)
Dim rect As New Drawing.Rectangle(0, 0, bmp.Width, bmp.Height)
monoImage = bmp.Clone(rect, Drawing.Imaging.PixelFormat.Format1bppIndexed)
End Using
不确定生成的位图看起来是好还是可用,GDI +对索引像素格式的支持不足。 Win7有一个不同版本的gdiplus.dll而不是Windows 2003.我只在Win7上测试了这段代码。
答案 2 :(得分:0)
看看这个:http://msdn.microsoft.com/en-us/library/system.windows.media.imaging.formatconvertedbitmap.aspx
但是将PixelFormats.Gray32Float
更改为PixelFormats.BlackWhite
答案 3 :(得分:0)
我立即看到的一个问题是你的代码有资源泄漏:
Dim monoImage As Drawing.Bitmap = New Drawing.Bitmap(1200, 1800,
Drawing.Imaging.PixelFormat.Format1bppIndexed)
...
monoImage = bmp.Clone(rect, Drawing.Imaging.PixelFormat.Format1bppIndexed)
您正在创建一个全新的位图并将其分配给monoImage
,然后几乎立即将其丢弃而不会丢弃原始Bitmap
。如果你经常这样做,你就会泄漏大量的GDI资源,并因此会收到各种错误(包括OOM)。
只需将声明移至与“新”作业相同的行:
Dim monoImage As Drawing.Bitmap = bmp.Clone(rect,
Drawing.Imaging.PixelFormat.Format1bppIndexed)
您还需要确保处置其他Bitmap
和MemoryStream
,依此类推;所有这些都实现了IDisposable
,所以只需将它们包装在Using
。