Results 1 to 20 of 20

Thread: [RESOLVED] Picturebox memory leak with .refresh() to redraw it.

  1. #1

    Thread Starter
    Member
    Join Date
    Oct 2007
    Posts
    63

    Resolved [RESOLVED] Picturebox memory leak with .refresh() to redraw it.

    I am writing to two pictureboxes via a thread constantly reading images as Bitmaps (video frames). This is all working fine except for the form updating.

    However the pictureboxes are not updating at the same speed. Picturebox 2 is almost realtime whilst picturebox 1 is updating 1 frame in about every 10.
    Picturebox 2 is a modified version of the original. Everything is diposed of and there are no memory leaks until i try to get the form to update.

    The code below is safe and doesn't increase memory usage.

    VB.NET Code:
    1. ' cross thread to update the picturebox
    2.                     If Me.InvokeRequired Then
    3.                         BeginInvoke(Sub()
    4.                                         If pic1.Image IsNot Nothing Then
    5.                                             pic1.Image.Dispose()
    6.                                             pic1.Image = Nothing
    7.                                             If MS IsNot Nothing Then MS.Dispose()
    8.                                             GC.Collect()
    9.                                             pic1.Image = Bmp
    10.                                         Else
    11.                                             pic1.Image = Bmp
    12.                                         End If
    13.  
    14.  
    15.                                         If pic2.Image IsNot Nothing Then
    16.                                             pic2.Image.Dispose()
    17.                                             pic2.Image = Nothing
    18.                                             If MS IsNot Nothing Then MS.Dispose()
    19.                                             GC.Collect()
    20.                                             pic2.Image = scanimage(Bmp)
    21.                                         Else
    22.                                             pic2.Image = scanimage(Bmp)
    23.  
    24.                                         End If
    25.                                     End Sub)
    26.                     Else
    27.                         ' nothing here yet
    28.                     End If

    However, when I try to refresh either a picturebox or the form with

    pic1.refresh() or me.refresh() after pic1.Image = Bmp

    Memory increases rapidly by 10MB + each image until it hits about 2GB and crashes.

    VB.NET Code:
    1. ' cross thread to update the picturebox
    2.                     If Me.InvokeRequired Then
    3.                         BeginInvoke(Sub()
    4.                                         If pic1.Image IsNot Nothing Then
    5.                                             pic1.Image.Dispose()
    6.                                             pic1.Image = Nothing
    7.                                             If MS IsNot Nothing Then MS.Dispose()
    8.                                             GC.Collect()
    9.                                             pic1.Image = Bmp
    10.                                         Else
    11.                                             pic1.Image = Bmp
    12.                                             pic1.refresh()                                     '<------------ refresh causes rapid memory leak
    13.                                         End If
    14.  
    15.  
    16.                                         If pic2.Image IsNot Nothing Then
    17.                                             pic2.Image.Dispose()
    18.                                             pic2.Image = Nothing
    19.                                             If MS IsNot Nothing Then MS.Dispose()
    20.                                             GC.Collect()
    21.                                             pic2.Image = scanimage(Bmp)
    22.                                         Else
    23.                                             pic2.Image = scanimage(Bmp)
    24.  
    25.                                         End If
    26.                                     End Sub)
    27.                     Else
    28.                         ' nothing here yet
    29.                     End If

    NB, The Garbage collector statement doesn't need to be there - its not doing anything.

    Does anyone have any idea how to get the pictureboxes to redraw without memory leaks ?
    Last edited by mickle026; Mar 4th, 2019 at 10:48 AM.

  2. #2
    Powered By Medtronic dbasnett's Avatar
    Join Date
    Dec 2007
    Location
    Jefferson City, MO
    Posts
    9,754

    Re: Picturebox memory leak with .refresh() to redraw it.

    Except for not knowing what MS is you might be over thinking this.

    Try this. Does it work?

    Code:
            If Me.InvokeRequired Then
                BeginInvoke(Sub()
                                If pic1.Image IsNot Nothing Then
                                    pic1.Image.Dispose()
                                    If MS IsNot Nothing Then MS.Dispose()
                                    pic1.Image = Bmp
                                Else
                                    pic1.Image = Bmp
                                End If
    
                                If pic2.Image IsNot Nothing Then
                                    pic2.Image.Dispose()
                                    If MS IsNot Nothing Then MS.Dispose()
                                    pic2.Image = scanimage(Bmp)
                                Else
                                    pic2.Image = scanimage(Bmp)
                                End If
                                Me.Refresh()
                            End Sub)
            Else
                ' nothing here yet
            End If
    My First Computer -- Documentation Link (RT?M) -- Using the Debugger -- Prime Number Sieve
    Counting Bits -- Subnet Calculator -- UI Guidelines -- >> SerialPort Answer <<

    "Those who use Application.DoEvents have no idea what it does and those who know what it does never use it." John Wein

  3. #3
    Super Moderator Shaggy Hiker's Avatar
    Join Date
    Aug 2002
    Location
    Idaho
    Posts
    38,989

    Re: Picturebox memory leak with .refresh() to redraw it.

    What is scanimage? What does it do?

    You might want to use Invalidate rather than Refresh. Refresh forces the code to stop and draw right then, whereas Invalidate just notes that the PB is dirty, so it gets drawn as soon as the UI thread has some time. This is generally nicer, though it can also mean that drawing doesn't happen, if the UI thread is NEVER free. That doesn't seem likely to be an issue, in your case.
    My usual boring signature: Nothing

  4. #4

    Thread Starter
    Member
    Join Date
    Oct 2007
    Posts
    63

    Re: Picturebox memory leak with .refresh() to redraw it.

    Hi, thanks

    Scanimage is another routine that alters the bitmap in a lockbits/marshalling function. There's no memory leak there.

    Invalidate updates just a few more frames and is very similar to not using it.

    Something interesting in what your response is though - the UI thread, i need to look at that

    I have just tried a timer refresh interval of 20ms

    Private Sub Timer1_Tick(sender As Object, e As EventArgs) Handles Timer1.Tick
    pic1.Refresh()
    pic2.Refresh()
    End Sub

    This updates it without memory increase - so the I think the UI thread is where i need to be , but its still slow on picturebox 1.

  5. #5
    Powered By Medtronic dbasnett's Avatar
    Join Date
    Dec 2007
    Location
    Jefferson City, MO
    Posts
    9,754

    Re: Picturebox memory leak with .refresh() to redraw it.

    I ran this test and:
    1. No memory leaks
    2. No out of mem
    3. UI was semi-responsive

    One refresh seems to take care of any control that is Invaidated. I changed the BeginInvoke to Invoke.

    Code:
        Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
            Static ptsk As task
            If ptsk Is Nothing OrElse ptsk.Status = TaskStatus.RanToCompletion Then
                ptsk = Task.Run(Sub() ShowPics())
            End If
        End Sub
    
        Private Sub ShowPics()
            Dim pth As String = Environment.GetFolderPath(Environment.SpecialFolder.MyPictures)
            Dim fs As List(Of String) = IO.Directory.GetFiles(pth).ToList
            Dim ie As IEnumerable(Of String)
            '47 pics after select
            ie = From f In fs
                 Where IO.Path.GetExtension(f).StartsWith(".jp") OrElse IO.Path.GetExtension(f) = ".png"
                 Select f
    
            For x As Integer = 1 To 3
                For Each f As String In ie
                    If Me.InvokeRequired Then
                        Me.Invoke(Sub()
                                      If PictureBox1.Image IsNot Nothing Then
                                          PictureBox1.Image.Dispose()
                                      End If
                                      PictureBox1.Image = Image.FromFile(f)
                                      If PictureBox2.Image IsNot Nothing Then
                                          PictureBox2.Image.Dispose()
                                      End If
                                      PictureBox2.Image = Image.FromFile(f)
    
                                      PictureBox1.Refresh()
                                  End Sub)
                    End If
                    Threading.Thread.Sleep(1) 'larger more responsive UI
                Next
            Next
        End Sub
    My First Computer -- Documentation Link (RT?M) -- Using the Debugger -- Prime Number Sieve
    Counting Bits -- Subnet Calculator -- UI Guidelines -- >> SerialPort Answer <<

    "Those who use Application.DoEvents have no idea what it does and those who know what it does never use it." John Wein

  6. #6

    Thread Starter
    Member
    Join Date
    Oct 2007
    Posts
    63

    Re: Picturebox memory leak with .refresh() to redraw it.

    Quote Originally Posted by dbasnett View Post
    I ran this test and:
    1. No memory leaks
    2. No out of mem
    3. UI was semi-responsive

    One refresh seems to take care of any control that is Invaidated. I changed the BeginInvoke to Invoke.

    Code:
        Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
            Static ptsk As task
            If ptsk Is Nothing OrElse ptsk.Status = TaskStatus.RanToCompletion Then
                ptsk = Task.Run(Sub() ShowPics())
            End If
        End Sub
    
        Private Sub ShowPics()
            Dim pth As String = Environment.GetFolderPath(Environment.SpecialFolder.MyPictures)
            Dim fs As List(Of String) = IO.Directory.GetFiles(pth).ToList
            Dim ie As IEnumerable(Of String)
            '47 pics after select
            ie = From f In fs
                 Where IO.Path.GetExtension(f).StartsWith(".jp") OrElse IO.Path.GetExtension(f) = ".png"
                 Select f
    
            For x As Integer = 1 To 3
                For Each f As String In ie
                    If Me.InvokeRequired Then
                        Me.Invoke(Sub()
                                      If PictureBox1.Image IsNot Nothing Then
                                          PictureBox1.Image.Dispose()
                                      End If
                                      PictureBox1.Image = Image.FromFile(f)
                                      If PictureBox2.Image IsNot Nothing Then
                                          PictureBox2.Image.Dispose()
                                      End If
                                      PictureBox2.Image = Image.FromFile(f)
    
                                      PictureBox1.Refresh()
                                  End Sub)
                    End If
                    Threading.Thread.Sleep(1) 'larger more responsive UI
                Next
            Next
        End Sub
    Hi

    Thanks for the reply.

    I don't have any memory leaks running the picturebox updates on the same thread. The problem occurs because its a cross thread update. The picturebox refresh called from another thread is causing the memory leak.

    What my program does is receive data through a named pipe, rebuild the bitmap as a memory stream (MS) from a byte array, and then to bitmap

    Dim img As Byte() = byteList.ToArray() <-- received from pipe from external program such as ffmpeg
    Dim MS As New MemoryStream(img) <--- create a memory stream
    Dim Bmp As New Bitmap(MS) <--- create a bitmap from memory stream

    The problem i am having is showing the images realtime in pictureboxes. If I do just one picturebox, its fine - it works ok with no refreshing. As soon as there is a second picturebox, the second draws ok, but the first is intermittant - even with no bitmap manipulation. Refreshing with an invoke causes a rapid memory leak.

    I think with your help the solution is to send the thread to sleep, ie slow it down. It seems that data is arriving faster than it can draw the image.

    Threading.Thread.Sleep(5) . 5 milliseconds is not quite enough to allow the pictureboxes to fully redraw without a glitch

    Threading.Thread.Sleep(10). However 10 milliseconds is !!! - 15 milliseconds is even better

    VB.NET Code:
    1. If Me.InvokeRequired Then
    2.                         BeginInvoke(Sub()
    3.                                         If pic1.Image IsNot Nothing Then
    4.                                             pic1.Image.Dispose()
    5.                                             pic1.Image = Nothing
    6.                                             If MS IsNot Nothing Then MS.Dispose()
    7.                                             pic1.Invalidate()
    8.                                             pic1.Image = Bmp
    9.                                         Else
    10.                                             pic1.Image = Nothing
    11.                                             pic1.Invalidate()
    12.                                             pic1.Image = Bmp
    13.                                         End If
    14.  
    15.                                         If pic2.Image IsNot Nothing Then
    16.                                             pic2.Image.Dispose()
    17.                                             pic2.Image = Nothing
    18.                                             If MS IsNot Nothing Then MS.Dispose()
    19.                                             pic2.Invalidate()
    20.                                             pic2.Image = scanimage(Bmp)
    21.                                         Else
    22.                                             pic2.Image = Nothing
    23.                                             pic2.Invalidate()
    24.                                             pic2.Image = scanimage(Bmp)
    25.                                         End If
    26.  
    27.                                         pic1.Refresh()
    28.  
    29.                                     End Sub)
    30.  
    31.                         Threading.Thread.Sleep(15)
    32.                     Else
    33.                         ' nothing here yet
    34.                     End If


    Just so you are aware, with thread sleep the refresh isn't causing memory leaks - I wonder why it does without sleeping ? ?

    With the thread sleep it doesn't actually need the pic1.refresh()

    My bitmap generation is in the same thread, so its sequential its not creating another first unless the UI doesn't release it because of the redraw?
    Last edited by mickle026; Mar 4th, 2019 at 02:35 PM.

  7. #7

    Thread Starter
    Member
    Join Date
    Oct 2007
    Posts
    63

    Re: Picturebox memory leak with .refresh() to redraw it.

    Actually leaving out pic1.refresh() make the UI thread more responsive. With the thread sleep it doesn't need it.

    Many thanks

  8. #8
    Powered By Medtronic dbasnett's Avatar
    Join Date
    Dec 2007
    Location
    Jefferson City, MO
    Posts
    9,754

    Re: Picturebox memory leak with .refresh() to redraw it.

    Quote Originally Posted by mickle026 View Post
    Actually leaving out pic1.refresh() make the UI thread more responsive. With the thread sleep it doesn't need it.

    Many thanks
    I changed the example to have a sender and receiver to more closely model receipt of data over a Pipe.

    Code:
        Private rcvd As New Threading.AutoResetEvent(False) 'set upon receipt
        Private Q As New Concurrent.ConcurrentQueue(Of Byte()) 'queue of Byte()
    
        Private Sub RcvPics()
            Do
                rcvd.WaitOne()
                Dim b() As Byte
                While Q.Count > 0
                    'get the byte array
                    If Q.TryDequeue(b) Then
                        Dim ms As New IO.MemoryStream(b) 'back to MemoryStream
                        Dim img As Image = Image.FromStream(ms) 'MemoryStream to image
                        ms.Close()
                        ms.Dispose()
                        b = Nothing
                        'running on thread, no need to check if invoke needed
                        Me.BeginInvoke(Sub()
                                           If PictureBox1.Image IsNot Nothing Then
                                               PictureBox1.Image.Dispose()
                                           End If
                                           PictureBox1.Image = img
                                           If PictureBox2.Image IsNot Nothing Then
                                               PictureBox2.Image.Dispose()
                                           End If
                                           PictureBox2.Image = img
                                           'PictureBox1.Refresh() 'un-comment as needed
                                       End Sub)
                    Else
                        rcvd.WaitOne(50)
                    End If
                End While
            Loop
        End Sub
    
        Private Sub SendPics()
            Dim pth As String = Environment.GetFolderPath(Environment.SpecialFolder.MyPictures)
            Dim fs As List(Of String) = IO.Directory.GetFiles(pth).ToList
            Dim ie As IEnumerable(Of String)
    
            ie = From f In fs
                 Where IO.Path.GetExtension(f).StartsWith(".jp")
                 Select f
    
            For x As Integer = 1 To 2
                For Each f As String In ie
                    Dim ms As New IO.MemoryStream
                    Dim img As Image = Image.FromFile(f) 'file to image
                    img.Save(ms, Drawing.Imaging.ImageFormat.Bmp) 'save in MemoryStream
                    img.Dispose()
                    Dim b() As Byte = ms.ToArray() 'get byte array
                    ms.Close()
                    ms.Dispose()
                    Q.Enqueue(b)
                    rcvd.Set() 'indicate receipt
                Next
            Next
            fs.Clear()
        End Sub
    Last edited by dbasnett; Mar 4th, 2019 at 04:41 PM.
    My First Computer -- Documentation Link (RT?M) -- Using the Debugger -- Prime Number Sieve
    Counting Bits -- Subnet Calculator -- UI Guidelines -- >> SerialPort Answer <<

    "Those who use Application.DoEvents have no idea what it does and those who know what it does never use it." John Wein

  9. #9
    Super Moderator Shaggy Hiker's Avatar
    Join Date
    Aug 2002
    Location
    Idaho
    Posts
    38,989

    Re: Picturebox memory leak with .refresh() to redraw it.

    Quote Originally Posted by mickle026 View Post
    Actually leaving out pic1.refresh() make the UI thread more responsive. With the thread sleep it doesn't need it.

    Many thanks
    That's reasonable. Refresh is a pretty brutal thing to be calling, so you want to do it as infrequently as you can. Adding the image is invalidating the picturebox already, and that's enough to trigger a redraw at the time and choosing of the UI. If that time and choosing is working for you, then that's the best solution. Refresh is for those times when the time and choosing aren't good enough.
    My usual boring signature: Nothing

  10. #10
    Bad man! ident's Avatar
    Join Date
    Mar 2009
    Location
    Cambridge
    Posts
    5,398

    Re: [RESOLVED] Picturebox memory leak with .refresh() to redraw it.

    What is the secondary thread doing before it calls invoke?

  11. #11
    Bad man! ident's Avatar
    Join Date
    Mar 2009
    Location
    Cambridge
    Posts
    5,398

    Re: [RESOLVED] Picturebox memory leak with .refresh() to redraw it.

    Quote Originally Posted by OP
    I don't have any memory leaks running the picturebox updates on the same thread. The problem occurs because its a cross thread update. The picturebox refresh called from another thread is causing the memory leak.
    You are always updating the picturebox on the same thread. You can't access the picturebox without being on the UI thread. You have fallen into the trap like so many others and not understanding worker threads by thinking to create a secondary thread invoking back to the UI is how multithreading should be done. If all you are doing is creating a new image on this thread to invoke back to the UI will likely cause you more penalties.

  12. #12
    Bad man! ident's Avatar
    Join Date
    Mar 2009
    Location
    Cambridge
    Posts
    5,398

    Re: [RESOLVED] Picturebox memory leak with .refresh() to redraw it.

    All objects, regardless of how long they have been in memory, are considered for collection; however, objects that are referenced in managed code are not collected. Use this method to force the system to try to reclaim the maximum amount of available memory.

    https://docs.microsoft.com/en-us/dotnet/api/system.gc.collect?view=netframework-4.7.2#System_GC_Collect

  13. #13

    Thread Starter
    Member
    Join Date
    Oct 2007
    Posts
    63

    Re: [RESOLVED] Picturebox memory leak with .refresh() to redraw it.

    Quote Originally Posted by ident View Post
    You are always updating the picturebox on the same thread. You can't access the picturebox without being on the UI thread. You have fallen into the trap like so many others and not understanding worker threads by thinking to create a secondary thread invoking back to the UI is how multithreading should be done. If all you are doing is creating a new image on this thread to invoke back to the UI will likely cause you more penalties.

    You are quite right that I don't understand it. I am trying to though.. and I understand it a lot more than i did a week ago....

    I've got named pipes nailed now thanks to help here

    Not being formally educating in programming I've been learning what I need to by trial and error. Always trying to run before I can walk - thats me.... Probably not the best way to do it but I get there in the end. I find this method gets my interest a lot better and I dont get bored with it.

    By having these errors its helping me understand why I can't do these things .

    Im not just creating a new image on this thread. Im reading it from a stream of bytes from a named pipe from a console application - ie ffmpeg.exe.
    Im wanting to display the original image extracted and a modified version (that I edit) as they are read from the pipe.

    Currently im just modifying a few colours to see if I can do "the concept" that I thought of - it is working ... perhaps vb.net or my computer is just not fast enough. I eventually want to try and detect objects in the images

    Name:  Untitled-1.jpg
