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 шт):
Изменения в глобальных переменных - объявление 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);
}
}
Так никаких ошибок больше не возникает.