Ошибка «Недостаточно памяти» при захвате потока с камеры

Я был измотан этой ошибкой за последние 2 недели. Я много пытался выяснить и пробовал код по-разному, но пока не преуспел. Я думаю, что основная проблема связана с растровым изображением, может быть, я использую его неправильно. Я делюсь своим кодом, чтобы помочь понять, что я делаю.

Сначала я расскажу вам сценарий. В этом приложении я использую камеру dslr для просмотра в реальном времени. Основная область кода из класса камеры приведена ниже:

internal void Run()
{
    LVrunning = true;
    while (LVrunning)
    {
        Thread.Sleep(20);
        if (LVrunning)
            UpdatePicture();
    }
}

private void UpdatePicture()
{
    try
    {
        if (err == EDSDK.EDS_ERR_OK && LVrunning)
        {
            inSide = true;

            // Download live view image data
            err = EDSDK.EdsDownloadEvfImage(cameraDev, EvfImageRef);

            if (err != EDSDK.EDS_ERR_OK)
            {
                Debug.WriteLine(String.Format("Download of Evf Image: {0:X}", err));
                return;
            }
            IntPtr ipData;
            err = EDSDK.EdsGetPointer(MemStreamRef, out ipData);
            if (err != EDSDK.EDS_ERR_OK)
            {
                Debug.WriteLine(String.Format("EdsGetPointer failed: {0:X}", err));
                return;
            }

            uint len;
            err = EDSDK.EdsGetLength(MemStreamRef, out len);
            if (err != EDSDK.EDS_ERR_OK)
            {
                Debug.WriteLine(String.Format("EdsGetLength failed:{0:X}", err));
                EDSDK.EdsRelease(ipData);
                return;
            }

            Byte[] data = new byte[len];
            Marshal.Copy(ipData, data, 0, (int)len);
            System.IO.MemoryStream memStream = new System.IO.MemoryStream(data);

            // get the bitmap
            Bitmap bitmap = null;
            try
            {
                bitmap = new Bitmap(memStream);
            }
            catch (OutOfMemoryException ex)
            {
                GC.WaitForPendingFinalizers();
                bitmap = new Bitmap(memStream); // sometimes error occur
            }

            NewFrame(bitmap, null); // this is event call back to form area.

            memStream.Dispose();
            EDSDK.EdsRelease(ipData);
        }
    }
    catch (Exception ex)
    {

    }
}
private void getCapturedItem(IntPtr directoryItem)
{
    uint err = EDSDK.EDS_ERR_OK;
    IntPtr stream = IntPtr.Zero;

    EDSDK.EdsDirectoryItemInfo dirItemInfo;

    err = EDSDK.EdsGetDirectoryItemInfo(directoryItem, out dirItemInfo);

    if (err != EDSDK.EDS_ERR_OK)
    {
        throw new CameraException("Unable to get captured item info!", err);
    }

    //  Fill the stream with the resulting image
    if (err == EDSDK.EDS_ERR_OK)
    {
        err = EDSDK.EdsCreateMemoryStream((uint)dirItemInfo.Size, out stream);
    }

    //  Copy the stream to a byte[] and
    if (err == EDSDK.EDS_ERR_OK)
    {
        err = EDSDK.EdsDownload(directoryItem, (uint)dirItemInfo.Size, stream);
    }

    //  Create the returned item
    //CapturedItem item = new CapturedItem();
    if (dirItemInfo.szFileName.ToString().ToLower().Contains("jpg") || dirItemInfo.szFileName.ToString().ToLower().Contains("jpeg"))
    {
        if (err == EDSDK.EDS_ERR_OK)
        {
            IntPtr imageRef = IntPtr.Zero;

            err = EDSDK.EdsCreateImageRef(stream, out imageRef);

            if (err == EDSDK.EDS_ERR_OK)
            {
                EDSDK.EdsImageInfo info;
                err = EDSDK.EdsGetImageInfo(imageRef, EDSDK.EdsImageSource.FullView, out info);
            }
        }
    }

    if (err == EDSDK.EDS_ERR_OK)
    {
        try
        {
            byte[] buffer = new byte[(int)dirItemInfo.Size];
            GCHandle gcHandle = GCHandle.Alloc(buffer, GCHandleType.Pinned);
            IntPtr address = gcHandle.AddrOfPinnedObject();
            IntPtr streamPtr = IntPtr.Zero;
            err = EDSDK.EdsGetPointer(stream, out streamPtr);
            if (err != EDSDK.EDS_ERR_OK)
            {
                throw new CameraDownloadException("Unable to get resultant image.", err);
            }

            try
            {
                Marshal.Copy(streamPtr, buffer, 0, (int)dirItemInfo.Size);//sometimes error comes here
                System.IO.MemoryStream memStream = new System.IO.MemoryStream(buffer);

                    Bitmap bitmap = null;
                    try
                    {
                        bitmap = new Bitmap(memStream);
                    }
                    catch (OutOfMemoryException ex)
                    {
                        GC.WaitForPendingFinalizers();
                        Bitmap b = new Bitmap(memStream);//sometimes error comes here
                    }

                    if (bitmap != null)
                    {


                            PhotoCaptured(bitmap, null);

                    }

            }
            catch (AccessViolationException ave)
            {
                throw new CameraDownloadException("Error copying unmanaged stream to managed byte[].", ave);
            }
            finally
            {
                gcHandle.Free();
                EDSDK.EdsRelease(stream);
                EDSDK.EdsRelease(streamPtr);
            }
        }
        catch (OutOfMemoryException ex)
        {
            GC.WaitForPendingFinalizers();
            IboothmeObject.minimizeMemory();
            getCapturedItem(directoryItem);
        }
    }
    else
    {
        throw new CameraDownloadException("Unable to get resultant image.", err);
    }
}

