I am completely new to video input, and just started working with AForge a few days ago. Working with live video is comfortable, but I need to do something with files for a project now.
Using the Windows Media Video 9 VCM codec, saving has not been a problem. The output file works normally with every player I have, but my program always plays it back at about double the frame rate. This is especially odd since there is never any indication that the frame rate is changed: both the default the video was saved with and the new player indicate that the frame rate is 25 fps.
The only suggestions I have found are to change the frame rate before the video is captured, but this seems to do nothing.
Looking around in the AVIFileVideoSource documentation, I found the FrameIntervalFromSource and FrameInterval properties which, together, should give me the results I am looking for, but I can't get them to work, either. Everything else has been a dead end, and I am out of ideas. Here is the code that I am using to read the file:
public partial class Form1 : Form
{
AVIReader input = new AVIReader();
AVIFileVideoSource source = new AVIFileVideoSource("test.avi");
public Form1()
{
InitializeComponent();
}
public void cam_NewFrame(object sender, NewFrameEventArgs eventArgs)
{
input.Open("test.avi");
for (int i = 0; i < input.Length; i++)
{
pictureBox1.Image = input.GetNextFrame();
}
source.Stop();
input.Close();
}
private void button1_Click(object sender, EventArgs e)
{
source.NewFrame += new NewFrameEventHandler(cam_NewFrame);
source.Start();
}
private void button2_Click(object sender, EventArgs e)
{
source.Stop();
input.Close();
}
}
Any other suggestions would be greatly appreciated. Thank you for your time.
I found a working solution to the problem by looking into some other areas of the library. In this solution, there were two other classes that I was overlooking: DirectShow, which was already referenced, and Control. Specifically, I needed to use instances of FileVideoSource and VideoSourcePlayer to get the video into something I could work with.
This version is different from the above in that both the read and write functions have been combined into one program. Furthermore, I was in something of a rush to get this done, so it is still quite fragile. Nevertheless, here is my solution:
public partial class Form1 : Form
{
public Bitmap newBitmap;
public VideoCaptureDevice cam = null;
public FilterInfoCollection usbCams;
AVIReader reader = new AVIReader();
AVIWriter writer = new AVIWriter("wmv3");
AVIFileVideoSource source = new AVIFileVideoSource("test.avi");
FileVideoSource normSource = new FileVideoSource("test.avi");
VideoSourcePlayer player = new VideoSourcePlayer();
public Form1()
{
InitializeComponent();
}
public void cam_NewFrame(object sender, NewFrameEventArgs eventArgs)
{
Bitmap image = (Bitmap)eventArgs.Frame.Clone();
writer.AddFrame(image);
pictureBox1.Image = image;
}
public void video_NewFrame(object sender, NewFrameEventArgs eventArgs)
{
newBitmap = (Bitmap)eventArgs.Frame.Clone();
pictureBox1.Image = newBitmap;
}
private void videoSourcePlayer_NewFrame(object sender, ref Bitmap image)
{
videoSourcePlayer1.VideoSource = normSource;
videoSourcePlayer1.GetCurrentVideoFrame();
videoSourcePlayer1.DrawToBitmap(newBitmap,
new Rectangle(0, 0, image.Width, image.Height));
}
private void button1_Click(object sender, EventArgs e)
{
source.NewFrame += new NewFrameEventHandler(video_NewFrame);
source.Start();
videoSourcePlayer1.NewFrame += new AForge.Controls.VideoSourcePlayer.NewFrameHandler(videoSourcePlayer_NewFrame);
videoSourcePlayer1.Start();
}
private void button2_Click(object sender, EventArgs e)
{
if (source.IsRunning == true)
{
source.Stop();
videoSourcePlayer1.Stop();
}
if (cam != null)
{
cam.Stop();
writer.Close();
}
}
private void button3_Click(object sender, EventArgs e)
{
usbCams = new FilterInfoCollection(FilterCategory.VideoInputDevice);
cam = new VideoCaptureDevice(usbCams[0].MonikerString);
cam.DesiredFrameSize = new Size(320, 240);
writer.Open("test.avi", 320, 240);
cam.NewFrame += new NewFrameEventHandler(cam_NewFrame);
cam.DesiredFrameRate = 25;
cam.Start();
}
}
Thank you for reading.
Related
I have written a C# WinForms application. I want to get the data of the sound card in regular intervals. For this, I use the NAudio package. To check if I initialize the objects waveIn and WaveFormat with the correct values (SampleRate, number of channels, number of depth bits), I generated several audio tracks with Audacity. Among others, a square wave that can be heard for 2.5 seconds, then is silent for 2.5 seconds, etc. In the WaveIn_DataAvailable event handler, I paint a rectangle. I would expect it to turn red for 2.5 seconds, then turn black for 2.5 seconds. Unfortunately, the rectangle always flickers red and black when the audio is muted. So there is the problem that I can't test because at the moment of silence (it seems) there are everything but zeros in e.Buffer. Even at the moment of the sound, the rectangle is not ‘permanently’ red. Actually there is always flickering between red and black. What is the problem?
My soundcard is capable of 48 kHz, 24 bit. I don't know the number of channels. If channels refers to stereo or mono, then, of course, channel = 2.
using System;
using System.Windows.Forms;
using NAudio.Wave;
namespace Sound_Capture_Program
{
public partial class FormMain : Form
{
public FormMain()
{
InitializeComponent();
}
private int DeviceNumber = 0;
private WaveIn waveIn = null;
private void FormMain_Load(object sender, EventArgs e)
{
for (int i = 0; i < WaveIn.DeviceCount; i++)
{
WaveInCapabilities deviceInfo = WaveIn.GetCapabilities(i);
ComboboxDevices.Items.Add(deviceInfo.ProductName);
}
if (ComboboxDevices.Items.Count >= 2)
{
ComboboxDevices.SelectedIndex = 1;
}
}
private void Combobox1_SelectedIndexChanged(object sender, EventArgs e)
{
if (ComboboxDevices.SelectedIndex != -1)
{
this.DeviceNumber = ComboboxDevices.SelectedIndex;
}
}
private void ButtonStart_Click(object sender, EventArgs e)
{
int sampleRate = int.Parse(Textbox_SR.Text);
waveIn = new WaveIn()
{
DeviceNumber = this.DeviceNumber,
WaveFormat = new WaveFormat(sampleRate, 24, 1)
};
waveIn.DataAvailable += WaveIn_DataAvailable;
waveIn.StartRecording();
ButtonStart.Enabled = false;
ButtonStop.Enabled = true;
}
private void ButtonStop_Click(object sender, EventArgs e)
{
waveIn.DataAvailable -= WaveIn_DataAvailable;
waveIn.StopRecording();
waveIn.Dispose();
ButtonStart.Enabled = true;
ButtonStop.Enabled = false;
}
private void WaveIn_DataAvailable(object sender, WaveInEventArgs e)
{
using (System.Drawing.Graphics g = this.CreateGraphics())
{
//byte redvalue = (byte)(((int)e.Buffer[0] + (int)e.Buffer[1] + (int)e.Buffer[2]) / 3);
System.Drawing.Color color = System.Drawing.Color.FromArgb(e.Buffer[0], 0, 0);
using (System.Drawing.SolidBrush Brush = new System.Drawing.SolidBrush(color))
{
g.FillRectangle(Brush, 250, 150, 20, 20);
}
}
}
private void FormMain_FormClosing(object sender, FormClosingEventArgs e)
{
if (waveIn != null)
{
waveIn.DataAvailable -= WaveIn_DataAvailable;
waveIn.StopRecording();
waveIn.Dispose();
}
}
}
}
Audacity
I am trying to write a game on Visual Studio with C#. The aim is to click on the right picture(the one with nemo fish) as they are falling.
Until now i have managed to add images from file randomly to Pictureboxes. As time runs, the images are updated.
But the problem is the images are not dropping until the end of the panel. They are updated just where they are.
Now I am trying to code that, the images are falling until the end of panel and there must be new coming images from behind of these images continously.
I am really new at coding, so it would be great if you explain it detailed and clear.
Thank you.
Here is a sample of my code:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
PictureBox[] pic = new PictureBox[4];
Image[] img = new Image[17];
Random ranFoto = new Random();
private void Form1_Load(object sender, EventArgs e)
{
panel1.Paint += new PaintEventHandler(panel1_Paint);
}
private void timer1_Tick(object sender, EventArgs e)
{
for (int i = 0; i < pic.Length; i++)
{
pic[i].Size = new System.Drawing.Size(140, 90);
this.pic[i].BorderStyle = BorderStyle.Fixed3D;
pic[i].Image = img[ranFoto.Next(17)];
panel1.Controls.Add(pic[i]);
}
}
private void panel1_Paint(object sender, PaintEventArgs e)
{
pic[0] = new PictureBox();
pic[0].Location = new System.Drawing.Point(0, 0);
pic[1] = new PictureBox();
pic[1].Location = new System.Drawing.Point(140, 0);
pic[2] = new PictureBox();
pic[2].Location = new System.Drawing.Point(280, 0);
pic[3] = new PictureBox();
pic[3].Location = new System.Drawing.Point(420, 0);
img[0] = Image.FromFile("C:\\Daten\\Fotos\\1.jpg");
....
img[16] = Image.FromFile("C:\\Daten\\Fotos\\17.jpg");
}
private void button1_Click(object sender, EventArgs e)
{
timer1.Enabled = true;
}
}
I want to show 4 videos at the same time in Windows Forms using Task. I have 4 video play clicks. When I click the first video play button, first video plays, when I click the second video play button, second video plays and first video continue to play at same time. But, when I click third video play button; first video stops and second video and third video plays at the same time. In the same way, when I click the fourth video play button, the second video stops, just third and fourth video plays at the same time.
My code:
private async void play1_Click(object sender, EventArgs e)
{
string inputPath = textBox1.Text;
await Task.Run(() => {
ReadFrames1(inputPath); });
}
and
public void ReadFrames1(string inputPath)
{
using (var vFReader = new VideoFileReader())
{
vFReader.Open(inputPath);
for (int i = 0; i < vFReader.FrameCount; i++)
{
Bitmap videoFrame = vFReader.ReadVideoFrame();
System.Drawing.Image pic = resizeImage(new Bitmap(videoFrame), new Size(305, 267));
pictureBox1.Image = new Bitmap(pic);
}
vFReader.Dispose();
vFReader.Close();
}
}
play2_Click(), play3_Click(), play4_Click() methods are same with play1_Click(). (I mean for example play2_Click() method calls ReadFrames2() and shows on PictureBox2.) Where I am wrong?
For multithreading via WinForms use BackgroundWorker.
here is a code example which works for me. hope it will help you:
public Form1()
{
InitializeComponent();
}
private BackgroundWorker CreateBackgroundWorker()
{
BackgroundWorker worker = new BackgroundWorker();
worker.DoWork += ReadFrames;
worker.RunWorkerCompleted += worker_RunWorkerCompleted;
return worker;
}
private void worker_RunWorkerCompleted(object sender,
RunWorkerCompletedEventArgs e)
{
Button playButton = (Button) e.Result;
playButton.Enabled = true;
}
private void ReadFrames(object sender, DoWorkEventArgs e)
{
BackgroundWorker backgroundWorker = (BackgroundWorker) sender;
(string path, PictureBox pictureBox, Button playButton) arguments = ((string, PictureBox, Button)) e.Argument;
using(var vFReader = new VideoFileReader())
{
vFReader.Open(arguments.path);
for (int i = 0; i < vFReader.FrameCount; i++)
{
arguments.pictureBox.Image = new Bitmap(vFReader.ReadVideoFrame(), arguments.pictureBox.Size);
}
vFReader.Dispose();
vFReader.Close();
}
e.Result = arguments.playButton;
}
private void play1_Click(object sender, EventArgs e)
{
var worker = CreateBackgroundWorker();
worker.RunWorkerAsync((textBox1.Text, pictureBox1, play1));
play1.Enabled = false;
}
private void play2_Click(object sender, EventArgs e)
{
var worker = CreateBackgroundWorker();
worker.RunWorkerAsync((textBox2.Text, pictureBox2, play2));
play2.Enabled = false;
}
private void play3_Click(object sender, EventArgs e)
{
var worker = CreateBackgroundWorker();
worker.RunWorkerAsync((textBox3.Text, pictureBox3, play3));
play3.Enabled = false;
}
private void play4_Click(object sender, EventArgs e)
{
var worker = CreateBackgroundWorker();
worker.RunWorkerAsync((textBox4.Text, pictureBox4, play4));
play4.Enabled = false;
}
I have been learning about drawing to panels using bitmaps. I thought I would run a trial program to simply turn a white panel black. (May seem a complicated way of doing it but this is just to test the basics) My program is as follows:
public partial class Form1 : Form
{
private Bitmap buffer = new Bitmap(100,100);
public Form1()
{
InitializeComponent();
}
private void panel1_Paint(object sender, PaintEventArgs e)
{
e.Graphics.DrawImageUnscaled(buffer, Point.Empty);
}
private void button1_Click(object sender, EventArgs e)
{
for (int i = 0; i < 100; i++)
{
for (int j = 0; j < 100; j++)
{
buffer.SetPixel(i, j, Color.Black);
}
}
}
}
When I run it and press the button the panel does not seem to change. Any Idea where I am going wrong. Thank you in advance.
You have to invalidate the panel's client area so that Windows will force a repaint. But there are some other issues:
FillRectangle will do a much more efficient job than painting each pixel in a loop, as #Tony suggested.
You might hit concurrency issues if the panel is invalidated before buffer is ready to be displayed. Be sure that the bitmap generation is isolated from its presentation.
These suggestions are summarized (but not tested) as follows:
private void button1_Click(object sender, EventArgs e)
{
Bitmap tempBuffer = new Bitmap(100, 100);
using (Graphics g = Graphics.FromImage(tempBuffer))
using (SolidBrush blackBrush = new SolidBrush(Color.Black))
{
g.FillRectangle(blackBrush, new Rectangle(0, 0, tempBuffer.Width-1, tempBuffer.Height-1);
}
buffer = tempBuffer;
panel1.Invalidate();
}
In addition to invalidating the panel's client area, if you're wanting it to paint when you click the button you'll want to wire up the paint event in the button's click event. Give this a shot:
public partial class Form1 : Form
{
private bool _paintWired;
public Form1()
{
InitializeComponent();
}
private void PanelPaint(object sender, PaintEventArgs e)
{
using (Graphics g = this.panel1.CreateGraphics())
{
g.FillRectangle(Brushes.Black, this.panel1.Bounds);
}
}
private void button1_Click(object sender, EventArgs e)
{
if(!_paintWired)
{
this.panel1.Paint += new PaintEventHandler(PanelPaint);
_paintWired = true;
}
this.panel1.Invalidate();
}
}
UPDATE: Sorry, I missed the point about using a bitmap.
try this example
I use it for something like what you want to do and it worked.
I hope to help you
example_1
I have made a program that allows users to draw lines onto a picturebox image but now need to save these lines to be opened at a later date. This is my current code for drawing the lines:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
pictureBox1.Image = new Bitmap(pictureBox1.Width, pictureBox1.Height);
}
int Drawshape;
private Point p1, p2;
List<Point> p1List = new List<Point>();
List<Point> p2List = new List<Point>();
private void Form1_MouseDown(object sender, MouseEventArgs e)
{
}
private void Form1_Paint(object sender, PaintEventArgs e)
{
}
private void button1_Click(object sender, EventArgs e)
{
Drawshape = 5;
}
private void button2_Click(object sender, EventArgs e)
{
Drawshape = 2;
}
private void pictureBox1_MouseDown(object sender, MouseEventArgs e)
{
if (Drawshape == 5)
{
if (p1.X == 0)
{
p1.X = e.X;
p1.Y = e.Y;
}
else
{
p2.X = e.X;
p2.Y = e.Y;
p1List.Add(p1);
p2List.Add(p2);
pictureBox1.Invalidate();
p1.X = 0;
}
}
}
private void pictureBox1_ParentChanged(object sender, EventArgs e)
{
}
private void pictureBox1_Paint(object sender, PaintEventArgs e)
{
Graphics G = Graphics.FromImage(pictureBox1.Image);
if (Drawshape == 5)
{
using (var p = new Pen(Color.Blue, 4))
{
for (int x = 0; x < p1List.Count; x++)
{
G.DrawLine(p, p1List[x], p2List[x]);
}
}
}
}
private void Form1_Load(object sender, EventArgs e)
{
}
private void pictureBox1_MouseMove(object sender, MouseEventArgs e)
{
pictureBox1.Invalidate();
}
private void Save_Click(object sender, EventArgs e)
{
}
}
}
I don't know how to save these lines and also open them again at a later time when the user wants to. I have put in open and save filedialogs but not sure how to make them do the job i want them to do. Please Help.
Thanks
If you want to save the image that is displayed in the picture box, complete with any lines that may have been drawn on top of it during run-time, you can use the Control.DrawToBitmap method.
I can't tell for sure if you're also asking how to use a SaveFileDialog to determine where the user wants to save the file or if you've already got that part figured out, but it's very simple.
Here's an example of a complete solution. First, the user is prompted by a save dialog (entitled "Save Image" and filtering to bitmap images (*.bmp) by default). If they click OK, the image displayed in the picture box is drawn to a temporary bitmap, and that temporary bitmap is saved to the location they specified. If they click Cancel, the file is not saved and the method simply exits.
private void Save_Click(object sender, EventArgs e)
{
//Show a save dialog to allow the user to specify where to save the image file
using (SaveFileDialog dlgSave = new SaveFileDialog())
{
dlgSave.Title = "Save Image";
dlgSave.Filter = "Bitmap Images (*.bmp)|*.bmp|All Files (*.*)|*.*";
if (dlgSave.ShowDialog(this) == DialogResult.OK)
{
//If user clicked OK, then save the image into the specified file
using (Bitmap bmp = new Bitmap(pictureBox1.Width, pictureBox1.Height))
{
picturebox1.DrawToBitmap(bmp, new Rectangle(0, 0, bmp.Width, bmp.Height));
bmp.Save(dlgSave.FileName);
}
}
}
}
Have a look at
PictureBox.Image Property
and
Image.Save Method (String, ImageFormat)
and
Image.FromFile Method (String, Boolean)
It's not clear what you want... do you want to save the resulting image, or the list of points ?
If you want to save the image, just use pictureBox1.Image.Save(fileName).
If you want to save the list of points, you could use serialization (it should work with either binary or XML serialization)