I am trying to save webcam image in directory using AForge.NET.
Here is my Code:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
FilterInfoCollection webcam;
VideoCaptureDevice cam;
Bitmap bitmap;
private void Form1_Load(object sender, EventArgs e)
{
webcam = new FilterInfoCollection(FilterCategory.VideoInputDevice);
cam = new VideoCaptureDevice(webcam[0].MonikerString);
cam.NewFrame += new NewFrameEventHandler(cam_NewFrame);
cam.Start();
}
void cam_NewFrame(object sender, NewFrameEventArgs eventArgs)
{
bitmap = (Bitmap)eventArgs.Frame.Clone();
pictureBox1.Image = bitmap;
pictureBox1.Image.Save("c:\\image\\image1.jpg");
}
But i am getting this exception:
InvalidOperationException was unhandled
Object is currently in use elsewhere.
If you are using Graphic objects after the GetHdc method, call the RealseHdc method.
Thanks in advance.
Problem is this line:
pictureBox1.Image = bitmap;
pictureBox1.Image.Save("c:\\image\\image1.jpg");
You are trying to save image that is not yet properly loaded and also you are facing cross-threading.
The solution in this case is to not use multiple threads when drawing.
void cam_NewFrame(object sender, NewFrameEventArgs eventArgs)
{
bitmap = (Bitmap)eventArgs.Frame.Clone();
pictureBox1.Image = bitmap;
try
{
this.Invoke((MethodInvoker)delegate
{
//saves image on its thread
pictureBox1.Image.Save("c:\\image\\image1.jpg");
});
}
catch (Exception ex)
{
MessageBox.Show(""+ex);
}
}
Related
I am trying to figure out how to save a snapshot of a video being shown from a picturebox control. I can already save an image file, however my problem is the whole image that is 'seen' by my camera is the one that is being saved. What I would like to save is the image that is only being shown from my picturebox control, which is just a portion of what the camera is capturing. By the way I am using Aforge framework set of video libraries.
My picturebox is set at Height = 400 and Width = 400.
Here is a sample of my code
private void Form1_Load(object sender, EventArgs e)
{
videoDevices = new FilterInfoCollection(FilterCategory.VideoInputDevice);
foreach (FilterInfo device in videoDevices)
{
drvrlist.Items.Add(device.Name);
}
drvrlist.SelectedIndex = 1;
videosource = new VideoCaptureDevice();
if (videosource.IsRunning)
{
videosource.Stop();
}
else
{
videosource = new VideoCaptureDevice(videoDevices[comboBox1.SelectedIndex].MonikerString);
videosource.NewFrame += new NewFrameEventHandler(videosource_NewFrame);
videosource.Start();
}
}
private void startbtn_Click(object sender, EventArgs e)
{
if (videosource.IsRunning)
{
videosource.Stop();
}
else
{
videosource = new VideoCaptureDevice(videoDevices[drvrlist.SelectedIndex].MonikerString);
videosource.NewFrame += new NewFrameEventHandler(videosource_NewFrame);
videosource.Start();
}
}
private void videosource_NewFrame(object sender, NewFrameEventArgs eventArgs)
{
pictureBox1.Image = (Bitmap)eventArgs.Frame.Clone();
//throw new NotImplementedException();
}
private void save_btn_Click(object sender, EventArgs e)
{
SaveFileDialog savefilediag = new SaveFileDialog();
savefilediag.Filter = "Portable Network Graphics|.png";
if(savefilediag.ShowDialog()== System.Windows.Forms.DialogResult.OK)
{
if (pictureBox1.Image != null)
{
//Save First
Bitmap varBmp = new Bitmap(pictureBox1.Image);
Bitmap newBitmap = new Bitmap(varBmp);
varBmp.Save(savefilediag.FileName, ImageFormat.Png);
//Now Dispose to free the memory
varBmp.Dispose();
varBmp = null;
pictureBox1.Image = null;
pictureBox1.Invalidate();
}
else
{ MessageBox.Show("null exception"); }
}
}
You can use the Clone method to overwrite an instance of your picturebox's image with a subspace of the image.
Bitmap varBmp = new Bitmap(pictureBox1.Image);
varBmp = varBmp.Clone(new RectangleF(0, 0, 400, 400), varBmp.PixelFormat);
From there, you can go ahead and save it to a file.
varBmp.Save(savefilediag.FileName, ImageFormat.Png);
First of all, I can't play and record HD video(1920x1080), when I change my camera resolution to 1920x1080 program show and record black background, if camera resolution is 720x576 it works without problems. I'm sure that the problem is in my solution because, when I use DesktopVideo(Software which is included with Blackmagic Decklink Studio 2) it shows HD video from camera.
Second, How do I convert video with AForge? like changing resolution and framerate, I can change codecs and bitrate, but when I change Resolution and FrameRate in "FileWriter.Open"
command I get Error Resolution and FrameRate must be the same as Bitmap I'm capturing from camera.
If anybody knows how to solve these problems please share information, thanks!
Here is the code
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using AForge.Video;
using AForge.Video.DirectShow;
using AForge.Video.FFMPEG;
using AForge.Video.VFW;
namespace WindowsFormsApplication12
{
public partial class Form1 : Form
{
private FilterInfoCollection VideoCaptureDevices;
private VideoCaptureDevice FinalVideo = null;
private VideoCaptureDeviceForm captureDevice;
private Bitmap video;
//private AVIWriter AVIwriter = new AVIWriter();
private VideoFileWriter FileWriter = new VideoFileWriter();
private SaveFileDialog saveAvi;
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
VideoCaptureDevices = new FilterInfoCollection(FilterCategory.VideoInputDevice);
captureDevice = new VideoCaptureDeviceForm();
}
private void button1_Click(object sender, EventArgs e)
{
/////capture device list
if (captureDevice.ShowDialog(this) == DialogResult.OK)
{
VideoCaptureDevice videoSource = captureDevice.VideoDevice;
FinalVideo = captureDevice.VideoDevice;
FinalVideo.NewFrame += new NewFrameEventHandler(FinalVideo_NewFrame);
FinalVideo.Start();
}
}
void FinalVideo_NewFrame(object sender, NewFrameEventArgs eventArgs)
{
if (butStop.Text == "Stop Record")
{
video = (Bitmap)eventArgs.Frame.Clone();
pictureBox1.Image = (Bitmap)eventArgs.Frame.Clone();
//AVIwriter.Quality = 0;
FileWriter.WriteVideoFrame(video);
//AVIwriter.AddFrame(video);
}
else
{
video = (Bitmap)eventArgs.Frame.Clone();
pictureBox1.Image = (Bitmap)eventArgs.Frame.Clone();
}
}
private void button2_Click(object sender, EventArgs e)
{
////record button
saveAvi = new SaveFileDialog();
saveAvi.Filter = "Avi Files (*.avi)|*.avi";
if (saveAvi.ShowDialog() == System.Windows.Forms.DialogResult.OK)
{
int h = captureDevice.VideoDevice.VideoResolution.FrameSize.Height;
int w = captureDevice.VideoDevice.VideoResolution.FrameSize.Width;
FileWriter.Open(saveAvi.FileName, w, h,25,VideoCodec.Default,5000000);
FileWriter.WriteVideoFrame(video);
//AVIwriter.Open(saveAvi.FileName, w, h);
butStop.Text = "Stop Record";
//FinalVideo = captureDevice.VideoDevice;
//FinalVideo.NewFrame += new NewFrameEventHandler(FinalVideo_NewFrame);
//FinalVideo.Start();
}
}
private void butStop_Click(object sender, EventArgs e)
{
if (butStop.Text == "Stop Record")
{
butStop.Text = "Stop";
if (FinalVideo == null)
{ return; }
if (FinalVideo.IsRunning)
{
//this.FinalVideo.Stop();
FileWriter.Close();
//this.AVIwriter.Close();
pictureBox1.Image = null;
}
}
else
{
this.FinalVideo.Stop();
FileWriter.Close();
//this.AVIwriter.Close();
pictureBox1.Image = null;
}
}
private void button3_Click(object sender, EventArgs e)
{
pictureBox1.Image.Save("IMG" + DateTime.Now.ToString("hhmmss") + ".jpg");
}
private void button4_Click(object sender, EventArgs e)
{
this.Close();
}
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
if (FinalVideo == null)
{ return; }
if (FinalVideo.IsRunning)
{
this.FinalVideo.Stop();
FileWriter.Close();
//this.AVIwriter.Close();
}
}
}
}
The provided code simply captures and saves in the format currently active/selected for the capture device. In general, resolution change is an "expensive" operation is not available as a complimentary operation.
You want to change resolution right on capture device by switching it to more appropriate format, then capture in this resolution and have all further data manipulation in correct resolution. Available resolutions and options might be hardware specific there.
If/when no suitable capture resolution option is available, you typically resample the video to new resolution. Even though Windows API has suitable functionality, in your particular case you are interested to have this integrated with AForge library and you need to check its documentation to find out whether it provides a respective wrapper and whether it can be integrated into capture process overall (or, you need to write this code yourself to handle scaling).
Say I have this code
private void photoChooserBtn_Click(object sender, RoutedEventArgs e)
{
photoChooserTask = new PhotoChooserTask();
photoChooserTask.Completed += new EventHandler<PhotoResult (photoChooserTask_Completed);
photoChooserTask.Show();
}
private void photoChooserTask_Completed(object sender, PhotoResult e)
{
if (e.TaskResult == TaskResult.OK)
{
BitmapImage bmp = new BitmapImage();
bmp.SetSource(e.ChosenPhoto);
imagecontrol.Source = bmp;
}
}
I have to repeat this code several times as I have several buttons. I want to avoid this.
I want to have one button click event. Then append extra parameter to photoChooserTask so that I can process the result in photoChooserTask_Completed event based on the parameter.
So in photoChooserBtn_Click event. I would like to do something like this.
Button btn = (Button)sender;
photoChooserTask.Tag = btn.Name;
Then
private void photoChooserTask_Completed(object sender, PhotoResult e)
{
if (e.TaskResult == TaskResult.OK)
{
BitmapImage bmp = new BitmapImage();
bmp.SetSource(e.ChosenPhoto);
imagecontrol.Source = bmp;
string param = ((PhotoChooserTask)sender).Tag;
Switch (param)
{
case "bla":
case "bla2":
...........
}
}
What's the best way to do this?
What you are looking for is not possible. However you can add a string tag property to your page. Since only one PhotoChooserTask can run at a time, this approach should be fine.
public partial class MainPage : PhoneApplicationPage
{
string tag;
// Constructor
public MainPage()
{
InitializeComponent();
}
private void photoChooserBtn_Click(object sender, RoutedEventArgs e)
{
photoChooserTask = new PhotoChooserTask();
tag = (sender as Button).Name;
photoChooserTask.Completed += new EventHandler<PhotoResult>(photoChooserTask_Completed);
photoChooserTask.Show();
}
private void photoChooserTask_Completed(object sender, PhotoResult e)
{
if (e.TaskResult == TaskResult.OK)
{
BitmapImage bmp = new BitmapImage();
bmp.SetSource(e.ChosenPhoto);
imagecontrol.Source = bmp;
switch(tag)
{
case tag1:
case tag2:
........
}
tag = null;
}
}
Using Photochooser Task the image has to be loaded and passed immediately to another page. But shows blank when implemented the following code:
private void LoadPicture_Click(object sender, RoutedEventArgs e)
{
PhotoChooserTask photoChooserTask;
photoChooserTask = new PhotoChooserTask();
photoChooserTask.Completed += new EventHandler<PhotoResult>(photoChooserTask_Completed);
photoChooserTask.Show();
NavigationService.Navigate(new Uri("/Page1.xaml", UriKind.Relative));
}
void photoChooserTask_Completed(object sender, PhotoResult e)
{
if (e.TaskResult == TaskResult.OK)
{
System.Windows.Media.Imaging.BitmapImage bmp = new System.Windows.Media.Imaging.BitmapImage();
bmp.SetSource(e.ChosenPhoto);
Page1 p1 = new Page1();
p1.encodeImg.Source = bmp;
}
else
{
MessageBox.Show("Image Loading Failed.");
}
}
Please suggest in fixing the above the issue.
Thanks!
Have you solved it? if you haven't you could use something like this. in your photoChooseTask handler save the bitmapImage
PhoneApplicationService.Current.State["yourparam"] = bmp;
and then in your Page1 you get the bitmapImage
BitmapImage bitmapGet = PhoneApplicationService.Current.State["yourparam"] as BitmapImage;
here's how you should use this.
void photoChooserTask_Completed(object sender, PhotoResult e)
{
if (e.TaskResult == TaskResult.OK)
{
System.Windows.Media.Imaging.BitmapImage bmp = new System.Windows.Media.Imaging.BitmapImage();
bmp.SetSource(e.ChosenPhoto);
//save the bitmapImage
PhoneApplicationService.Current.State["yourparam"] = bmp;
}
else
{
MessageBox.Show("Image Loading Failed.");
}
NavigationService.Navigate(new Uri("/Page1.xaml", UriKind.Relative));
}
your Page1
protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
{
//get the bitmapImage
BitmapImage bitmapGet = PhoneApplicationService.Current.State["yourparam"] as BitmapImage;
//set the bitmpaImage
img.Source = bitmapGet;
base.OnNavigatedTo(e);
}
More about PhoneApplicationService.Current.State :)
The navigation must be done after completed event, photochooser.show() suppresses the main application thread, hence you can only pass the image stream once you get it. So, shift navigation statement to completed event handler and using isolatedstoragesettings.applicationsettings to store the image and get it back on second page.
Another way to achieve it is to save the image in isolateStorage first and pass the file path to your page1 as a string parameter.
page1 then could load the image anytime it needs.
I'm creating a C# program that's capturing the screen with bitmap.
And than I want to save it to an .Avi/ .mpeg file.
But I don't know how to save it to a video.
Here is the code I already have.
public Form1()
{
InitializeComponent();
}
static Bitmap bm;
private void btnFolder_Click(object sender, EventArgs e)
{
FolderBrowserDialog folderDlg = new FolderBrowserDialog();
folderDlg.ShowNewFolderButton = true;
DialogResult result = folderDlg.ShowDialog();
if (result == DialogResult.OK)
{
textBox1.Text = folderDlg.SelectedPath;
Environment.SpecialFolder root = folderDlg.RootFolder;
}
}
private void btnStart_Click(object sender, EventArgs e)
{
timer1.Start();
}
private void btnStop_Click(object sender, EventArgs e)
{
timer1.Stop();
SaveCapture(textBox1.Text);
}
private void SaveCapture(string path)
{
// Here should be the code to save it to mpeg/avi
}
private void timer1_Tick(object sender, EventArgs e)
{
// Take screenshot
bm = new Bitmap(Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height);
Graphics graphics = Graphics.FromImage(bm as Image);
graphics.CopyFromScreen(0, 0, 0, 0, bm.Size);
pictureBox1.SizeMode = PictureBoxSizeMode.StretchImage;
// Show it in picturebox
pictureBox1.Image = bm;
}
Thank you very much!
Create a Video Stream (AVI) from a Series of Images
I think this might be your best solution. Store all the .jpg's and create an avi from the command line at intervals. I don't see how creating video on the fly would produce a "lightweight" solution.
Hello click this to download the aviwrapper liblary. And the code that you should write is this:
var pngFileList = Directory.EnumerateFiles(folderImages, "*.png");
//load the first image
Bitmap bitmap = (Bitmap)Image.FromFile(pngFileList.First());
//create a new AVI file
AviManager aviManager = new AviManager(fileName, false); // location and the name of video file
//add a new video stream and one frame to the new file
//set IsCompressed = false
VideoStream aviStream = aviManager.AddVideoStream(false, 3, bitmap);
pngFileList.Skip(1).ToList().ForEach(file =>
{
bitmap = (Bitmap)Bitmap.FromFile(file);
aviStream.AddFrame(bitmap);
bitmap.Dispose();
});
aviManager.Close();