0

I'm building a small java application to load images from a USB camera or display an error message (image) if the USB camera is not found. The application needs to load and start 'CaptureImage()' automatically, but I'm getting NullPointerException error under my 'CaptureImage()' method.

If I call 'CaptureImage()' from a button click on the gui (instead of trying to kick it off in 'initialize()' via starting a thread), then it works properly. Any guidance on how to handle properly would be most appreciated.


public class WebcamCapture_gui {
    
    JFrame frmWebcamCapture_gui;
    static Canvas canvas = new Canvas();
    JPanel panel = new JPanel();
    static Image notFound = null;
    static boolean running = true;

    
    public static void main(String[] args) {
        
        EventQueue.invokeLater(new Runnable() {
            public void run() {
                try {
                    WebcamCapture_gui window = new WebcamCapture_gui();
                    window.frmWebcamCapture_gui.setVisible(true);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });
    }
    
    public WebcamCapture_gui() throws IOException {
        initialize();
    }


    private void initialize() {

        JButton btnNewButton = new JButton("OK", new ImageIcon(WebcamCapture_gui.class.getResource("/resources/ok-32.png")));
        btnNewButton.setFont(new Font("Lucida Grande", Font.BOLD, 16));
        btnNewButton.setMnemonic('o');
        btnNewButton.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                
                CaptureImage();
            }
        });
        btnNewButton.setBounds(918, 573, 156, 83);
        
        canvas.setBounds(6, 6, 1052, 547);
        
        frmWebcamCapture_gui = new JFrame();
        frmWebcamCapture_gui.setResizable(false);
        frmWebcamCapture_gui.setUndecorated(true);
        frmWebcamCapture_gui.setTitle("Camera Test");
        frmWebcamCapture_gui.setSize(1080, 662);
        frmWebcamCapture_gui.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frmWebcamCapture_gui.getContentPane().setLayout(null);
        
        panel.setBorder(new EtchedBorder(EtchedBorder.LOWERED, null, null));
        panel.setBounds(6, 6, 1068, 563);
        panel.setLayout(null);
        panel.add(canvas);
        frmWebcamCapture_gui.getContentPane().add(panel);
        frmWebcamCapture_gui.getContentPane().add(btnNewButton);
        
        File classPathInput = new File(WebcamCapture_gui.class.getResource("/resources/USB_not_found.png").getFile());
        try {
            notFound = ImageIO.read(classPathInput);
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        
        /*
        *  If I remove this Thread start and use the button to call 'CaptureImage()' instead, then the images will load and no NPE.
        */
        Thread t1 = new Thread(new thread1());
        
        t1.start();
        
        try { 
            t1.join();
        } catch (Exception e1) {
            System.out.println(e1);
        }
    }
    
    
    public static class thread1 implements Runnable {
        @Override
        public void run() {
            
            CaptureImage();
            
        }
    }

    
    public static void CaptureImage() {
        
        Graphics g = canvas.getGraphics();
        
        Webcam webcam = Webcam.getDefault();
        
        if (webcam != null) {
            webcam.open();
            g.drawImage(webcam.getImage(), 0, 0, 1060, 554, null);
            webcam.close();
        } else {
            g.drawImage(notFound, 0, 0, 1060, 547, null);
        }
    }
}

Stack Trace:

javax.imageio.IIOException: Can't read input file!
    at java.desktop/javax.imageio.ImageIO.read(ImageIO.java:1308)
    at webcamCapture.WebcamCapture_gui.initialize(WebcamCapture_gui.java:80)
    at webcamCapture.WebcamCapture_gui.<init>(WebcamCapture_gui.java:44)
    at webcamCapture.WebcamCapture_gui$1.run(WebcamCapture_gui.java:34)
    at java.desktop/java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:316)
    at java.desktop/java.awt.EventQueue.dispatchEventImpl(EventQueue.java:770)
    at java.desktop/java.awt.EventQueue$4.run(EventQueue.java:721)
    at java.desktop/java.awt.EventQueue$4.run(EventQueue.java:715)
    at java.base/java.security.AccessController.doPrivileged(AccessController.java:391)
    at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:85)
    at java.desktop/java.awt.EventQueue.dispatchEvent(EventQueue.java:740)
    at java.desktop/java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:203)
    at java.desktop/java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:124)
    at java.desktop/java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:113)
    at java.desktop/java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:109)
    at java.desktop/java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:101)
    at java.desktop/java.awt.EventDispatchThread.run(EventDispatchThread.java:90)
