IBCBiomech / insolesibc

Prototipo de aplicación para plantillas
0 stars 1 forks source link

Investigacion GC #50

Open bernatDiaz opened 1 year ago

bernatDiaz commented 1 year ago

el codigo puede producir un deadlock: https://stackoverflow.com/questions/12265598/is-correct-to-use-gc-collect-gc-waitforpendingfinalizers

bernatDiaz commented 1 year ago

Llamar al GC solo una vez https://stackoverflow.com/questions/38000442/does-the-garbage-collector-benefit-from-multiple-calls-to-collect-and-waitforpen

 //Force garbage collection.
   GC.Collect();

   // Wait for all finalizers to complete before continuing.
   // Without this call to GC.WaitForPendingFinalizers, 
   // the worker loop below might execute at the same time 
   // as the finalizers.
   // With this call, the worker loop executes only after
   // all finalizers have been called.
   GC.WaitForPendingFinalizers();
ivanjimenez commented 1 year ago

Está claro que el GC al llamarlo de manera manual y varias veces iba a repercutir sobre el rendimiento, lo cual lo hace no usable en esta situación.

Mira y escribe la issue para EMguCV, pues debe haber algún workarround para gestionar la memoria dispoable sin estar llamando al GC. Comenta el problema que ocurre, que no es en todos los PCs. A ver qué contestan.

bernatDiaz commented 1 year ago

ok

ivanjimenez commented 1 year ago

Mira esto de la ayuda de EmguCV: https://emgu.com/wiki/index.php/Working_with_Images

