C# / .NET4.7.2. Как вывести составную картинку из 3-х Bitmap-ов в pictureBox в процессе видеозахвата с web-камеры. (Использую библиотеку AForge.)

Видимо, что-то не правильно делаю. Не судите строго, я далеко не профи.

Если вкратце: нужно соединить 3 битмапа в один, вывести результат в PictureBox. Одна из картинок - кадр из web-камеры. Соответственно соединять нужно в событии по кадру, каждый раз.

Сначала использовал библиотеку Emgu, сейчас AForge, но всякий раз вылетают критические ошибки. Иногда программа запускается и какое-то время работает, иногда даже долго, а иногда еще до запуска ошибка. На данный момент такая: "System.ArgumentException" в System.Drawing.dll Недопустимый параметр Подозреваю, что возникают конфликтные ситуации, т.к. в программе используется таймер (), событие Form_Paint, и, собственно, событие кадра с web-камеры NewFrameEventArgs, в котором и производится слияние всех Bitmap и вывод в PictureBox. В глобальных переменных находится большой массив с картинками, инициализируются они в процессе работы программы. Сами картинки хранятся в отдельной папке в каталоге проекта, вызываются по имени (массив имен типа String) из ресурсов, где они все прикреплены. Весь код пару тысяч строк, привожу основные моменты. Глобальные переменные:

    //  Глобальные переменные (для WebCam)
    private FilterInfoCollection VideoCaptureDevices;
    private VideoCaptureDevice FinalVideo;
    //  картинки
    Bitmap[] arrBmpWebCam = new Bitmap[1023];
    //  массив имен картинок из ресурсов для картинок
    string[] strFileNames_2 = new string[1024] {
        "_01x", "_02x", "_03x".......};
    //  индекс для массива (выше). Принимает разные значения по таймеру
    int ImgIndex = 0;
    //  Битмапы для наложения картинок
    //  не изменяющаяся картинка
    Bitmap maskBmp = new Bitmap(Properties.Resources._MaskAur, DB.pb_WebCam_Size);
    //  картинка кадр из web-камеры
    Bitmap baseBmp;
    //  пустой битмап для склейки всех картинок и вывода в pictureBox
    Bitmap WebCam_rezultBmp = new Bitmap(DB.pb_WebCam_Size.Width, DB.pb_WebCam_Size.Height,
                PixelFormat.Format32bppArgb);

