JPEG压缩导致GDI +异常 [英] JPEG Compression causes GDI+ Exception

查看:186
本文介绍了JPEG压缩导致GDI +异常的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

所以,我目前正在开发一个LAN-Video-Streaming程序,它记录单个图像并发送它们。因为它太多,每秒发送30 1920x1080张图片,得到30FPS,我做了一些研究,并找到JPEG压缩。问题是,当我尝试保存压缩的JPEG,它抛出一个 System.Runtime.InteropServices.ExternalException ,附加信息:一般错误在GDI +。



这是我的代码:

  Private Sub Stream()处理StreamTimer.Tick 

如果Streaming = True然后

Try
ScreenCap =新的位图(Bounds.Width,Bounds。高度)

GFX = Graphics.FromImage(ScreenCap)
GFX.CopyFromScreen(0,0,0,0,ScreenCap.Size)

位图(ScreenCap,Resolution.Split(;)(0),Resolution.Split(;)(1))

Dim jpgEncoder As ImageCodecInfo = GetEncoder(ImageFormat.Jpeg)
Dim myEncoder As Encoder = Encoder.Quality
Dim myEncoderParameters As new EncoderParameters(1)
Dim myEncoderParameter As New EncoderParameter(myEncoder,Compression)
myEncoderParameters.Param(0)= myEncoderParameter

Frame.Save(My.Computer.FileSystem.SpecialDirectories.Temp& \LSSFrame.jpg,jpgEncoder,myEncoderParameters)'错误发生在这一行

使用FS作为新的FileStream(My.Computer.FileSystem.SpecialDirectories.Temp&\LSSFrame.jpg ,FileMode.Open)
Frame = Image.FromStream(FS)
FrameSizeStatus.Text = Math.Round(FS.Length / 1000)& KB
FS.Close()
结束使用

PreviewBox.Image = Frame
FPSStat + = 1

FlushMemory

如果ViewerIPs.Count> 0 Then

For i = 0 To ViewerIPs.Count - 1
SendFrame(ViewerIPs(i),Frame)
Next

结束如果
Catch ex As Exception
LostFrames + = 1
结束尝试
结束如果

结束子

解决方案

不会处理您创建的 Graphics Bitmap 对象。错误消息不是非常有帮助,但不处理那些将留下资源未回收。



这个过程中还有很多事情。如果它被分成部分,可能更容易调整性能等。

 '表单级对象
私有jEncParams As EncoderParameters
私有jpgEncoder作为ImageCodecInfo
...
初始化过程开始的某个地方:
质量差作为Int64 = 95

jpgEncoder = GetEncoder(ImageFormat.Jpeg)
Dim myjEnc As Imaging.Encoder = Imaging.Encoder.Quality

jEncParams = New EncoderParameters(1)
'quality与compression相反
jEncParams.Param(0)= New EncoderParameter(myjEnc,quality)

质量元素不会为每个屏幕快照变化,创建一次并重新使用它们。然后你的计时器事件:

  Dim scrBytes = GetScreenSnap(1280,720)
' ..可以排队他们?
Console.WriteLine(image size:{0} k,(scrBytes.Length / 1024).ToString)

优化 SendFrame 超出了此Q / A的范围,但获取屏幕截图与发送是分开的。

 私有函数GetScreenSnap(w As Int32,h As Int32)As Byte()

使用bmpScrn作为新的位图(My.Computer.Screen.Bounds.Width,My.Computer.Screen.Bounds.Height)
使用g As Graphics = Graphics.FromImage(bmpScrn)
g.CopyFromScreen(0,0 ,0,0,bmpScrn.Size)
结束使用'done with graphics

使用bmpThumb作为新的位图(bmpScrn,w,h),
ms作为新的MemoryStream
bmpThumb.Save(ms,jpgEncoder,jEncParams)
返回ms.ToArray
结束使用'dispose of bmp
结束使用'dispose of bmpScrn

结束函数

由于没有特别的原因,我缩略整个屏幕。你的似乎没有使用 Bounds.Width,Bounds.Height ,因为那将指的是形式。它只能作为屏幕快照,如果窗体被最大化。关键点:




  • 这是基于您可以/将会在流中发送字节数组的想法。因此,我把它作为一个字节数组,而不是创建一个BMP(推测)以后转换回来。

  • 没有必要创建一个磁盘文件来获取大小。

  • 我从未使用过COMPRESSION参数,但我知道Quality与压缩是相反的。



各种品质因素的尺寸:


100 = 462

95 = 254

90 = 195

80 = 147


严格地说,你不需要编码器, bmpThumb.Save(ms,Imaging.ImageFormat.Jpeg)也可以工作,但一个编码器对象提供更多的微调。 p>




对于发送部分,您可能需要 Stack Queue LinkedList 来存储字节数组。这将进一步隔离图像不发送它们。



然后,如果有多个收件人,我会调查一下 SendFrame 作为任务,可能一次发送2-3。可能有一个点,收件人的数量干扰你可以抓住新的速度。