Automatic Garbage Collection
The Image<[TColor](https://emgu.com/wiki/index.php/Working_with_Images#Image_Color), [TDepth](https://emgu.com/wiki/index.php/Working_with_Images#Image_Depth)> class automatically take care of the memory management and garbage collection.

Once the garbage collector decided that there is no more reference to the Image<[TColor](https://emgu.com/wiki/index.php/Working_with_Images#Image_Color), [TDepth](https://emgu.com/wiki/index.php/Working_with_Images#Image_Depth)> object, it will call the Disposed method, which release the unmanaged IplImage structure.

The time of when garbage collector decides to dispose the image is not guaranteed. When working with large image, it is recommend to call the Dispose() method to explicitly release the object. Alternatively, use the using keyword in C# to limit the scope of the image

using (Image<Gray, Single> image = new Image<Gray, Single>(1000, 800))
{
   ... //do something here in the image
} //The image will be disposed here and memory freed

Se podría hacer algo como:

public static class IImageExtensions
{
    // http://stackoverflow.com/questions/20825497/difference-between-using-and-dispose-call-in-c-sharp 
    public static void FreeMem(this IImage image)
    {
        using (image)
        {
            using (image.Bitmap)
            {

            }
        }
        //image.Bitmap.Dispose();
        //image.Dispose();
    }
}

Son snippets para ver cómo encajarían para usar las imágenes con EmguCV. Yo creo que realmente el problema que EmguCV/Opencv hace un dispose penoso, lo dicen en los stackoverflow.

Revisa lo anterior y si tienes dudas de cómo acometerlo lo conversamos mañana.

ivanjimenez commented 1 year ago

Puedes usar el profiling de la aplicación, se puede ver la gestión de threads, ram, cpu, gpu, etc. Pero creo que el asunto es más del buen uso del dispose de EmguCV como pongo arriba.

Image

bernatDiaz commented 1 year ago

Me han respondido de EmguCV https://github.com/emgucv/emgucv/issues/877#issuecomment-1715649657

ivanjimenez commented 1 year ago

Por qué no lo haces con using Image como viene arriba. El using debe venir con dispose automáticamente, es como un fichero o un streaming que lo controla el propio código.

ivanjimenez commented 1 year ago

De todas formas en el código de la aplicación no haces un dispose del CurrentTopFrame

bernatDiaz commented 1 year ago

BitmapSource no es Disposable

ivanjimenez commented 1 year ago

El código de arriba comenta la parte de dispose del bitmap, más bien se utiliza con el bloque using

using (Image<Gray, Single> image = new Image<Gray, Single>(1000, 800))
{
   ... //do something here in the image
} //The

A lo que me refiero que puedes hacer alguna colección que lleve la imagen y luego hacer using. Automáticamente llama al dispose

bernatDiaz commented 1 year ago
Application.Current.Dispatcher.BeginInvoke(() =>
{
    using (frame)
    {
        CurrentFrameTop = FormatConversions.ToBitmapSource(frame);
    }
});

Así? esto se cuelga

ivanjimenez commented 1 year ago

Es posible utilizar un método asíncrono para obtener el frame propio de EmguCV y no poner el dispatcher de la aplicación a funcionar?

Mira el método retrieve u otros que tenga EmguCV, seguro que tiene alguno asíncrono.

bernatDiaz commented 1 year ago

El Emgu tiene un metodo ToBitmapSource y estaba usando uno que saque de internet, tal vez sea eso?

public static BitmapSource ToBitmapSource(Mat mat)
{
    using (var stream = new System.IO.MemoryStream())
    {
        mat.ToBitmap().Save(stream, System.Drawing.Imaging.ImageFormat.Bmp);
        stream.Position = 0;

        var bitmapImage = new BitmapImage();
        bitmapImage.BeginInit();
        bitmapImage.StreamSource = stream;
        bitmapImage.CacheOption = BitmapCacheOption.OnLoad;
        bitmapImage.EndInit();
        bitmapImage.Freeze();

        return bitmapImage;
    }
}
ivanjimenez commented 1 year ago

Puede ser que no sea el método correcto el que estés usando de internet. Mira mejor el manual de emgucv incluso pregúntales para usar el código. A veces internet tiene código que es poco eficiente u obsoleto. Eso de arriba parece que tiene mejor pinta.

bernatDiaz commented 1 year ago

No, eso es lo que estaba usando. El de emgucv es frame.ToBitmapSource();

ivanjimenez commented 1 year ago

Para revisar solución:

https://stackoverflow.com/questions/21877221/memory-not-getting-released-in-wpf-image

bernatDiaz commented 1 year ago

stopwatch 60fps

127,7151
160,1167
192,2349
223,5168
255,8391
304,458
338,2991
367,8774
399,4349
434,2547
463,6302
498,3681
527,7854
560,0384
591,8802
639,5253
671,7268
703,7438
735,5553
767,47
799,4564
831,3799
863,7236
895,7599
930,2266
962,1631
1008,6382

stopwatch 30fps

134,4174
163,5138
195,383
227,6463
275,722
307,9169
339,3227
371,308
403,367
435,8012
467,3946
499,8222
532,2595
564,1917
611,413
643,6991
675,7531
707,6966
739,8192
771,9137
803,2873
835,379
867,367
899,9036
931,8139
980,4372
1011,2938
bernatDiaz commented 1 year ago

Se settea bien la variable pero ImageGrabbedCallback se sigue llamando con la misma frequencia con 30 fps que con 60 fps

ivanjimenez commented 1 year ago

Pero los fps se setean antes. Posiblemente si desuscribes (-) y vuelves a suscribir ese método (+)

bernatDiaz commented 1 year ago

primero lo seteo y despues me suscribo

videoCapture = new VideoCapture(index, VideoCapture.API.DShow);
videoCapture.Set(CapProp.Fps, fps);
//videoCapture.Set(CapProp.FrameHeight, resolution.Height);
//videoCapture.Set(CapProp.FrameWidth, resolution.Width);
videoCapture.Set(CapProp.Autofocus, 39);
videoCapture.Set(CapProp.Bitrate, 20000);
//videoCapture.Set(CapProp.FourCC, VideoWriter.Fourcc('H', '2', '6', '4')); // Con esto no funciona
//videoCapture.Set(CapProp.FourCC, VideoWriter.Fourcc('D', 'I', 'V', 'X'));

videoCapture.ImageGrabbed += ImageGrabbedCallback;
videoCapture.Start();
ivanjimenez commented 1 year ago

Por eso digo que primero se desuscriba -= y luego suscribirse += a ver si el callback coge los fps

bernatDiaz commented 1 year ago

pero es en el constructor, antes no estaba suscrito

ivanjimenez commented 1 year ago

El callback se llama, pero no sé si por alguna razón ese callback se llama sin los fps que seteas por eso lo digo.

bernatDiaz commented 1 year ago

https://github.com/emgucv/emgucv/issues/880