#c# #.net #paint #picturebox #emgucv
#c# #.net #Краски #picturebox #emgucv
Вопрос:
итак, я написал приложение Windows Form на C #, чтобы открывать видео, отображать видео в pictureBox_display и позволять пользователям определять (рисовать) ограничивающие рамки на picturebox. Единственное, что я могу очень предсказуемо заставить программу сбросить Систему.AccessViolationException и сбой, просто: 1) открываем видео (любое видео) 2) щелкаем перетаскиваем мышь на picturebox в течение нескольких секунд.
Вот некоторый код:
VideoCapture VC;
bool IsMouseDown = false;
bool MouseMoved = false;
string VideoFileName;
private void OpenFileButton_Click(object sender, EventArgs e) {
OpenFileDialog OFD = new OpenFileDialog();
if (OFD.ShowDialog() == DialogResult.OK) {
FrameNumber = 0;
VideoFileName = OFD.FileName;
VC = new VideoCapture(VideoFileName);
if (VC.Width<=0 || VC.Height<=0) {
System.Windows.Forms.MessageBox.Show("ERROR: Please load a valid video file");
return;
}
// Initialize picturebox dimensions according to image
double W_H_ratio = Convert.ToDouble(VC.Width) / VC.Height;
if (W_H_ratio >= (double)(5 / 4)) {
pictureBox_display.Width = Math.Min(VC.Width, 800);
pictureBox_display.Height = Math.Min(VC.Height, (int)((1 / W_H_ratio )* 800));
}
else {
pictureBox_display.Height = Math.Min(VC.Height, 640);
pictureBox_display.Width = Math.Min(VC.Width, (int)(W_H_ratio * 640));
}
Mat tempimg = GetMat(FrameNumber);
Console.WriteLine("Size of tempimg: " tempimg.Size);
pictureBox_display.Image = tempimg.Bitmap;
}
}
private Mat GetMat(int someFrameNumber) {
try {
Mat m = new Mat();
VC.SetCaptureProperty(Emgu.CV.CvEnum.CapProp.PosFrames, someFrameNumber);
VC.Read(m);
Mat resized = new Mat();
CvInvoke.ResizeForFrame(m, resized, pictureBox_display.Size);
m.Dispose();
return resized;
}
catch {
Mat m = new Mat();
System.Windows.Forms.MessageBox.Show("ERROR: Cannot read video frame");
return m;
}
}
private void pictureBox_display_MouseDown(object sender, MouseEventArgs e) {
if (VideoFileName != null amp;amp; checkBox1.Checked == false amp;amp; VideoPlaying == false) {
IsMouseDown = true;
MouseMoved = false;
ThereIsNewSelection = true;
StartLocation = e.Location;
Console.WriteLine(StartLocation);
}
}
// Constantly alter the end location of the mouse to provide the coordinates for the rectangle
private void pictureBox_display_MouseMove(object sender, MouseEventArgs e) {
if (IsMouseDown == true) {
MouseMoved = true;
EndLocation = e.Location;
pictureBox_display.Invalidate();
}
}
private void pictureBox_display_Paint(object sender, PaintEventArgs e) {
if (!VideoPlaying amp;amp; ThereIsNewSelection) {
e.Graphics.DrawRectangle(Pens.Red, GetRectangle());
}
}
Я знаю, что это определенно связано с Invalidate(), поскольку я комментировал код построчно, и он вылетает только при наличии Invalidate() . Кто-нибудь может сказать мне, что может быть причиной проблемы?
Вот трассировка стека, если она полезна:
в системе.Рисование.SafeNativeMethods.Gdip.GdipDrawImageRectI(HandleRef графика, HandleRef изображение, Int32 x, Int32 y, ширина Int32, высота Int32) в системе.Рисование.Графика.drawImage(изображение изображения, Int32 x, Int32 y, ширина Int32, высота Int32) в системе.Рисование.Графика.drawImage (Изображение изображения, прямоугольник прямоугольника) в системе.Windows.Формы.PictureBox.OnPaint(PaintEventArgs pe) в системе.Windows.Формы.Управление.PaintWithErrorHandling(PaintEventArgs e, слой Int16) в системе.Windows.Формы.Управление.WmPaint(сообщение и m)
в системе.Windows.Формы.Управление.WndProc(Messageamp; m) в системе.Windows.Формы.NativeWindow.DebuggableCallback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)
Комментарии:
1. вы устанавливаете
pictureBox_display.Image = tempimg.Bitmap;
, а также рассылаете спам WinForms приpictureBox_display.Invalidate();
каждом движении мыши, поэтому я предполагаю, чтоtempimg.Bitmap
это было удалено в середине операции GDIGdipDrawImageRectI
(или, может быть, прямо перед этим), так что у вас естьAccessViolationException
.2. Привет @vasily.sib, спасибо за ваш ответ! Я рассматривал возможность этого, но я не вижу никакого способа сохранить контроль над tempimg. Растровое изображение. Как бы вы порекомендовали мне решить проблему? В любом случае мне удалось найти исправление после того, как вы подтвердили мои подозрения. Для тех, кому интересно, я в основном добавил еще одну глобальную переменную
Mat CurrentFrameMat
и в функцииOpenFileButton_Click(...)
присвоил Frame 0CurrentFrameMat
и выполнил apictureBox_display.Image = CurrentFrameMat.Bitmap
непосредственно перед Invalidate() в функции MouseMove . Большое спасибо!3. Вам просто нужно переместить
tempimg
переменную из метода, чтобы она стала полем класса form. Таким образом, сборщик мусора всегда видит ссылку на него и предотвращает сборку мусора.
Ответ №1:
Вам необходимо клонировать (глубоко копировать) растровое изображение, например, следующее:
B.Clone(new Rectangle(0, 0, B.Width, B.Height), B.PixelFormat)
Комментарии:
1. Что не так с этим ответом? Если вместо
pictureBox_display.Image = tempimg.Bitmap;
этого вы будете использовать «клонированную» копиюtempimg.Bitmap
и назначить ее графическому блоку, сбой не произойдет.