На стороне формы изображение просто обновляется в окне изображения

private void StartLiveView()
    {
        if (this.liveView.Connected)
        {
            this.liveView.PhotoCaptured += new EventHandler(liveView_PhotoCaptured);
            this.liveView.NewFrame += new EventHandler(liveView_NewFrame);
            this.liveView.StartLiveView();

        }
    }

    void liveView_NewFrame(object sender, EventArgs e)
    {
        this.picMain.Image = sender as Image;
    }
    void liveView_PhotoCaptured(object sender, EventArgs e)
    {
        Image img = sender as Image;
        // this image is big in size like 5000x3000.
        Bitmap tempbitmap = new Bitmap(img.Width, img.Height);// now mostly error comes here
        tempbitmap.SetResolution(img.HorizontalResolution, img.VerticalResolution);
            using (Graphics g = Graphics.FromImage(tempbitmap))
            {
                g.DrawImage(img, new Rectangle(0, 0, img.Width, img.Height));
                g.Save();
            }
        picMain.Image = tempbitmap;
        tempbitmap.Save(path,ImageFormat.Jpeg);
    }

Еще одна область кода, которая использует растровое изображение и просмотр в реальном времени с камеры. Этот код получает кадр с камеры и записывает некоторые объекты в кадр. В моем случае я пишу несколько шариков на кадре.

void liveView_NewFrame(object sender, EventArgs e)
    {
        using (Image<Bgr, byte> Frame = new Image<Bgr, byte>(new Bitmap(sender as Image)))
        {
            Frame._SmoothGaussian(3);
            IntPtr hsvImage = CvInvoke.cvCreateImage(CvInvoke.cvGetSize(Frame), Emgu.CV.CvEnum.IPL_DEPTH.IPL_DEPTH_8U, 3);
            CvInvoke.cvCvtColor(Frame, hsvImage, Emgu.CV.CvEnum.COLOR_CONVERSION.CV_BGR2HSV);

            Image<Gray, byte> imgThresh = new Image<Gray, byte>(Frame.Size);
            imgThresh.Ptr = GetThresholdedImage(hsvImage);
            //CvInvoke.cvSmooth(imgThresh, imgThresh, Emgu.CV.CvEnum.SMOOTH_TYPE.CV_GAUSSIAN, 3, 3, 3, 3);

            #region Draw the contours of difference
            //this is tasken from the ShapeDetection Example
            Rectangle largest = new Rectangle();
            try
            {
                using (MemStorage storage = new MemStorage()) //allocate storage for contour approximation
                    //detect the contours and loop through each of them
                    for (Contour<Point> contours = imgThresh.Convert<Gray, Byte>().FindContours(
                          Emgu.CV.CvEnum.CHAIN_APPROX_METHOD.CV_CHAIN_APPROX_SIMPLE,
                          Emgu.CV.CvEnum.RETR_TYPE.CV_RETR_EXTERNAL,
                          storage);
                       contours != null;
                       contours = contours.HNext)
                    {
                        //Create a contour for the current variable for us to work with
                        Contour<Point> currentContour = contours.ApproxPoly(contours.Perimeter * 0.05, storage);

                        //Draw the detected contour on the image
                        if (currentContour.Area > ContourThresh) //only consider contours with area greater than 100 as default then take from form control
                        {
                            if (currentContour.BoundingRectangle.Width > largest.Width && currentContour.BoundingRectangle.Height > largest.Height)
                            {
                                largest = currentContour.BoundingRectangle;
                            }
                        }
                        //storage.Dispose();
                    }

            }
            catch (Exception)
            {

            }

            #endregion

            #region Draw Object
            Random r = new Random();
            //Bitmap bb = Frame.Bitmap;
            foreach (var item in objectList)
            {
                using (Graphics g = Graphics.FromImage(Frame.Bitmap))
                {
                    if (DrawAble(item, largest))
                    {
                        if (item.Y < 0)
                        {
                            if (item.X < picMain.Width)
                            {
                                g.DrawImage(item.image, new Rectangle(item.X, 0, item.image.Width, item.image.Height + item.Y),
                                    new Rectangle(), GraphicsUnit.Pixel);
                                item.X += r.Next(-5, 5);
                                item.Y += 15;
                            }
                        }
                        else
                        {
                            if (item.X < picMain.Width && item.Y < picMain.Height)
                            {
                                g.DrawImage(item.image, new Rectangle(item.X, item.Y, item.image.Width, item.image.Height));
                                item.X += r.Next(-5, 5);
                                item.Y += 15;
                            }
                            else
                            {
                                item.X = r.Next(0, picMain.Width - 5);
                                item.Y = r.Next(-item.image.Height, -5);
                            }

                        }
                    }
                    else
                    {
                        item.X = r.Next(0, picMain.Width - 5);
                        item.Y = r.Next(-item.image.Height, -5);
                    }

                }
            }

            #endregion

            picMain.Image = Frame.ToBitmap();

        }

        minimizeMemory();
    }