Views: 1360
Size:  20.7 KB

    I need a little time to ingest the info dbasnett has posted.

    The secondary thread is reading the data pipe

    Heres the code
    VB.NET Code:
    1. Private Sub StartServerPipe()
    2.         Dim ServerPipe As New System.IO.Pipes.NamedPipeServerStream("From_FFmpeg")
    3.  
    4.         ServerPipe.WaitForConnection()
    5.         ServerPipe.WaitForPipeDrain()
    6.         ' a continual listening loop
    7.  
    8.         Dim ImageLength As Integer = 0 ' create a reader
    9.         Dim byteList As New List(Of Byte)
    10.  
    11.         Do
    12.             Dim B(4095) As Byte
    13.             Dim L As Integer = ServerPipe.Read(B, 0, 4096)
    14.             If L > 0 Then
    15.                 ReDim Preserve B(L - 1)
    16.                 byteList.AddRange(B)
    17.  
    18.                 ' read the pipe until the next bitmap by getting the image length in bytes
    19.                 ' read the three byte header to check if its a BM6 type bitmap
    20.  
    21.                 If B(1) = &H4D AndAlso B(0) = &H42 Then
    22.                     ImageLength = BitConverter.ToInt32(B, 2)
    23.                     Dim remaining As Integer = ImageLength - byteList.Count
    24.                     Do While remaining > 0
    25.                         L = ServerPipe.Read(B, 0, Math.Min(B.Length, remaining))
    26.                         If L > 0 Then
    27.                             ReDim Preserve B(L - 1)
    28.                             byteList.AddRange(B)
    29.                         End If
    30.                         remaining = ImageLength - byteList.Count
    31.                     Loop
    32.  
    33.                     Dim img As Byte() = byteList.ToArray()
    34.                     Dim MS As New MemoryStream(img)
    35.                     Dim Bmp As New Bitmap(MS)
    36.  
    37.                     ' cross thread to update the picturebox
    38.                     If Me.InvokeRequired Then
    39.                         BeginInvoke(Sub()
    40.                                         If pic1.Image IsNot Nothing Then
    41.                                             pic1.Image.Dispose()
    42.                                             pic1.Image = Nothing
    43.                                             If MS IsNot Nothing Then MS.Dispose()
    44.                                             pic1.Invalidate()
    45.                                             ' Not updating the input picturebox will increase speed
    46.                                             If Me.chkShowImages.Checked = True Then
    47.                                                 pic1.Image = Bmp
    48.                                             End If
    49.                                         Else
    50.                                             pic1.Image = Nothing
    51.                                             pic1.Invalidate()
    52.                                             ' Not updating the input picturebox will increase speed
    53.                                             If Me.chkShowImages.Checked = True Then
    54.                                                 pic1.Image = Bmp
    55.                                             End If
    56.                                         End If
    57.  
    58.                                         If pic2.Image IsNot Nothing Then
    59.                                             pic2.Image.Dispose()
    60.                                             pic2.Image = Nothing
    61.                                             If MS IsNot Nothing Then MS.Dispose()
    62.                                             pic2.Invalidate()
    63.                                             pic2.Image = scanimage(Bmp)
    64.                                         Else
    65.                                             pic2.Image = Nothing
    66.                                             pic2.Invalidate()
    67.                                             pic2.Image = scanimage(Bmp)
    68.                                         End If
    69.  
    70.                                     End Sub)
    71.  
    72.                         Threading.Thread.Sleep(15)
    73.  
    74.                     Else
    75.                         ' nothing here yet
    76.                     End If
    77.  
    78.                     ' reset/flush the byte list array
    79.                     byteList.Clear()
    80.  
    81.                     '
    82.                     img = Nothing
    83.                 Else
    84.                     'output is not a valid Bitmap!
    85.                     'Throw Exception
    86.                 End If
    87.  
    88.             End If
    89.  
    90.  
    91.         Loop While ServerPipe.IsConnected
    92.  
    93.         ServerPipe.Close()
    94.     End Sub
    95.  
    96.     Private Sub ButAskFFmpegForImage_Click(sender As Object, e As EventArgs) Handles ButAskFFmpegForImage.Click
    97.  
    98.         OpenFileDialog1.RestoreDirectory = True
    99.         OpenFileDialog1.Filter = "Video Files (*.ts, *.mkv, *.mp4, *.avi)|*.ts;*.mkv;*.mp4; *.avi|All Files (*.*)|*.*"
    100.         OpenFileDialog1.ShowDialog()
    101.  
    102.         OpenVideoName = OpenFileDialog1.FileName
    103.         If OpenVideoName = "" Or OpenVideoName = "(Select a Movie File)" Then
    104.             ' no file selected
    105.             Exit Sub
    106.         End If
    107.  
    108.         If File.Exists(Application.StartupPath + "\ffmpeg.exe") Then
    109.             Dim ffmpegfilepath As String = Application.StartupPath + "\ffmpeg.exe"
    110.  
    111.             ' New ProcessStartInfo created
    112.             Dim p As New Process
    113.  
    114.             ' Specify the location of the binary
    115.             p.StartInfo.FileName = ffmpegfilepath
    116.  
    117.             ' single frame
    118.             'p.StartInfo.Arguments = " -i """ + OpenVideoName + """ -ss 00:00:1 -s 720x560 -vframes 1 -c:v bmp -y -f image2pipe " + """\\.\pipe\From_FFmpeg"""
    119.  
    120.             ' continous stream of frames
    121.             p.StartInfo.Arguments = " -i """ + OpenVideoName + """ -ss 00:00:5 -s 720x560 -c:v bmp -y -f image2pipe " + """\\.\pipe\From_FFmpeg"""
    122.  
    123.             p.StartInfo.UseShellExecute = False
    124.             ' Use a hidden window (rem these out to debug)
    125.             p.StartInfo.WindowStyle = ProcessWindowStyle.Hidden
    126.             p.StartInfo.CreateNoWindow = True
    127.             p.StartInfo.RedirectStandardError = True
    128.             p.StartInfo.RedirectStandardOutput = True
    129.             p.SynchronizingObject = Me
    130.             p.EnableRaisingEvents = True
    131.             AddHandler p.OutputDataReceived, AddressOf UpdateDataReceived
    132.             AddHandler p.ErrorDataReceived, AddressOf UpdateDataReceived
    133.             p.Start()
    134.             p.BeginErrorReadLine()
    135.             p.BeginOutputReadLine()
    136.         Else
    137.             MsgBox("No ffmpeg")
    138.         End If
    139.  
    140.     End Sub

    I have another thread reading the stderr / stdoutput.

    Im personally starting to think that 25fps to 2 pictureboxes is just too much and I should probably only draw at 4 of 5 fps
    Last edited by mickle026; Mar 4th, 2019 at 05:50 PM.

  14. #14
    Bad man! ident's Avatar
    Join Date
    Mar 2009
    Location
    Cambridge
    Posts
    5,398

    Re: [RESOLVED] Picturebox memory leak with .refresh() to redraw it.

    Not being formally educating in programming I've been learning what I need to by trial and error. Always trying to run before I can walk - thats me.... Probably not the best way to do it but I get there in the end. I find this method gets my interest a lot better and I dont get bored with it.

    No shame in that. I picked up my first VB book under the framework in 2002 when I shamefully say I made mistakes at 15 and was in a young institute prison. I had to write everything down on pen and paper and only ever advanced when a member here sitten (I met on another forum) drilled it into me that copying from
    other's code was not programming. I went on to get my degree and even though I am a carpenter (well was as quite ill now) I realized trial and error as so many people state is a way to learn is not a great way to learn.

    This forum has a great wealth of programmers.

  15. #15
    Sinecure devotee
    Join Date
    Aug 2013
    Location
    Southern Tier NY
    Posts
    6,582

    Re: [RESOLVED] Picturebox memory leak with .refresh() to redraw it.

    Since you're receiving your data and creating the images from a background thread, you may want to use a BufferedGraphics object so that you can fill an area of the screen associated with a control quicker and can do it from the background thread so no need to invoke back to the GUI thread.
    A BufferedGraphics object is made for the situation where you want to update the full image of an area of the screen repeatedly.

    An example here.

  16. #16
    Hyperactive Member
    Join Date
    Jun 2018
    Posts
    434

    Re: [RESOLVED] Picturebox memory leak with .refresh() to redraw it.

    "Im personally starting to think that 25fps to 2 pictureboxes is just too much and I should probably only draw at 4 of 5 fps "


    Are you using a windows form timer? You could use a separate thread with your own timer instead of the windows form timer. First off my bet is that you will run faster and second run much smoother.

    Anyhow how fast you update the screen depends on the image size. If you are full screen you can only do about 30fps on most pcs if you have anything else going on in between screen updates. How big is your image?

    30 fps is about as fast as the eye can see anyway.

    But what it sounds like to me from your description is not a memory leak. How do you know there is a memory leak?

    Although pic2.Image.Dispose does not dispose the original image that you copied there by reference from the other thread it just disposes the reference.

    No it sounds like a whatucallit cascading recursive infinate loop. Sounds like your calls are piling up and your thread id updating faster than the screen is updating and gets stacked up? Why do you say memory leak have I missed that?

    Put a break in there and then look at the call history when the mb are going up and see if you have events stacked up.

    But the bottom line is what are you making and how fast does it need to run? Why use a background worker at all? Are you doing something on the main gui thread besides showing the screen? It sounds like you are just monitoring your process? Why use a bgw? Maybe a normal thread or async or something is better? Or even just a timer on the main gui thread?

    ANd yes I agree with Shaggy using Invalidate is better.

    Refresh will wait your thread until the screen updates. That is what is wrong?? Your refresh hoses the whole thing. Makes the events stack up. Thats why removing it makes it run better.

    How are you timing your fps? You should put a counter in the paint event (that what is refreshed?) and use that frame counter to see how fast it is really updating the screen. Is that how yours is set up? Then compare refresh with invalidate. Even single step debug if you can and watch where it goes may not do what you think. You may find the only thing running fast is your timer.

    Thoughts for discussion.

    PS if it comes to it instead of running 5 fps you could show every 3 frames and run at equivalent 15 fps etc? Does that make sense?
    Last edited by tommytwotrain; Mar 5th, 2019 at 09:17 AM.

  17. #17
    Powered By Medtronic dbasnett's Avatar
    Join Date
    Dec 2007
    Location
    Jefferson City, MO
    Posts
    9,754

    Re: [RESOLVED] Picturebox memory leak with .refresh() to redraw it.

    You should separate the receiving on the named pipe from the display of the image. In the example I posted I showed how to do that. In other words, one thread that accepts data from the pipe and another that shows the images.

    I'd also make that initial buffer size of the read much much larger. Something like this (NOT TESTED)

    Code:
        Private RcvdImg As New Threading.AutoResetEvent(False) 'set upon receipt
        Private QRcvd As New Concurrent.ConcurrentQueue(Of Bitmap) 'queue of Bitmap
        Private Sub StartServerPipe()
            Dim ServerPipe As New System.IO.Pipes.NamedPipeServerStream("From_FFmpeg")
    
            ServerPipe.WaitForConnection()
            ServerPipe.WaitForPipeDrain()
    
            Const InitBufSZ As Integer = 1024 * 1024 * 10
            Dim ImageLength As Integer = 0 ' create a reader
            Dim byteList As New List(Of Byte)(InitBufSZ) 'set capacity
    
            ' a continual listening loop
    
            Do
                Dim B(InitBufSZ - 1) As Byte
                Dim L As Integer = ServerPipe.Read(B, 0, InitBufSZ)
                If L > 0 Then
                    ReDim Preserve B(L - 1)
                    byteList.AddRange(B)
    
                    ' read the pipe until the next bitmap by getting the image length in bytes
                    ' read the three byte header to check if its a BM6 type bitmap
    
                    If B(1) = &H4D AndAlso B(0) = &H42 Then
                        ImageLength = BitConverter.ToInt32(B, 2)
                        Dim remaining As Integer = ImageLength - byteList.Count
                        Do While remaining > 0
                            L = ServerPipe.Read(B, 0, Math.Min(B.Length, remaining))
                            If L > 0 Then
                                ReDim Preserve B(L - 1)
                                byteList.AddRange(B)
                            End If
                            remaining = ImageLength - byteList.Count
                        Loop
    
                        Dim img As Byte() = byteList.ToArray()
                        Dim MS As New MemoryStream(img)
                        Dim Bmp As New Bitmap(MS)
                        QRcvd.Enqueue(Bmp)
                        RcvdImg.Set()
                        MS.Close()
                        MS.Dispose()
                        byteList.Clear()
                        img = Nothing
                    Else
                        ' nothing here yet
                    End If
    
                    ' reset/flush the byte list array
                Else
                    'output is not a valid Bitmap!
                    'Throw Exception
                End If
            Loop While ServerPipe.IsConnected
    
            ServerPipe.Close()
        End Sub
    
        Private Sub ShowRcvdImgs()
            Do
                RcvdImg.WaitOne()
                While QRcvd.Count > 0
                    Dim Bmp As Bitmap
                    If QRcvd.TryDequeue(Bmp) Then
                        ' cross thread to update the picturebox
                        Me.BeginInvoke(Sub()
                                           If pic1.Image IsNot Nothing Then
                                               pic1.Image.Dispose()
                                               pic1.Image = Nothing
                                           End If
                                           If Me.chkShowImages.Checked Then
                                               pic1.Image = Bmp
                                           End If
    
                                           If pic2.Image IsNot Nothing Then
                                               pic2.Image.Dispose()
                                               pic2.Image = Nothing
                                           End If
                                           pic2.Image = scanimage(Bmp)
                                           pic1.Invalidate()
                                           pic2.Invalidate()
                                       End Sub)
                        ' Threading.Thread.Sleep(15)'needed?
    
                    Else
                        RcvdImg.WaitOne(50) 'Dequeue failed
                    End If
                End While
            Loop
        End Sub
    Last edited by dbasnett; Mar 5th, 2019 at 10:31 AM.
    My First Computer -- Documentation Link (RT?M) -- Using the Debugger -- Prime Number Sieve
    Counting Bits -- Subnet Calculator -- UI Guidelines -- >> SerialPort Answer <<

    "Those who use Application.DoEvents have no idea what it does and those who know what it does never use it." John Wein

  18. #18

    Thread Starter
    Member
    Join Date
    Oct 2007
    Posts
    63

    Re: [RESOLVED] Picturebox memory leak with .refresh() to redraw it.

    Quote Originally Posted by tommytwotrain View Post
    "Im personally starting to think that 25fps to 2 pictureboxes is just too much and I should probably only draw at 4 of 5 fps "


    Are you using a windows form timer? You could use a separate thread with your own timer instead of the windows form timer. First off my bet is that you will run faster and second run much smoother.

    Anyhow how fast you update the screen depends on the image size. If you are full screen you can only do about 30fps on most pcs if you have anything else going on in between screen updates. How big is your image?

    30 fps is about as fast as the eye can see anyway.

    But what it sounds like to me from your description is not a memory leak. How do you know there is a memory leak?

    Although pic2.Image.Dispose does not dispose the original image that you copied there by reference from the other thread it just disposes the reference.

    No it sounds like a whatucallit cascading recursive infinate loop. Sounds like your calls are piling up and your thread id updating faster than the screen is updating and gets stacked up? Why do you say memory leak have I missed that?

    Put a break in there and then look at the call history when the mb are going up and see if you have events stacked up.

    But the bottom line is what are you making and how fast does it need to run? Why use a background worker at all? Are you doing something on the main gui thread besides showing the screen? It sounds like you are just monitoring your process? Why use a bgw? Maybe a normal thread or async or something is better? Or even just a timer on the main gui thread?

    ANd yes I agree with Shaggy using Invalidate is better.

    Refresh will wait your thread until the screen updates. That is what is wrong?? Your refresh hoses the whole thing. Makes the events stack up. Thats why removing it makes it run better.

    How are you timing your fps? You should put a counter in the paint event (that what is refreshed?) and use that frame counter to see how fast it is really updating the screen. Is that how yours is set up? Then compare refresh with invalidate. Even single step debug if you can and watch where it goes may not do what you think. You may find the only thing running fast is your timer.

    Thoughts for discussion.

    PS if it comes to it instead of running 5 fps you could show every 3 frames and run at equivalent 15 fps etc? Does that make sense?


    Im still learning all this, its all new to me.

    I had a memory leak when I was trying to update the pictureboxes. Both dbassnet and ident showed me a few things which I used and stopped the memory leak.

    And yes showing every third frame makes sense. The image are 720 x 576.

  19. #19

    Thread Starter
    Member
    Join Date
    Oct 2007
    Posts
    63

    Re: [RESOLVED] Picturebox memory leak with .refresh() to redraw it.

    Quote Originally Posted by passel View Post
    Since you're receiving your data and creating the images from a background thread, you may want to use a BufferedGraphics object so that you can fill an area of the screen associated with a control quicker and can do it from the background thread so no need to invoke back to the GUI thread.
    A BufferedGraphics object is made for the situation where you want to update the full image of an area of the screen repeatedly.

    An example here.
    THanks for the pointer passel, I will look at this as soon as I have time - my next few days are really busy so no time to look just yet

  20. #20

    Thread Starter
    Member
    Join Date
    Oct 2007
    Posts
    63

    Re: [RESOLVED] Picturebox memory leak with .refresh() to redraw it.

    Quote Originally Posted by dbasnett View Post
    You should separate the receiving on the named pipe from the display of the image. In the example I posted I showed how to do that. In other words, one thread that accepts data from the pipe and another that shows the images.

    I'd also make that initial buffer size of the read much much larger. Something like this (NOT TESTED)

    Code:
        Private RcvdImg As New Threading.AutoResetEvent(False) 'set upon receipt
        Private QRcvd As New Concurrent.ConcurrentQueue(Of Bitmap) 'queue of Bitmap
        Private Sub StartServerPipe()
            Dim ServerPipe As New System.IO.Pipes.NamedPipeServerStream("From_FFmpeg")
    
            ServerPipe.WaitForConnection()
            ServerPipe.WaitForPipeDrain()
    
            Const InitBufSZ As Integer = 1024 * 1024 * 10
            Dim ImageLength As Integer = 0 ' create a reader
            Dim byteList As New List(Of Byte)(InitBufSZ) 'set capacity
    
            ' a continual listening loop
    
            Do
                Dim B(InitBufSZ - 1) As Byte
                Dim L As Integer = ServerPipe.Read(B, 0, InitBufSZ)
                If L > 0 Then
                    ReDim Preserve B(L - 1)
                    byteList.AddRange(B)
    
                    ' read the pipe until the next bitmap by getting the image length in bytes
                    ' read the three byte header to check if its a BM6 type bitmap
    
                    If B(1) = &H4D AndAlso B(0) = &H42 Then
                        ImageLength = BitConverter.ToInt32(B, 2)
                        Dim remaining As Integer = ImageLength - byteList.Count
                        Do While remaining > 0
                            L = ServerPipe.Read(B, 0, Math.Min(B.Length, remaining))
                            If L > 0 Then
                                ReDim Preserve B(L - 1)
                                byteList.AddRange(B)
                            End If
                            remaining = ImageLength - byteList.Count
                        Loop
    
                        Dim img As Byte() = byteList.ToArray()
                        Dim MS As New MemoryStream(img)
                        Dim Bmp As New Bitmap(MS)
                        QRcvd.Enqueue(Bmp)
                        RcvdImg.Set()
                        MS.Close()
                        MS.Dispose()
                        byteList.Clear()
                        img = Nothing
                    Else
                        ' nothing here yet
                    End If
    
                    ' reset/flush the byte list array
                Else
                    'output is not a valid Bitmap!
                    'Throw Exception
                End If
            Loop While ServerPipe.IsConnected
    
            ServerPipe.Close()
        End Sub
    
        Private Sub ShowRcvdImgs()
            Do
                RcvdImg.WaitOne()
                While QRcvd.Count > 0
                    Dim Bmp As Bitmap
                    If QRcvd.TryDequeue(Bmp) Then
                        ' cross thread to update the picturebox
                        Me.BeginInvoke(Sub()
                                           If pic1.Image IsNot Nothing Then
                                               pic1.Image.Dispose()
                                               pic1.Image = Nothing
                                           End If
                                           If Me.chkShowImages.Checked Then
                                               pic1.Image = Bmp
                                           End If
    
                                           If pic2.Image IsNot Nothing Then
                                               pic2.Image.Dispose()
                                               pic2.Image = Nothing
                                           End If
                                           pic2.Image = scanimage(Bmp)
                                           pic1.Invalidate()
                                           pic2.Invalidate()
                                       End Sub)
                        ' Threading.Thread.Sleep(15)'needed?
    
                    Else
                        RcvdImg.WaitOne(50) 'Dequeue failed
                    End If
                End While
            Loop
        End Sub
    Thanks for doing this, as stated in another response I dont have much time at the moment and will get to look at this properly in a few days. I don't want to just copy it, I want to know whats happening.

    I appears at a glance that you are queing the images in a collection to show them on another thread .... If i read it correctly then I really like this idea.

    Many thanks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  



Click Here to Expand Forum to Full Width