I have trouble with continuous image loading&processing&display in Swing:
Below there is simple case where clicking a button causes my program to grab frame from webcam and then display this picture in jlabel. It works as it should, but i have to consecutively click this "Start" button in order to get new image(frame) shown.
private void StartActionPerformed(java.awt.event.ActionEvent evt) {
displayed_img = this.getPIC_COLOR(player);
img_field.setIcon(new ImageIcon(displayed_img));
}
Im more demanding and i would like to make some image processing during live video stream, therefore i need to "grab" & "show" my frames continuously. You might say that i can just launch webcam stream, but it is not what i want to achieve since i'm about to implement some threshholding/etc. functions which will modify my image on-fly. Therefore i need to perform this grabbing&processin&display as fast as possible (delay is a no-no since i want to achieve something like 10 fps including image processing). Despite the fact that i delay is utterly undesirable i tried to make some Thread.sleep however it didnt work.
Simple while(true) /* presented below */ does not work, my program "hangs" and not even single frame is displayed, although in debugger it keeps working over and over.
private void StartActionPerformed(java.awt.event.ActionEvent evt) {
while(true){
displayed_img = this.getPIC_COLOR(player);
//threshholding & further processing...
img_field.setIcon(new ImageIcon(displayed_img));
}
}
just in case getPIC_COLOR() was required, i paste it below:
public BufferedImage getPIC_COLOR(Player player){
FrameGrabbingControl frameGrabber = (FrameGrabbingControl)player.getControl("javax.media.control.FrameGrabbingControl");
Buffer buf = frameGrabber.grabFrame();
Image img = (new BufferToImage((VideoFormat)buf.getFormat()).createImage(buf));
BufferedImage buffImg = new BufferedImage(img.getWidth(null), img.getHeight(null), BufferedImage.TYPE_INT_RGB);
Graphics2D g = buffImg.createGraphics();
g.drawImage(img, null, null);
return buffImg;
}
Any help greatly appreciated.
Ok, after some reading i managed to write some SwingWorker Code:
private void SingleFrameActionPerformed(java.awt.event.ActionEvent evt) {
SwingWorker<Void, BufferedImage> worker = new SwingWorker<Void, BufferedImage>() {
@Override
protected Void doInBackground() {
while (!isCancelled()) {
displayed_img = getPIC_COLOR(player);
publish(displayed_img);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
break;
}
}
return null;
}
@Override
protected void process(List mystuff) {
Iterator it = mystuff.iterator();
while (it.hasNext()) {
img_field.setIcon(new ImageIcon(displayed_img));
try {
ImageIO.write(displayed_img, "png", new File("c:\\testimg.jpg"));
} catch (IOException ex) {
Logger.getLogger(TestCAMGUI.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
@Override
protected void done() {
infoBAR.setText("FINISHED");
}
};
worker.execute();
}
Now what seriously bothers me is the fact that my img_field
is not updated in swing, although ImageIO.write
present in process
works, and image is "redrawn" on C:\ with MAD speed ("mad speed" is highly desirable). Moreover my GUI is still frozen even though i created this swing worker thread...
btw i also tried to "sleep" this saving thread for a second, however my GUI hangs even with this additional sleep
Now some explanations about my code:
doInBackground()
returns void, as i dont need to return anything, since i assume this process will run till cancelled (which is unlikely to happen, unless program is closed).- inside
process()
i've included try/catch block withImageIO.write
just to make sure my thread launched and works - i didnt use 'SwingUtilities.invokeLater' due to fact that browsing guides/tutorials i've read that it is better to use SwingWorker in my case
- What bothers me furthermore: in
process
i operate on list which enlarges... i dont need that, i just need a single element and thats all. So is there a way to collect single object fromdoInBackground()
? making list seems memory waste for me. Or maybe i shouldclear()
the list at the end ofprocess()
? (in order to reduce memory allocated)
Any further hints are appreciated.
You must not run any kind of infinite loop of that form in a method that is called from an event (unless you explicitly intend to hang your user interface, which would be odd to say the least).
You should run your frame grab in a separate thread, and then use something like SwingUtilities.invokeLater to display it.