Теперь я подробно расскажу о всей проблеме. Прежде всего, я создал форму для просмотра в реальном времени и, используя библиотеку opencv (Emgu), рисую воздушные шары на кадре. В режиме реального времени эти воздушные шары движутся. Другая форма предназначена для захвата изображения с камеры с высоким разрешением. Я заметил, что память моего приложения увеличивалась с каждым кадром, и после 2 просмотров в реальном времени и захвата 2 изображений она достигает 1+ ГБ. Если я попытался снова показать первую форму для просмотра в реальном времени, в функции UpdatePicture() произошла ошибка. затем я добавляю код, чтобы минимизировать память текущего приложения. Теперь я вызываю эту функцию после каждого кадра в режиме реального времени. После этого решения, когда я проверил память приложения, она не превышала 100 МБ или 200 МБ. Но проблема осталась. После нескольких захватов возникла ошибка в UpdatePicture() при получении растрового изображения из потока ( растровое изображение = новое растровое изображение (memStream);). Ошибка была такой же из-за нехватки памяти. После некоторого поиска я нашел это решение.

// get the bitmap
        Bitmap bitmap = null;
        try
        {
            bitmap = new Bitmap(memStream);
        }
        catch (OutOfMemoryException ex)
        {
            GC.WaitForPendingFinalizers();
            bitmap = new Bitmap(memStream);
        }

Но не рабочая ошибка все та же.

Ошибка иногда отображалась в методе UpdatePicture(), иногда возникала в методе liveView_NewFrame. Означает, что проблема связана с растровым изображением, размером растрового изображения или повреждением памяти. Поэтому, пожалуйста, помогите мне. Я беспокоюсь, прошло 2 недели, но я не мог решить эту проблему.


person t4taurus    schedule 10.07.2013    source источник
comment
Это очень много кода, который мы должны отлаживать. И к тому времени, когда OutOfMemoryException будет брошен, уже слишком поздно думать о сборщике мусора — он уже сделал все возможное и потерпел неудачу. Каждый раз, когда вы пишете код, который вызывает класс GC (кроме KeepAlive), вы, вероятно, делаете что-то не так.   -  person Damien_The_Unbeliever    schedule 10.07.2013
comment
Вместо того, чтобы ожидать, что мы отладим этот код, вам нужно зафиксировать дамп процесса при возникновении исключения OOM, а затем выполнить поиск утечки памяти .NET для получения подсказок и советов по изучению дампа и поиску проблемы. Тесс Феррандес уже писала об этом несколько хороших постов в блоге.   -  person Damien_The_Unbeliever    schedule 10.07.2013
comment
Попробуйте вызвать dispose для растровых изображений, когда вы закончите с ними, может немного помочь.   -  person Pharap    schedule 10.07.2013
comment
ок попробую узнать.   -  person t4taurus    schedule 10.07.2013


Ответы (1)


вы вызываете CvInvoke.cvCreateImage созданные данные находятся в собственной куче
, поэтому сборщик мусора не будет их собирать.
вы должны вызвать cvReleaseImage(IntPtr), чтобы освободить данные

есть много профилировщиков памяти, которые могут помочь понять проблему

попробуйте использовать профилировщик памяти ANTS 7.

person makc    schedule 10.07.2013
comment
спасибо makc, я удалил ошибку из этой области. Без этого удаления указателя память увеличивалась и увеличивалась. Но после этого я решаю эту проблему. Но у меня все еще есть ошибка в другой точке приложения. Теперь я пытаюсь для правильной проверки объектов Bitmap. - person t4taurus; 18.07.2013
comment
@ t4taurus Я рад, что смог помочь :), попробуйте использовать любой профилировщик памяти, это поможет выяснить проблему. - person makc; 18.07.2013