|
Well Done!
Happy it works for you now!
Got my '5' for sharing it with the community.
All the best,
Martin
|
|
|
|
|
Hello,
I currently have the problem that I want to update the contents (a string) of an item of a listbox with a textbox. Best would be on-the-fly so I tried listBox1.Items[listBox1.SelectedIndex] = textBox1.Text in TextChanged.
However I also want to be able to select which item to update by selecting the item in that very listbox. So I do something like textBox1.Text = listBox1.Items[listBox1.SelectedIndex] in SelectedIndexChanged of the listbox. (I say "something like" because the information includes other numbers that are also stored in a list along with the text.)
The problem with that is that selecting an item changes the text in the textbox and that again updates the item which implicitly calls an SelectedIndexChanged again (or so it seems). And during that updating of the listbox it happens that the selectedindex changes to -1. Which is a problem if you try to read that item...
I was wondering if there is an easier/better/cleaner way to accomplish what I try?
Stephan
|
|
|
|
|
Simple fix, just add if (listBox1.SelectedIndex > -1) so that the conditional is evaluated before making any updates. This should resolve your issue and update the reruired data. For example:
<br />
private void textBox1_TextChanged(object sender, EventArgs e)<br />
{<br />
if (listBox1.SelectedIndex > -1)<br />
{<br />
listBox1.Items[listBox1.SelectedIndex] = textBox1.Text;<br />
}<br />
}<br />
<br />
private void listBox1_SelectedIndexChanged(object sender, EventArgs e)<br />
{<br />
if (listBox1.SelectedIndex > -1)<br />
{<br />
textBox1.Text = listBox1.Items[listBox1.SelectedIndex].ToString();<br />
}<br />
}<br />
-Arcond
|
|
|
|
|
Thanks for your answer, I found another solution myself today.
Stephan
|
|
|
|
|
Hello,
I am looking for a fast and efficient way to read the overall/average brightness of a 640x480 JPEG image. Quick background, I have a CCTV camera outside my house that I can control through ActiveX control in my application. I have code to grab a sample image every x minutes. I then want to read the overall brightness of this sample image to determine if it is dark, bright, etc... so I can adjust gain on the camera, etc... Later I can create a set of parameters to map brightness value ranges to approximate time of day and camera settings.
I understand from what I have read that GetPixel is very slow and that some unsafe code may be quicker but I am still new to image processing in C# so I don't fully understand that.
I have written this code and it works but as you can see, it returns to me (via the messagebox) the brightness of each pixel as it iterates through the loop. This is really annoying because because it kept popping up a messagebox for every pixel and there was no way I was going to sit there and hit "OK" for the whole 640X480 image - so i had to end task. I will pause so you can finish laughing at me, it was a stupid mistake.
public void LoadImage()
{
string strFileName;
strFileName = textBox1.Text;
Bitmap original_bitmap = new Bitmap(strFileName);
pictureBox1.Image = original_bitmap;
Color pixel;
float b;
for (int y = 0; y < pictureBox1.Image.Height; y++)
{
for (int x = 0; x < pictureBox1.Image.Width; x++)
{
//Get a pixel from the image
pixel = original_bitmap.GetPixel(x, y);
//read brightness of pixel
b = pixel.GetBrightness();
//convert float value of pixel to string
string result = b.ToString();
//show pixel brightness to user
MessageBox.Show(result);
}
}
}
At any rate, how do I calculate and output the overall image brightness and not just the brightness of each individual pixel? For now I would like to output to a messagebox for my benefit but later I can obviously store it in a variable to be used by my app. Is the way I am doing it going to be fast enough without having to learn the unsafe code method?
Thanks in advance!!!
Mike
|
|
|
|
|
newb2vb wrote: JPEG
The JPEG is irrelevant, it's decompressed to a bitmap in memory.
newb2vb wrote: I then want to read the overall brightness of this sample image to determine if it is dark, bright, etc...
You may want to convert your pixels to HLS, in order to work out brightness. Then you can check luminance.
newb2vb wrote: . I will pause so you can finish laughing at me, it was a stupid mistake.
Not at all, it was a viable way to test your code and see what the result was. I'd have used Console.WriteLine and checked the result coming out in the IDE, but either way, it's just test code.
newb2vb wrote: I understand from what I have read that GetPixel is very slow and that some unsafe code may be quicker but I am still new to image processing in C# so I don't fully understand that.
Yes, you should lock the bits and iterate over them.
My image processing articles will show you how to iterate over the bits of an image, and I have one on converting between RGB and HLS as well. To calculate overall brightness, I'd add the lumanance of each pixel and work out the average across the bitmap.
Christian Graus - Microsoft MVP - C++
"I am working on a project that will convert a FORTRAN code to corresponding C++ code.I am not aware of FORTRAN syntax" ( spotted in the C++/CLI forum )
|
|
|
|
|
Christian,
Thank you for the answer, this helps alot. Just one more question if you don't mind.
The way the sample code I attached in my first message shows, the code will loop through x first from 0 to 640, then it will work y from 0 to 480. I understand what you suggested above to do a simple add and divide by to get the average. How do you recommend I buffer/add all 640 iterations on the first loop, x (then divide by 640) and then do the same for y? Or do I have the concept right but am approaching it all wrong?
Thanks,
Mike
|
|
|
|
|
I'd just keep a running total and then divide it. Your only issue is if the number of pixels means a high value would be bigger than the max of an unsigned int. Then you need to be more creative.
Christian Graus - Microsoft MVP - C++
"I am working on a project that will convert a FORTRAN code to corresponding C++ code.I am not aware of FORTRAN syntax" ( spotted in the C++/CLI forum )
|
|
|
|
|
Christian,
This simple problem is kicking my butt!!!! I figured out how to do all of the math and sum everything up then divide at the end and I got the code working!
However, the getpixel method as we already knew it would be, was very slow to give me the message box at the end with the one average number.
Therefore, I agree that I should be locking the image and I have now done that and I can get my x and y loops working going through the image no problem. However, when the unsafe/locked image approach is used, you can no longer use pixel.getbrightness like I could do using the other message (error says that image has already been locked.
So the last question I have is, if I have locked my image and can iterate through each pixel using the unsafe/locked method, how do I read the brightness of each pixel?
Thanks again!
Mike
|
|
|
|
|
You would iterate over the bits of the image, which are the r/g/b components ( actually, they are b/g/r in memory ) and then convert the resultant color to HLS, using the code from my article on color spaces.
Christian Graus - Microsoft MVP - C++
"I am working on a project that will convert a FORTRAN code to corresponding C++ code.I am not aware of FORTRAN syntax" ( spotted in the C++/CLI forum )
|
|
|
|
|
Hi,
here is another idea to speed things up.
you could create a smaller image based on the image you have
using the constructor
public Bitmap (
Image original,
int width,
int height
)
say you reduce by 8 in both directions (640*480 --> 80*60)
Now get the brightness of every pixel, and average that (accumulate them all in a
float, divide by width*height at the end).
I expect a significant speed gain, while the reduction should have little or no
influence on the image brightness.
Remark: I have not done this myself yet...
|
|
|
|
|
My Code below is using a backgroundWorker component. My entire GUI freezes while it loads the content tree from the database. The window even says (not responding) at the top.
My desired result is to have a loading animation play while loading the data from the database... am I going at this wrong? Shouldn't the loading take place in the background, preventing the GUI from freezing up?
Any ideas?
___________________________________________________________
private void bgWorker_DoWork(object sender, DoWorkEventArgs e)
{
LoadCompanies();
}
private void bgWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
ShowLoadingAnimation(false);
}
private void LoadCompanies(object sender, EventArgs e)
{
ShowLoadingAnimation(true);
this.bgWorker.RunWorkerAsync();
}
private delegate void LoadCompaniesDelegate();
public void LoadCompanies()
{
PROSOCO.Library.Companies.Criteria criteria = PROSOCO.Library.Companies.Criteria.GetCriteria();
BindingSource src = new BindingSource();
PROSOCO.Library.Companies.Nodes nodes = PROSOCO.Library.Companies.Nodes.GetNodes(criteria.NodeCriteria());
src.DataSource = nodes;
if (pnlNavigation.InvokeRequired)
{
LoadCompaniesDelegate find = new LoadCompaniesDelegate(LoadCompanies);
this.Invoke(find, new object[] { });
}
else
{
pnlNavigation.ContentTree.ParentMember = "ParentId";
pnlNavigation.ContentTree.ValueMember = "Id";
pnlNavigation.ContentTree.DisplayMember = "Name";
pnlNavigation.ContentTree.PrefixMember = "CustomerNumber";
pnlNavigation.ContentTree.PostfixMember = "CityState";
pnlNavigation.ContentTree.DataSource = src;
}
_activeModule = "Companies";
}
|
|
|
|
|
Hi,
you seem aware of the fact that controls are not thread-safe, so you try
to use InvokeRequired and Invoke(), but IMO there are some mistakes in the
implementation\; the normal pattern is a method that ONLY contains something like
if (InvokeRequired) Invoke(delegate)
else do_whatever_needs_to_be_done_to_the_GUI
Your public void LoadCompanies() method however contains stuff before the if;
that stuff will execute twice, the second time on the GUI (bad for responsiveness);
it also generates two BindingSource objects, only the second one will be used.
I suggest you split the method in two parts, i.e. move the second half
(the if-else) to a separate method, and apply the pattern to it, hence avoiding
the first half to be executed twice (of which once on the GUI)
|
|
|
|
|
It still has the same isse.
I'm not sure I understood your suggestion, but I updated it as shown below. However, the gui still hangs while loading the companies.
public void LoadCompanies()
{
if (pnlNavigation.InvokeRequired)
{
LoadCompaniesDelegate find = new LoadCompaniesDelegate(LoadCompanies);
this.Invoke(find, new object[] { });
}
else
{
PROSOCO.Library.Companies.Criteria criteria = PROSOCO.Library.Companies.Criteria.GetCriteria();
BindingSource src = new BindingSource();
PROSOCO.Library.Companies.Nodes nodes = PROSOCO.Library.Companies.Nodes.GetNodes(criteria.NodeCriteria());
src.DataSource = nodes;
pnlNavigation.ContentTree.ParentMember = "ParentId";
pnlNavigation.ContentTree.ValueMember = "Id";
pnlNavigation.ContentTree.DisplayMember = "Name";
pnlNavigation.ContentTree.PrefixMember = "CustomerNumber";
pnlNavigation.ContentTree.PostfixMember = "CityState";
pnlNavigation.ContentTree.DataSource = src;
}
_activeModule = "Companies";
}
|
|
|
|
|
This still makes it execute on the GUI thread.
One idea would be to run everything but the actual GUI stuff in the background. When BackgroundWorker finishes, you can return the data that you need to load the GUI stuff (Binding Source) through the DoWorkEventArgs.Result (which gets sent to the RunWorkerCompletedEvent when it finish DoWork). You then cast the Result to a BindingSource and do the last portion of your code (after src.DataSource = nodes; ) in the GUI thread.
|
|
|
|
|
I am new to C# so forgive me for not "just getting it" but this is what I have so far. I am doing something wrong because "BindingSource src = (BindingSource)e.Result;" Doesn't seem to be loading any data...
Did I atleast get everything running in the background except the GUI stuff?
Can you tell why I wouldn't be loading any data?
Thanks in advance.
<br />
private delegate object LoadCompaniesDelegate();<br />
<br />
public object LoadCompanies()<br />
{<br />
BindingSource src = new BindingSource();<br />
if (pnlNavigation.InvokeRequired)<br />
{<br />
LoadCompaniesDelegate find = new LoadCompaniesDelegate(LoadCompanies);<br />
this.Invoke(find, new object[] { });<br />
}<br />
else<br />
{<br />
PROSOCO.Library.Companies.Criteria criteria = PROSOCO.Library.Companies.Criteria.GetCriteria(); <br />
PROSOCO.Library.Companies.Nodes nodes = PROSOCO.Library.Companies.Nodes.GetNodes(criteria.NodeCriteria()); <br />
src.DataSource = nodes;<br />
MessageBox.Show("Loaded Notes");<br />
}<br />
return src;<br />
} <br />
private void bgWorker_DoWork(object sender, DoWorkEventArgs e)<br />
{<br />
e.Result = LoadCompanies();<br />
}<br />
private void bgWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)<br />
{<br />
BindingSource src = (BindingSource)e.Result;<br />
pnlNavigation.ContentTree.ParentMember = "ParentId";<br />
pnlNavigation.ContentTree.ValueMember = "Id";<br />
pnlNavigation.ContentTree.DisplayMember = "Name";<br />
pnlNavigation.ContentTree.PrefixMember = "CustomerNumber";<br />
pnlNavigation.ContentTree.PostfixMember = "CityState";<br />
pnlNavigation.ContentTree.DataSource = src;<br />
_activeModule = "Companies";<br />
<br />
MessageBox.Show("Completed");<br />
} <br />
|
|
|
|
|
Remove the
---
if (pnlNavigation.InvokeRequired)
{
LoadCompaniesDelegate find = new LoadCompaniesDelegate(LoadCompanies);
this.Invoke(find, new object[] { });
}
else
{
--
Don't touch anything that has to do with a GUI element inside of your background worker DoWork for now... just keep what you have in RunWorkerCompleted.
Currently it is taking the first path (InvokeRequired path), so the data won't be loaded into the BindingSource (since that is done in the Else) part.
|
|
|
|
|
Thank you Phil.
I still get the (Not Responding) for a couple seconds when it gets to "pnlNavigation.ContentTree.DataSource = src;" This is a lot of data to load. Since it is taking the most time, shouldn't that part be inside bgWorker_DoWork? ... but it cannot because it is updating the GUI ... so I don't konw what to do.
|
|
|
|
|
What do you think about this explanation?
The freezing is because the control is being filled out by the data source, and the only thread allowed to do that is the UI. So, if your data source is large there isn't a way around the 2 seconds pause... unless...
You could manually fill out your control by looping over the data source yourself, and every 10 or so, do a DoEvents() call to keep the UI fresh.
|
|
|
|
|
justin.moses wrote: So, if your data source is large there isn't a way around the 2 seconds pause... unless...
If your data set is too large to be displayed all at once, your best bet is filling the dataset a few rows at a time, preferrably in the Application.Idle event handler.
p.s. don't use DoEvents(). It's evil, trust me.
|
|
|
|
|
No, that is not how I explained it; the else part executes on the GUI thread and
hence must be fast. Take out everything that does not need to be there, and
have it execute before you call public void LoadCompanies().
|
|
|
|
|
I'm not quite sure what the LoadCompanies is supposed to be doing, but focus on this part:
if (pnlNavigation.InvokeRequired)
{
LoadCompaniesDelegate find = new LoadCompaniesDelegate(LoadCompanies);
this.Invoke(find, new object[] { });
}
else
{
pnlNavigation.ContentTree.ParentMember = "ParentId";
pnlNavigation.ContentTree.ValueMember = "Id";
pnlNavigation.ContentTree.DisplayMember = "Name";
pnlNavigation.ContentTree.PrefixMember = "CustomerNumber";
pnlNavigation.ContentTree.PostfixMember = "CityState";
pnlNavigation.ContentTree.DataSource = src;
}
As you can see by the comments, the BackgroundWorker will indeed run LoadCompanies on its own thread. You then check for InvokeRequired -- since crossthreaded operations are illegal, this function returns true if the object is on another thread. If it is (which I'm guessing pnlNavigation is a GUI element on the GUI thread), you then call Invoke to call LoadCompanies, which forces it to run on the GUI thread! So you're back where you started, and you have to load from the database AGAIN!
|
|
|
|
|
I changed LoadCompanies so as best I can tell, it isn't loading anything twice.
It still has the same (not responding) issue while it loads all the records into the ContentTree.
public void LoadCompanies()
{
if (pnlNavigation.InvokeRequired)
{
LoadCompaniesDelegate find = new LoadCompaniesDelegate(LoadCompanies);
this.Invoke(find, new object[] { });
}
else
{
PROSOCO.Library.Companies.Criteria criteria = PROSOCO.Library.Companies.Criteria.GetCriteria();
BindingSource src = new BindingSource();
PROSOCO.Library.Companies.Nodes nodes = PROSOCO.Library.Companies.Nodes.GetNodes(criteria.NodeCriteria());
src.DataSource = nodes;
pnlNavigation.ContentTree.ParentMember = "ParentId";
pnlNavigation.ContentTree.ValueMember = "Id";
pnlNavigation.ContentTree.DisplayMember = "Name";
pnlNavigation.ContentTree.PrefixMember = "CustomerNumber";
pnlNavigation.ContentTree.PostfixMember = "CityState";
pnlNavigation.ContentTree.DataSource = src;
}
_activeModule = "Companies";
}
|
|
|
|
|
Yes, the double loading was only one problem. Invoke forces it to run on the thread of the object that you called it from.
|
|
|
|
|
Try changing the LoadCompanies method to this instead:
public void LoadCompanies(DoWorkEventArgs e)
{
PROSOCO.Library.Companies.Criteria criteria = PROSOCO.Library.Companies.Criteria.GetCriteria();
BindingSource src = new BindingSource();
PROSOCO.Library.Companies.Nodes nodes = PROSOCO.Library.Companies.Nodes.GetNodes(criteria.NodeCriteria());
src.DataSource = nodes;
e.Result = src;
}
Then change your RunWorkerCompleted handler to look like this:
private void bgWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
BindingSource src = (BindingSource)e.Result;
pnlNavigation.ContentTree.ParentMember = "ParentId";
pnlNavigation.ContentTree.ValueMember = "Id";
pnlNavigation.ContentTree.DisplayMember = "Name";
pnlNavigation.ContentTree.PrefixMember = "CustomerNumber";
pnlNavigation.ContentTree.PostfixMember = "CityState";
pnlNavigation.ContentTree.DataSource = src;
ShowLoadingAnimation(false);
_activeModule = "Companies";
}
|
|
|
|