So, I'm currently working on a LAN-Video-Streaming program, which records single images and sends them over. Because it would be too much to send 30 1920x1080 pictures per second, to get 30FPS, I did some research and found JPEG-Compression. The problem is, that when I try to save the compressed JPEG, it throws an System.Runtime.InteropServices.ExternalException, with the additional information: General error in GDI+.

Here's my code:

Private Sub Stream() Handles StreamTimer.Tick

    If Streaming = True Then

        Try
            ScreenCap = New Bitmap(Bounds.Width, Bounds.Height)

            GFX = Graphics.FromImage(ScreenCap)
            GFX.CopyFromScreen(0, 0, 0, 0, ScreenCap.Size)

            Dim Frame As New Bitmap(ScreenCap, Resolution.Split(";")(0), Resolution.Split(";")(1))

            Dim jpgEncoder As ImageCodecInfo = GetEncoder(ImageFormat.Jpeg)
            Dim myEncoder As Encoder = Encoder.Quality
            Dim myEncoderParameters As New EncoderParameters(1)
            Dim myEncoderParameter As New EncoderParameter(myEncoder, Compression)
            myEncoderParameters.Param(0) = myEncoderParameter

            Frame.Save(My.Computer.FileSystem.SpecialDirectories.Temp & "\LSSFrame.jpg", jpgEncoder, myEncoderParameters) 'Error occurs in this line

            Using FS As New FileStream(My.Computer.FileSystem.SpecialDirectories.Temp & "\LSSFrame.jpg", FileMode.Open)
                Frame = Image.FromStream(FS)
                FrameSizeStatus.Text = Math.Round(FS.Length / 1000) & "KB"
                FS.Close()
            End Using

            PreviewBox.Image = Frame
            FPSStat += 1

            FlushMemory()

            If ViewerIPs.Count > 0 Then

                For i = 0 To ViewerIPs.Count - 1
                    SendFrame(ViewerIPs(i), Frame)
                Next

            End If
        Catch ex As Exception
            LostFrames += 1
        End Try
    End If

End Sub

Any help is appreciated!

解决方案

In part, you are not disposing of the Graphics or Bitmap objects you create. The error message is not very helpful, but not disposing of those will leave resources unrecovered.

There is also a lot going on in that procedure. If it were broken into parts it might be easier to fine-tune for performance and such.

' form level objects
Private jEncParams As EncoderParameters
Private jpgEncoder As ImageCodecInfo
...
' inititalize somewhere when the process starts:
Dim quality As Int64 = 95

jpgEncoder = GetEncoder(ImageFormat.Jpeg)
Dim myjEnc As Imaging.Encoder = Imaging.Encoder.Quality

jEncParams = New EncoderParameters(1)
' quality is inverse to compression
jEncParams.Param(0) = New EncoderParameter(myjEnc, quality)

Since the encoder and quality elements are not going to change for each screen snap, create them once and resuse them. Then your timer event:

Dim scrBytes = GetScreenSnap(1280, 720)
' do something to send them....maybe queue them?
Console.WriteLine("image size: {0}k", (scrBytes.Length / 1024).ToString)

Optimizing SendFrame is outside the scope of this Q/A, but getting the screen shot is separate from sending.

Private Function GetScreenSnap(w As Int32, h As Int32) As Byte()

    Using bmpScrn As New Bitmap(My.Computer.Screen.Bounds.Width, My.Computer.Screen.Bounds.Height)
        Using g As Graphics = Graphics.FromImage(bmpScrn)
            g.CopyFromScreen(0, 0, 0, 0, bmpScrn.Size)
        End Using       ' done with graphics

        Using bmpThumb As New Bitmap(bmpScrn, w, h),
            ms As New MemoryStream
            bmpThumb.Save(ms, jpgEncoder, jEncParams)
            Return ms.ToArray
        End Using        ' dispose of bmp
    End Using            ' dispose of bmpScrn

End Function

For no particular reason, I am thumbnailing the entire screen. Yours seems off using Bounds.Width, Bounds.Height since that would refer to the form. It would only work as a screen snapshot if the form is maximized. keypoints:

  • This is predicated on the idea that you can/will be sending the byte array in a stream. As such, I leave it as a byte array rather than creating a BMP only to (presumably) convert it back later.
  • There is no need to create a disk file to get the size. If the array contains encoded bytes, it will be the same size.
  • I've never used the COMPRESSION param, but I know Quality is inverse to compression.

Resulting sizes for various Quality factors:

100 = 462
95 = 254
90 = 195
80 = 147

Strictly speaking you do not need the encoder, bmpThumb.Save(ms, Imaging.ImageFormat.Jpeg) will also work but a single encoder object offers more fine tuning.


For the sending part, you might want a Stack, Queue or LinkedList to store the byte array. This would further isolate getting images from sending them. The collection would be a sort of ToDo/ToSend list.

Then, if there are multiple recipients, I'd look into perhaps doing SendFrame as a Task, perhaps sending 2-3 at a time. There might be a point where the number of recievers interferes with how fast you can grab new ones.

这篇关于JPEG压缩导致GDI +异常的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