I’m using WinForms. My application works like a simple image document (tif) viewer. The tif/image documents I work with have multiple pages, so by pressing the next button my application goes to the next page. The problem with my application is that it is slow when I move to the next page. Previously, my application was slower. I’ve modified the application a little bit to make it faster, but it’s still not fast enough.
I’ve compared my application’s speed to Windows Photo Viewer and the result was that my application still needs improvement on performance. Does anyone know how I could make my application faster?
In the link below I’ve provided a sample tif document for testing purposes.
Link: http://www.filedropper.com/tiftestingdoc
My Code:
FileStream _stream;
Image _myImg; // setting the selected tiff
string _fileName;
private int intCurrPage = 0; // defining the current page
private void Open_Btn_Click(object sender, EventArgs e)
{
if (openFileDialog1.ShowDialog() == DialogResult.OK)
{
lblFile.Text = openFileDialog1.FileName; //Shows the filename in the lable
Image img = Image.FromFile(openFileDialog1.FileName);
pictureBox1.Image = img;
Size size = new Size(img.Height, img.Width);
pictureBox1.Size = size;
Open_Image_Control();
}
}
public void Open_Image_Control()
{
Image myBmp;
if (_myImg == null) //I made a copy of the file because i want to be able to modify the file in the directory for example go to directory and delete the file while still having the ability to view it on the application
{
_fileName = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString("N"));
File.Copy(#"C:\my_Image_document", _fileName);
_stream = new FileStream(_fileName, FileMode.Open, FileAccess.Read);
_myImg = Image.FromStream(_stream);
}
int intPages = _myImg.GetFrameCount(System.Drawing.Imaging.FrameDimension.Page); // getting the number of pages of this tiff
intPages--; // the first page is 0 so we must correct the number of pages to -1
lblNumPages.Text = Convert.ToString(intPages); // showing the number of pages
lblCurrPage.Text = Convert.ToString(intCurrPage); // showing the number of page on which we're on
_myImg.SelectActiveFrame(System.Drawing.Imaging.FrameDimension.Page, intCurrPage); // going to the selected page
myBmp = new Bitmap(_myImg, pictureBox1.Width, pictureBox1.Height);
//myBmp = new Bitmap(_myImg, pictureBox1.Height, pictureBox1.Width);
pictureBox1.Image = myBmp; // showing the page in the pictureBox1
}
private void NextPage_btn_Click(object sender, EventArgs e)
{
if (intCurrPage == Convert.ToInt32(lblNumPages.Text)) // if you have reached the last page it ends here
// the "-1" should be there for normalizing the number of pages
{ intCurrPage = Convert.ToInt32(lblNumPages.Text); }
else
{
intCurrPage++; //page increment (Goes to next page)
Open_Image_Control();
}
}
Load the image directly into the PictureBox, then to change pages call the pictureBox1.Image.SelectActiveFrame() method directly and refresh the PictureBox.
This prevents making new bitmaps copies of each page each time.
That was causing extra memory to be allocated each time and slowness while it copies all the pixels from the page.
Please see the code changes below:
// Variable to hold the current page number
private int intCurrPage = 0;
private int intTotalPages = 0;
private void Open_Btn_Click(object sender, EventArgs e)
{
if (openFileDialog1.ShowDialog() == DialogResult.OK)
{
lblFile.Text = openFileDialog1.FileName;
// Before loading you should check the file type is an image
pictureBox1.Image = Image.FromFile(openFileDialog1.FileName);
pictureBox1.Size = new Size(pictureBox1.Image.Height, pictureBox1.Image.Width);
// Reset the current page when loading a new image
intCurrPage = 0;
intTotalPages = pictureBox1.Image.GetFrameCount(System.Drawing.Imaging.FrameDimension.Page);
lblNumPages.Text = intTotalPages.ToString();
}
}
private void NextPage_btn_Click(object sender, EventArgs e)
{
// Check that the current page is not going past the max page
if (intCurrPage < (intTotalPages-1))
{
//page increment (Go to next page)
intCurrPage++;
// Directly increment the active frame within the image already in the PictureBox
pictureBox1.Image.SelectActiveFrame(System.Drawing.Imaging.FrameDimension.Page, intCurrPage);
// Adjust the size of the picturebox control to the size of the current page.
// not sure if this is necessary, but including it due to prior example
pictureBox1.Size = new Size(pictureBox1.Image.Height, pictureBox1.Image.Width);
// Refresh the PictureBox so that it will show the currently active frame
pictureBox1.Refresh();
lblCurrPage.Text = intCurrPage.ToString();
}
}
Also, your code to copy the image had a hard coded directory name that was throwing errors for me, so I removed that.
Related
Every time the user presses a button my application gets the image from the web, but this slow since the image isn't getting cached. Any way I can cache the image so it doesn't download it over and over making things faster.
This is my code so you know how my application works.
if (listBox1.SelectedIndex == 0)
{
richTextBox1.Text = "Explore any game with Dex";
pictureBox1.Load("THE LINK ");
ProtoxRe.lol = 0;
}
else if ((listBox1.SelectedIndex == 1))
{
richTextBox1.Text = "SOME TEXT";
pictureBox1.Load("LINK");
ProtoxRe.lol = 1;
}
There's probably more sophisticated methods, and this assumes your image is not expected to change, but you can do something like this:
//Image class is from this library
using System.Drawing;
//The scope of this will depend on where you need to access it from
Image img;
private Image GetImage()
{
if (img == null)
{
img = GetImageFromHttp();
}
return img;
}
This is very basic, and has a number of flaws; you're not caching it between sessions so every application start will require the http get, and you're not checking if the image on the web is different, but it's a starting point.
I'm generating a picture in the picturebox
pictureBox1.Image = Image.FromStream(imageActions.GetImage(reader["ID_no"].ToString()));
It is working perfectly, but I am also creating an option for the user to edit it via any application (Let's example macromedia) So I created a button and did the thing.
private void button2_Click(object sender, EventArgs e)
{
Process photoViewer = new Process();
photoViewer.StartInfo.FileName = #"C:\Program Files\Macromedia\Fireworks 8\Fireworks.exe";
photoViewer.StartInfo.Arguments = ___________;
photoViewer.Start();
}
I understand that in the photoViewer.StartInfo.Arguments = you can put here the path of the image, but in my case. the image is stored in the Database as Image datatype. any ideas?
In order to load the image in an external application, you will first of all need to save it to the disk.
After the application has closed you will need to load the updated image to display to the user.
The Image property of the PictureBox control has a Save method you can call:
string tempFile = System.IO.Path.GetTempFileName();
pictureBox1.Image.Save(tempFile);
You can then pass the tempFile value as a parameter to the photoViewer process (I used MSPaint as a Proof of Concept):
Process photoViewer = new Process();
photoViewer.StartInfo.FileName = #"C:\Windows\System32\MSPaint.exe";
photoViewer.StartInfo.Arguments = tempFile;
photoViewer.EnableRaisingEvents = true;
photoViewer.Exited += photoViewer_Exited;
photoViewer.Start();
The two lines of photoViewer.EnableRaisingEvents = true and photoViewer.Exited += photoViewer_Exited; will tell your application when the photoViewer process exits, and this is a good place for you to load the image and display to your users.
private void photoViewer_Exited(object sender, EventArgs e)
{
pictureBox1.Image = Image.FromFile(tempFile);
}
Note: string tempFile will need to be a class member variable so it can be accessed in the two functions.
I'm trying to capture the finger print scanned by this device-> http://www.nitgen.com/eng/product/finkey.html
I'm able to scan the fingerprint and save the binary data successfully. I'm also able to display the fingerprint in the picture box. However when I'm trying to save the fingerprint displayed in the picture box, I'm getting an error that the Image of the picturebox is null.
Below is my code of capturing the fingerprint and saving the image from the picturebox.
public class Form1 : System.Windows.Forms.Form
{
public NBioBSPCOMLib.NBioBSP objNBioBSP;
public NBioBSPCOMLib.IExtraction objExtraction;
private PictureBox pictureExtWnd;
private void Form1_Load(object sender, System.EventArgs e)
{
// Create NBioBSP object
objNBioBSP = new NBioBSPCOMLib.NBioBSPClass();
objExtraction = (NBioBSPCOMLib.IExtraction)objNBioBSP.Extraction;
pictureExtWnd.Image = new Bitmap(pictureExtWnd.Width, pictureExtWnd.Height);
}
private void buttonEnroll_Click(object sender, System.EventArgs e)
{
//tell NBIO to not display their fingerprint scanning window
objExtraction.WindowStyle = NBioBSPType.WINDOW_STYLE.INVISIBLE;
//set the color of the fingerprint captured
objExtraction.FPForeColor = "000000";
//set the color of the background where the fingerprint will be displayed
objExtraction.FPBackColor = "FFFFFF";
//tell NBIO that the scanned fingerprint will be displayed in the picturebox
//by giving the handle control to NBIO
objExtraction.FingerWnd = pictureExtWnd.Handle.ToInt32();
//start scanning the fingerprint. This is also where the fingerprint
//is displayed in the picturebox.
objExtraction.Capture((int)NBioBSPType.FIR_PURPOSE.VERIFY);
//if there's no problem while scanning the fingerprint, save the fingerprint image
if (objExtraction.ErrorCode == NBioBSPError.NONE)
{
string fileName = RandomString.GetRandomString(16, true) + ".bmp";
using (SaveFileDialog sfdlg = new SaveFileDialog())
{
sfdlg.Title = "Save Dialog";
sfdlg.Filter = "Bitmap Images (*.bmp)|*.bmp|All files(*.*)|*.*";
if (sfdlg.ShowDialog(this) == DialogResult.OK)
{
pictureExtWnd.Image.Save(sfdlg.FileName, ImageFormat.Bmp);
MessageBox.Show("FingerPrint Saved Successfully.");
}
}
}
else
{
MessageBox.Show("FingerPrint Saving Failed!");
}
}
}
I've tried enclosing inside
using(Graphics g = new Graphics)
{
objExtraction.Capture((int)NBioBSPType.FIR_PURPOSE.VERIFY);
}
since I've read that when doing edits on an image, you need to use graphics. but nothing is happening obviously since the api isn't using the graphic object I instantiated.
UPDATE:
This is what I ended up doing:
using (SaveFileDialog sfdlg = new SaveFileDialog())
{
sfdlg.Title = "Save Dialog";
sfdlg.Filter = "Bitmap Images (*.bmp)|*.bmp|All files(*.*)|*.*";
if (sfdlg.ShowDialog(this) == DialogResult.OK)
{
Graphics gfx = this.pictureExtWnd.CreateGraphics();
Bitmap bmp = new Bitmap(this.pictureExtWnd.Width, this.pictureExtWnd.Height);
this.pictureExtWnd.DrawToBitmap(bmp, new Rectangle(0, 0, this.pictureExtWnd.Width, this.pictureExtWnd.Height));
bmp.Save(sfdlg.FileName, ImageFormat.Bmp);
gfx.Dispose();
//pictureExtWnd.Image.Save(sfdlg.FileName, ImageFormat.Bmp);
MessageBox.Show("Saved Successfully...");
}
}
objExtraction.FingerWnd = pictureExtWnd.Handle.ToInt32();
You passed the window handle to the fingerprint scanner. That's a common way to tell a chunk of native code about a window that it can draw to. It will typically sub-class the window procedure to respond to WM_PAINT requests, for example, same idea as NativeWindow.WndProc().
Implied however is that the Image property is useless. That native code has no idea that this is a PictureBox control and that it has an Image property. It only knows about the native window that was created for the control.
Do look in the api for an option to save the image, this ought to be available. If not then your first shot at saving it is using the picture box' DrawToBitmap() method. Which may work if the scanner implements the WM_PRINT message handler. If that doesn't work then your only other backup plan is to use Graphics.CopyFromScreen(). Which will always work, as long as the window is in the foreground. Similar to using the PrtSc button on your keyboard, a screen-shot.
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.
In my (Silverlight) weather app I am downloading up to 6 seperate weather radar images (each one taken about 20 mins apart) from a web site and what I need to do is display each image for a second then at the end of the loop, pause 2 seconds then start the loop again. (This means the loop of images will play until the user clicks the back or home button which is what I want.)
So, I have a RadarImage class as follows, and each image is getting downloaded (via WebClient) and then loaded into a instance of RadarImage which is then added to a collection (ie: List<RadarImage>)...
//Following code is in my radar.xaml.cs to download the images....
int imagesToDownload = 6;
int imagesDownloaded = 0;
RadarImage rdr = new RadarImage(<image url>); //this happens in a loop of image URLs
rdr.FileCompleteEvent += ImageDownloadedEventHandler;
//This code in a class library.
public class RadarImage
{
public int ImageIndex;
public string ImageURL;
public DateTime ImageTime;
public Boolean Downloaded;
public BitmapImage Bitmap;
private WebClient client;
public delegate void FileCompleteHandler(object sender);
public event FileCompleteHandler FileCompleteEvent;
public RadarImage(int index, string imageURL)
{
this.ImageIndex = index;
this.ImageURL = imageURL;
//...other code here to load in datetime properties etc...
client = new WebClient();
client.OpenReadCompleted += new OpenReadCompletedEventHandler(wc_OpenReadCompleted);
client.OpenReadAsync(new Uri(this.ImageURL, UriKind.Absolute));
}
private void wc_OpenReadCompleted(object sender, OpenReadCompletedEventArgs e)
{
if (e.Error == null)
{
StreamResourceInfo sri = new StreamResourceInfo(e.Result as Stream, null);
this.Bitmap = new BitmapImage();
this.Bitmap.SetSource(sri.Stream);
this.Downloaded = true;
FileCompleteEvent(this); //Fire the event to let the app page know to add it to it's List<RadarImage> collection
}
}
}
As you can see, in the class above I have exposed an event handler to let my app page know when each image has downloaded. When they have all downloaded I then run the following code in my xaml page - but only the last image ever shows up and I can't work out why!
private void ImageDownloadedEventHandler(object sender)
{
imagesDownloaded++;
if (imagesDownloaded == imagesToDownload)
{
AllImagesDownloaded = true;
DisplayRadarImages();
}
}
private void DisplayRadarImages()
{
TimerSingleton.Timer.Stop();
foreach (RadarImage img in radarImages)
{
imgRadar.Source = img.Bitmap;
Thread.Sleep(1000);
}
TimerSingleton.Timer.Start(); //Tick poroperty is set to 2000 milliseconds
}
private void SingleTimer_Tick(object sender, EventArgs e)
{
DisplayRadarImages();
}
So you can see that I have a static instance of a timer class which is stopped (if running), then the loop should show each image for a second. When all 6 have been displayed then it pauses, the timer starts and after two seconds DisplayRadarImages() gets called again.
But as I said before, I can only ever get the last image to show for some reason and I can't seem to get this working properly.
I'm fairly new to WP7 development (though not to .Net) so just wondering how best to do this - I was thinking of trying this with a web browser control but surely there must be a more elegant way to loop through a bunch of images!
Sorry this is so long but any help or suggestions would be really appreciated.
Mike
You can use a background thread with either a Timer or Sleep to periodically update your image control.
Phạm Tiểu Giao - Threads in WP7
You'll need to dispatch updates to the UI with
Dispatcher.BeginInvoke( () => { /* your UI code */ } );
Why don't you add the last image twice to radarImages, set the Timer to 1000 and display just one image on each tick?