I'm having some trouble using ZXing in silverlight.
I'm using this ZXing port: http://zxingnet.codeplex.com/
My proejct is able to get the video feed from the webcam, but I'm stuck at this line.
This is how i get the feed:
CaptureSource _capture = new CaptureSource();
_capture.VideoCaptureDevice = CaptureDeviceConfiguration.GetDefaultVideoCaptureDevice();
videoBrush = new VideoBrush();
videoBrush.Stretch = Stretch.Uniform;
videoBrush.SetSource(_capture);
webcam.Fill = videoBrush;
if (CaptureDeviceConfiguration.AllowedDeviceAccess||CaptureDeviceConfiguration.RequestDeviceAccess())
{
try
{
_capture.Start();
}
catch (Exception E)
{
MessageBox.Show(E.Message);
}
}
LuminanceSource source = new RGBLuminanceSource(,webcam.Width, webcam.Height);
It says, that says that it need a byte array, the "rbgRawBytes".
I got a videobrush which contains the webcam stream, i think :)
and i got the webcam rectangle that displays the output.
You should use the method CaptureImageAsync and the event CaptureImageCompleted. In the event handler you get a WriteableBitmap within the event arguments. The WriteableBitmap is a captured image from the webcam. Use the WriteableBitmap instance directly with the method Decode of the BarcodeReader class. Don't do it manually by using RGBLuminanceSource.
Here is a good sample how you can use CaptureImageAsync and CaptureImageCompleted:
http://channel9.msdn.com/coding4fun/articles/FaceLight--Silverlight-4-Real-Time-Face-Detection
Related
I have a program in which I use the Aforge library for viewing a webcam.
This works wonder:
LocalWebcamsCollection = new FilterInfoCollection(FilterCategory.VideoInputDevice);
LocalScannerBarcode = new VideoCaptureDevice(LocalWebcamsCollection[WebcamNumber].MonikerString);
LocalScannerBarcode.NewFrame += LocalScannerBarcode_NewFrame;
LocalScannerBarcode.Start();
and in the new frame event I get the bitmap
System.Drawing.Bitmap frame;
void LocalScannerBarcode_NewFrame(object sender, AForge.Video.NewFrameEventArgs eventArgs)
{
frame = (System.Drawing.Bitmap)eventArgs.Frame.Clone();
}
now I have to decode what is seen. Basically I have to pass the bitmap to decode.
So global I have;
ZXing.BarcodeReader bcr;
and into the event LocalScannerBarcode_NewFrame
if (bcr == null)
bcr = new ZXing.BarcodeReader();
but as soon as I put the two lines above the event is not called anymore.
Please notice that in Windows forms that works but I have to do it in WPF.
Thanks
Not sure if this helps but have you tried putting the reference to the ZXing library in another project? Something an Helper.
So in you project you will have:
string strResult = Helper.ReadBarcode(frame);
if (strResult != null)
{
... do stuff with the string
}
and in the helper
static ZXing.BarcodeReader bcr;
public static string ReadBarcode(System.Drawing.Bitmap bmp)
{
if (bcr == null)
bcr = new ZXing.BarcodeReader();
return bcr.Decode(bmp).ToString();
}
I have an issue that I think has not been covered in the multitude of other WPF image loading issues. I am scanning in several images and passing them to a "Preview Page". The preview page takes the image thumbnails and displays what a printout would look like via a generated bitmap.
The weird thing to me is, it will work fine if I run the program the first time. Upon reaching the end of the process and hitting "start over", the preview will return blank. I am creating the BitmapImage in a method that saves the bitmap as a random file name so I do not believe theres a lock on the file the second time around. Also, if I go to look at the temporary file created through explorer, it is drawn correctly so I know the appropriate data is getting to it.
Finally, when I navigate away from this page, I am clearing necessary data. I'm really perplexed and any help would be appreciated.
//Constructor
public Receipt_Form() {
InitializeComponent();
printData = new List<Object>();
this.Loaded += new RoutedEventHandler(MainWindow_Loaded);
}
void MainWindow_Loaded(object sender, RoutedEventArgs e) {
// populates global variable fileName
var task = System.Threading.Tasks.Task.Factory.StartNew(() => outputToBitmap()); task.ContinueWith(t => setImage(fileName),
System.Threading.Tasks.TaskScheduler.FromCurrentSynchronizationContext());
// I started the image creation in a separate thread because I
// thought it may be blocking the UI thread, but it didn't matter
}
private void setImage(string imageURI) {
BitmapImage image;
using (FileStream stream = File.OpenRead(imageURI)) {
image = new BitmapImage();
image.BeginInit();
image.StreamSource = stream;
image.CacheOption = BitmapCacheOption.OnLoad;
image.EndInit();
}
receiptPreview.Source = image;
//this works the first iteration but not the second, though the temp file is created successfully
}
Found the issue - the Modern UI container was getting cleared when transitioning off the page.
I'm trying to display images of various file types (including animated .gif files) in my Winforms application. I also have to be able to modify the files that are shown. (change the file name, delete them).
The problem is that a Picturebox locks the image file until the application is closed when using the normal way.
That means I can't do this:
private void Form1_Load(object sender, EventArgs e)
{
PictureBox pic = new PictureBox();
pic.Size = new Size(250, 250);
pic.Image = Image.FromFile("someImage.gif");
this.Controls.Add(pic);
//No use to call pic.Image = null or .Dispose of it
File.Delete("someImage.gif"); //throws exception
}
The workaround in the link above is as follows:
private void Form1_Load2(object sender, EventArgs e)
{
PictureBox pic = new PictureBox();
pic.Size = new Size(250, 250);
//using a FileStream
var fs = new System.IO.FileStream("someImage.gif", System.IO.FileMode.Open, System.IO.FileAccess.Read);
pic.Image = System.Drawing.Image.FromStream(fs);
fs.Close();
this.Controls.Add(pic);
pic.MouseClick += pic_MouseClick;
}
That works fine for normal image types, but it won't load animated .gifs, which is important to me. Trying to load one will make it look like this.
I've found a few other topics about it (this and this) but they're all about WPF and use BitmapImage. I've searched about how to use BitmapImage in a Winforms application, but haven't found anything apart from that it is supposed to somehow work.
I would like to stay with Winforms because I'm just getting used to it, but that's not a necessity.
To summarize: I need a way to show common image types (png, jpg, bmp, and animated gif) while still being able modify the file on the HDD. It is OK if that means unloading->modifying->reloading the file. I'd prefer Winforms, but other Frameworks would do.
Thanks for your help.
Edit: Another way I've tried
using (System.IO.FileStream fs = new System.IO.FileStream("E:\\Pics\\small.gif", System.IO.FileMode.Open, System.IO.FileAccess.Read))
{
System.IO.MemoryStream ms = new System.IO.MemoryStream();
fs.CopyTo(ms);
pic.Image = Image.FromStream(ms);
}
But shows the same problem as the second example. The gif doesn't load.
Using a MemoryStream is indeed the right way to avoid the file lock. Which is a strong optimization btw, the lock is created by the memory-mapped file that the Image class uses to keep the pixel data out of the paging file. That matters a great deal when the bitmap is large. Hopefully not on an animated gif :)
A small mistake in your code snippet, you forgot to reset the stream back to the start of the data. Fix:
using (var fs = new System.IO.FileStream(...)) {
var ms = new System.IO.MemoryStream();
fs.CopyTo(ms);
ms.Position = 0; // <=== here
if (pic.Image != null) pic.Image.Dispose();
pic.Image = Image.FromStream(ms);
}
In case it needs to be said: do not dispose the memory stream. That causes very hard to diagnose random crashes later, pixel data is read lazily.
Essentialy you'll have to make a copy of the image file in your memory.
Pre .Net 4.0 (2.0,3.0,3.5) you'd have to create a FileStream and copy it to a MemoryStream and rewind it, as seen in another answer.
Since .Net 4.0 (4.0,4.5,...) Image.FromFile supports animated GIF's
If you work with .Net 4.0 or later following method will suffice:
Using System.IO, System.Drawing and System.Drawing.Imaging
private void Form1_Load(object sender, EventArgs e)
{
string szTarget = "C:\\someImage.gif";
PictureBox pic = new PictureBox();
pic.Dock = DockStyle.Fill;
Image img = Image.FromFile(szTarget); // Load image fromFile into Image object
MemoryStream mstr = new MemoryStream(); // Create a new MemoryStream
img.Save(mstr, ImageFormat.Gif); // Save Image to MemoryStream from Image object
pic.Image = Image.FromStream(mstr); // Load Image from MemoryStream into PictureBox
this.Controls.Add(pic);
img.Dispose(); // Dispose original Image object (fromFile)
// after this you should be able to delete/manipulate the file
File.Delete(szTarget);
}
I am trying to change the code http://sites.google.com/site/webcamlibrarydotnet/winfrom-and-csharp-sample-code-and-download from picture box to image or bitmap as I dont want to display any image or plan to display, all I want is that it will output the image to file.
I have tried changing the webcam.cs from PictureBox _FrameImage to Bitmap _FrameImage and PictureBox ImageControl to Bitmap ImageControl and casting (Bitmap) at e.WebCamImage
As well as changing it on the main form:
private void bWebcam_Click(object sender, EventArgs e)
{
WebCam webcam = new WebCam();
Bitmap image = null;
webcam.InitializeWebCam(ref image);
webcam.Start();
webcam.Stop();
FileStream fstream = new FileStream("testWebcam.jpg", FileMode.Create);
image.Save(fstream, System.Drawing.Imaging.ImageFormat.Jpeg);
fstream.Close();
}
Unhappyly it doesnt seem to work so
how could I change it from picture
box to Bitmap or Image or similar
storage before saving it to a file or
save it to file directly ?
The source code I am using is:
http://sites.google.com/site/webcamlibrarydotnet/winfrom-and-csharp-sample-code-and-download
Instead of using the WebCam class, why not just use the WebCamCapture class directly (since you are not displaying this in a form) and handle the ImageCapture event directly. The event argument for the event contains the Image. You could, in the event handler save the image to disk. Alternately, if you want to use the sample and the WebCam class, and you have a form. Use a PictureBox but leave it hidden (set Visible to false) and then just copy the image from there and save to disk when you need to.
Here is some sample code of using the WebCamCapture class instead of the WebCam class. It should be noted that this code is based on the sample code from the link provided in the question. I have kept the style of the sample so that code lines up.
Edit: Adding example of using WebCamCapture instead of WebCam class. This code should be used to modify Form1.cs in the sample code.
// Instead of having WebCam as member variable, have WemCamCapture
WebCamCapture webCam;
// Change the mainWinForm_Load function
private void mainWinForm_Load(object sender, EventArgs e)
{
webCam = new WebCamCapture();
webCam.FrameNumber = ((ulong)(0ul));
webCam.TimeToCapture_milliseconds = 30;
webCam.ImageCaptured += webcam_ImageCaptured;
}
// Add the webcam Image Captured handler to the main form
private void webcam_ImageCaptured(object source, WebcamEventArgs e)
{
Image imageCaptured = e.WebCamImage;
// You can now stop the camera if you only want 1 image
// webCam.Stop();
// Add code here to save image to disk
}
// Adjust the code in bntStart_Click
// (yes I know there is a type there, but to make code lineup I am not fixing it)
private void bntStart_Click(object sender, Event Args e)
{
webCam.Start(0);
}
Using reflector you can see that internally the WebCam class uses a timer to simulate a framerate. Therefore calling start and stop right after each other will never generate an image since the application doesnt handle application events (and therefore the timer tick event) in between starting and stopping. You should register on the ImageChanged event and call stop in there.
Good luck
** Edit: the start logic **
public void Start(ulong FrameNum)
{
try
{
this.Stop();
this.mCapHwnd = capCreateCaptureWindowA("WebCap", 0, 0, 0, this.m_Width, this.m_Height, base.Handle.ToInt32(), 0);
Application.DoEvents();
SendMessage(this.mCapHwnd, 0x40a, 0, 0);
SendMessage(this.mCapHwnd, 0x432, 0, 0);
this.m_FrameNumber = FrameNum;
this.timer1.Interval = this.m_TimeToCapture_milliseconds;
this.bStopped = false;
this.timer1.Start();
}
catch (Exception exception)
{
MessageBox.Show("An error ocurred while starting the video capture. Check that your webcamera is connected properly and turned on.\r\n\n" + exception.Message);
this.Stop();
}
}
In WebCam.cs you have:
public void InitializeWebCam(ref System.Windows.Forms.PictureBox ImageControl)
{
webcam = new WebCamCapture();
webcam.FrameNumber = ((ulong)(0ul));
webcam.TimeToCapture_milliseconds = FrameNumber;
webcam.ImageCaptured += new WebCamCapture.WebCamEventHandler(webcam_ImageCaptured);
_FrameImage = ImageControl;
}
void webcam_ImageCaptured(object source, WebcamEventArgs e)
{
_FrameImage.Image = e.WebCamImage;
}
If you modify the ImageCaptured code you can do what you want: e.WebCamImage is an Image.
for example you could change/add constructor to accept a file name and, in the ImageCaptured event, you could save image to file.
If I've got a Silverlight Image control that downloads from a full URL, how can I get the size (in bytes) of the downloaded image without making another web call?
I can't find anything on the Image or the BitmapImage source behind it that would tell me. And even the DownloadProgress event on the BitmapImage only gives a percentage.
I never noticed it before, but that is kind of a strange gap in the framework...
You'll probably have to download the image by itself using a WebClient object. That'll give you a stream of bytes. You can check the length of the stream, and then create a bitmap from the stream.
Code to set up the web client and begin the download (note, it's an async call, so we assign an event handler to fire when it completes the download.)
WebClient wc = new WebClient();
wc.OpenReadCompleted += new OpenReadCompletedEventHandler(wc_OpenReadCompleted);
Uri someImageUri = new Uri("http://www.somesite.com/someimage.jpg");
wc.OpenReadAsync(someImageUri);
Here's an example of what the event handler method might look like:
void wc_OpenReadCompleted(object sender, OpenReadCompletedEventArgs e)
{
System.IO.Stream imageStream = e.Result;
long imageSize = imageStream.Length;
BitmapImage bi = new BitmapImage();
bi.SetSource(imageStream);
Image image = new Image();
image.Source = bi;
}
Obviously, if you already have an image control on your form, you wouldn't need to create a new one, or if you did want to create it, you'll have to add it to a parent panel of some kind...
~Chris