В таймере меняется значение ImgIndex (индекс массива имен картинок из ресурсов):

    private void timer_Interval_Tick(object sender, EventArgs e) { ImgIndex = x; // постоянно изменяющееся значение}

Главный виновник ошибок - событие по кадру:

    private void FinalVideo_NewFrame(object sender, NewFrameEventArgs eventArgs)
    {
        //  сборка мусора
        if (pb_WebCam.Image != null) pb_WebCam.Image.Dispose();
          
        try
        {
            //  запись картинки в массив из ресурсов по индексу
            arrBmpWebCam[ImgIndex] = new Bitmap((Bitmap)Properties.Resources.ResourceManager.GetObject(strFileNames_2[ImgIndex]), pb_WebCam.Size);
            //  запись картинки кадра из камеры
            baseBmp = new Bitmap((Bitmap)eventArgs.Frame.Clone(), pb_WebCam.Size);
            //  слияние 3-х картинок
            var graphics = Graphics.FromImage(WebCam_rezultBmp);
            graphics.CompositingMode = CompositingMode.SourceOver;

            graphics.DrawImage(baseBmp, 0, 0);  //  кадр из камеры
            graphics.DrawImage(maskBmp, 0, 0);  //  картинка, инициализированная в глобальных переменных
            graphics.DrawImage((Bitmap)SetImageOpacity(arrBmpWebCam[ImgIndex], 0.3f), 0, 0);  //  картинка из
            //ресурсов (используется функция, увеличивающая прозрачность)
            //  вывод в PictureBox
            pb_WebCam.Image = WebCam_rezultBmp;
        }
        catch (Exception ex)
        {
            MessageBox.Show(ex.Message, "Frame_Error!", MessageBoxButtons.OK, MessageBoxIcon.Error);
        }
    }

Код функции, увеличивающей прозрачность (взял из интернета, источник уже не помню. Ф-ция рабочая):

        public Image SetImageOpacity(Image image, float opacity)
        {
            try
            {
                //create a Bitmap the size of the image provided  
                Bitmap bmp = new Bitmap(image.Width, image.Height);
                //create a graphics object from the image  
                using (Graphics gfx = Graphics.FromImage(bmp))
                {
                    //create a color matrix object  
                    ColorMatrix matrix = new ColorMatrix();
                    //set the opacity  
                    matrix.Matrix33 = opacity;
                    //create image attributes  
                    ImageAttributes attributes = new ImageAttributes();
                    //set the color(opacity) of the image  
                    attributes.SetColorMatrix(matrix, ColorMatrixFlag.Default, ColorAdjustType.Bitmap);
                    //now draw the image  
                    gfx.DrawImage(image, new Rectangle(0, 0, bmp.Width, bmp.Height), 0, 0, image.Width, image.Height, GraphicsUnit.Pixel, attributes);
                }
                return bmp;
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
                return null;
            }
        }

Если в коде события по кадру оставить следующий код, то все прекрасно работает, ошибок нет:

    Bitmap img = (Bitmap)eventArgs.Frame.Clone();
    pb_WebCam.Image = img;

Но мне нужно совместить 3 картинки. Что я делаю не так?


Ответы (1 шт):

Автор решения: Faust

Изменения в глобальных переменных - объявление Bitmap-ов без инициализации:

    //  Глобальные переменные (для WebCam)
    //  не изменяющаяся картинка
    Bitmap maskBmp;
    //  картинка кадр из web-камеры
    Bitmap baseBmp;
    //  пустой битмап для склейки всех картинок и вывода в pictureBox
    Bitmap WebCam_rezultBmp;

Изменения в событии по кадру - убрал строку if (pb_WebCam.Image != null) pb_WebCam.Image.Dispose();, вместо eventArgs.Frame.Clone() - просто eventArgs.Frame, добавил буферную локальную переменную, принимающую кадр Bitmap frameBmp = eventArgs.Frame;, всем Bitmap-ам присваиваются значения в данном событии, при объединении 3-х Bitmap-ов baseBmp, maskBmp и arrBmpWebCam[auraImgIndex] в один WebCam_rezultBmp, а так же при присвоении картинки PictureBox-у pb_WebCam добавлены проверки на != null:

    private void FinalVideo_NewFrame(object sender, NewFrameEventArgs eventArgs)
        {
            try
            {
                arrBmpWebCam[ImgIndex] = new Bitmap((Bitmap)Properties.Resources.ResourceManager.
                    GetObject(strFileNames_2[ImgIndex]), DB.pb_WebCam_Size);

                Bitmap frameBmp = eventArgs.Frame;

                baseBmp = new Bitmap(frameBmp, DB.pb_WebCam_Size);

                maskBmp = new Bitmap(Properties.Resources._Mask, DB.pb_WebCam_Size);

                WebCam_rezultBmp = new Bitmap(DB.pb_WebCam_Size.Width, DB.pb_WebCam_Size.Height,
                            PixelFormat.Format32bppArgb);

                var graphics = Graphics.FromImage(WebCam_rezultBmp);
                graphics.CompositingMode = CompositingMode.SourceOver;

                if (baseBmp != null)
                    graphics.DrawImage(baseBmp, 0, 0);

                if (maskBmp != null)
                    graphics.DrawImage(maskBmp, 0, 0);

                if (arrBmpWebCam[ImgIndex] != null)
                    graphics.DrawImage((Bitmap)SetImageOpacity(arrBmpWebCam[ImgIndex], 0.3f), 0, 0);

                if (WebCam_rezultBmp != null)
                    pb_WebCam.Image = WebCam_rezultBmp;
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message, "Frame_Error!", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }

Так никаких ошибок больше не возникает.

→ Ссылка