[Thread-0] INFO com.github.sarxos.webcam.Webcam - WebcamDefaultDriver capture driver will be used
[Thread-0] WARN com.github.sarxos.webcam.Webcam - No webcam has been detected!
Exception in thread "Thread-0" java.lang.NullPointerException: Cannot invoke "java.awt.Graphics.drawImage(java.awt.Image, int, int, int, int, java.awt.image.ImageObserver)" because "g" is null
    at webcamCapture.WebcamCapture_gui.CaptureImage(WebcamCapture_gui.java:120)
    at webcamCapture.WebcamCapture_gui$thread1.run(WebcamCapture_gui.java:102)
    at java.base/java.lang.Thread.run(Thread.java:832)
Jon Love
  • 3
  • 2
  • 1
    Please edit your question and add the stack trace from the exception. – tgdavies Nov 13 '20 at 23:52
  • Also, you are starting and join()'ing a thread in your constructor, which is pretty much the same as just running the code directly. – Trevor Harrison Nov 14 '20 at 00:02
  • Hey, thanks for commenting. I updated and added the stack trace from the console. I usually run multiple threads from user interaction, so this isn't something I've done before (start running without user interaction). I'd be grateful for any pointers. Thanks! – Jon Love Nov 14 '20 at 01:58
  • Add this and compare the output with your expectations: `System.out.println(classPathInput.getAbsolutePath());` – Bohemian Nov 14 '20 at 03:02
  • Your problem doesn't seem to be what the title of your question states. You're able to run some code alright, but it's not able to find a file. – Abhijit Sarkar Nov 14 '20 at 03:12
  • Yeah, I'm pulling the Image to put on the canvas from either a camera or a picture embedded in the jar. The problem is that it's trying to display the image before it's ready. If I remove the Thread start at the end of Initialize() and instead call CaptureImage() on the button click event, then it works as desired. – Jon Love Nov 14 '20 at 03:13
  • Show your project structure, i.e. where the class getting NPE is, and where the resource is. Also, if the class is getting loaded by a different classloader, that might be a problem as well. – Abhijit Sarkar Nov 14 '20 at 03:17
  • Here is your problem: `File classPathInput = new File(WebcamCapture_gui.class.getResource("/resources/USB_not_found.png").getFile());` When your program is running from a JAR file all resources are streams within the jar file so you can't get it like that. You have to get the stream and convert it to a file. See here how to do it: https://mkyong.com/java/java-read-a-file-from-resources-folder/ read the second topic – Jorge Campos Nov 14 '20 at 03:41
  • I know that's what the problem seems like based on the stack trace. Trust me, I spent a lot of time looking that up, too. I don't have a problem displaying the picture using the JButton (removing the thread start). The problem is I'm unsure how to get it to work on application load instead of using the JButton click event. – Jon Love Nov 14 '20 at 03:53

1 Answers1

0

Notice that the error is not just caused by the image.

Exception in thread "Thread-0" java.lang.NullPointerException: Cannot invoke "java.awt.Graphics.drawImage(java.awt.Image, int, int, int, int, java.awt.image.ImageObserver)" because "g" is null
    at webcamCapture.WebcamCapture_gui.CaptureImage(WebcamCapture_gui.java:120)
    at webcamCapture.WebcamCapture_gui$thread1.run(WebcamCapture_gui.java:102)
    at java.base/java.lang.Thread.run(Thread.java:832)

When you start the thread immediately, the canvas graphics might not have been initialised and calling the drawImage method raises the NPE.

To circumvent the NPE, you could replace

t1.start();

with

SwingUtilities.invokeLater(t1); 

Another "dirty" workaround would be to leave t1.start(); but force the app to load it's components by adding frmWebcamCapture_gui.setVisible(true); in the run method of thread1

  public class thread1 implements Runnable {
    @Override
    public void run() {
     frmWebcamCapture_gui.setVisible(true);
      CaptureImage();
    }
  }

This is mainly for demonstration and explonatory purposes, so I will suggest you adopt the first method of using SwingUtilities. You can read more about SwingUtilities.invokeLater on this post and the documentation