Try-Catch with LoadImage()

edited March 2018 in Questions about Code

I'm trying to safely load an image from the web into a PImage in Processing 3. I tried to adapt the try-catch example from the website but IOExceptions was not the right kind of exception, I tried with a general Exception but that doesn't seem to work either. How would I find the right kind of exception to try to catch? Is the following snippet the right thing (other than the exception kind or are there better ways to check for errors like this one?

PImage webImg;
String urls[];

void setup() {
  size(200,200);
  
  urls = new String[2];
  // working URL
  urls[0] = "https://"+"processing.org/img/processing-web.png";
  // bad URL
  urls[1] = "https://"+"**********.org/img/processing-web.png";
}

void draw() {
  
  background(0);
    try {
      webImg = loadImage(urls[frameCount%2], "png");
    } catch (Exception e) {
    //e.printStackTrace();
    webImg = null;
    println("Error!");
  }
  
  if (webImg != null) {
    println("It Worked!");
   image(webImg,0,0); 
  }

}

Answers

  • What exception is happening that you're trying to guard against?

  • edited March 2018

    This is what I get when I try to fetch an image from *****.org: (the code i posted should be copy pastable except for the urls being formatted by the forum)

    java.net.UnknownHostException: **********.org
        at java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:184)
        at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:392)
        at java.net.Socket.connect(Socket.java:589)
        at sun.security.ssl.SSLSocketImpl.connect(SSLSocketImpl.java:673)
        at sun.security.ssl.BaseSSLSocketImpl.connect(BaseSSLSocketImpl.java:173)
        at sun.net.NetworkClient.doConnect(NetworkClient.java:180)
        at sun.net.www.http.HttpClient.openServer(HttpClient.java:463)
        at sun.net.www.http.HttpClient.openServer(HttpClient.java:558)
        at sun.net.www.protocol.https.HttpsClient.(HttpsClient.java:264)
        at sun.net.www.protocol.https.HttpsClient.New(HttpsClient.java:367)
        at sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.getNewHttpClient(AbstractDelegateHttpsURLConnection.java:191)
        at sun.net.www.protocol.http.HttpURLConnection.plainConnect0(HttpURLConnection.java:1138)
        at sun.net.www.protocol.http.HttpURLConnection.plainConnect(HttpURLConnection.java:1032)
        at sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.connect(AbstractDelegateHttpsURLConnection.java:177)
        at sun.net.www.protocol.http.HttpURLConnection.getInputStream0(HttpURLConnection.java:1546)
        at sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1474)
        at java.net.HttpURLConnection.getResponseCode(HttpURLConnection.java:480)
        at sun.net.www.protocol.https.HttpsURLConnectionImpl.getResponseCode(HttpsURLConnectionImpl.java:338)
        at processing.core.PApplet.loadBytes(PApplet.java:7258)
        at processing.core.PApplet.loadImage(PApplet.java:5439)
        at sketch_180322b.draw(sketch_180322b.java:34)
        at processing.core.PApplet.handleDraw(PApplet.java:2437)
        at processing.awt.PSurfaceAWT$12.callDraw(PSurfaceAWT.java:1557)
        at processing.core.PSurfaceNone$AnimationThread.run(PSurfaceNone.java:316)
    
    
  • Don't load images in draw. Do it in setup.

    Calling loadImage, OVER THE INTERNET, 60 times a second is bad.

  • Okay, so it sounds like you need to catch UnknownHostException.

    However, your general Exception catch should have caught this, so something is off with your code. What did you expect to happen? What happened instead?

  • That was going to be my follow up, this loads a webcam image in real time over wifi-LAN.

    I know processing doesn't do multithreading, is there any way to do this in an asynchronous way?

    The image is always being updated in real time, i can't pre-load it

  • Good call @koogs. Definitely fix that problem before trying to figure out your try-catch logic.

  • I really can't pre-load the images, please read previous comment

  • edited March 2018

    You're loading 60 images per second. After 10 seconds you're going to have 600 requests out to the internet. Something is going to break. You need to fix this problem, because it's going to interfere with any other problems you're having.

    Doing this asynchronously is going to make the problem worse, not better.

    Your example can use a simple for loop to load the images in setup(). Surely you aren't loading an infinite number of images, are you?

  • This is a small recreation of the issue, my sketch does a lot more. I just want the catch execution block to run, and from my console output it doesn't seem like it is. furthermore i would not expect the error message to be displayed if the exception is caught. Is that just my assumption?

  • edited March 2018

    My original sketch is loading small compressed images from a server in the same LAN. It's only using one at the time so it's overwriting the same PImage. It's only doing this for 5 seconds every minute. It will be running 24/7 for a couple of weeks as part of an installation. While I'd like to hear more about the possible ways this could break right now my most pressing concern is how to catch this exception.

    Please could I get some help with the issue I asked about?

  • here pretend i posted this:

    
    PImage webImg;
    String urls[];
     
    void setup() {
      size(200,200);
       
      urls = new String[2];
      // working URL
      urls[0] = "https://"+"processing.org/img/processing-web.png";
      // bad URL
      urls[1] = "https://"+"**********.org/img/processing-web.png";
      
      background(0);
      try {
          webImg = loadImage(urls[0], "png");
        } catch (Exception e) {
        //e.printStackTrace();
        webImg = null;
        println("Error!");
      }
      
        if (webImg != null) {
        println("It Worked!");
       image(webImg,0,0); 
      }
      
      
      try {
          webImg = loadImage(urls[1], "png");
        } catch (Exception e) {
        //e.printStackTrace();
        webImg = null;
        println("Error!");
         background(0);
    
      }
       
      if (webImg != null) {
        println("It Worked!");
       image(webImg,0,0); 
      }
     
    }
     
  • edited March 2018 Answer ✓

    Thanks for the improved example.

    From the reference:

    If the file is not available or an error occurs, null will be returned and an error message will be printed to the console. The error message does not halt the program, however the null value may cause a NullPointerException if your code does not check whether the value returned is null.

    The stack trace is coming from inside Processing. Look at the loadImage() function here.

    In this case, loadImage() will complete but return null. So instead of using a catch block to detect this case, just check whether the image is null.

    if(webImg == null){
      println("Error!");
    }
    
  • edited March 2018

    From the comments in the code it seems that requestImage is the async version of loadImage right?

    Should I use that instead?

     public PImage requestImage(String filename) {
    //    return requestImage(filename, null, null);
        return requestImage(filename, null);
      }
    
    
      /**
       * ( begin auto-generated from requestImage.xml )
       *
       * This function load images on a separate thread so that your sketch does
       * not freeze while images load during setup(). While the image is
       * loading, its width and height will be 0. If an error occurs while
       * loading the image, its width and height will be set to -1. You'll know
       * when the image has loaded properly because its width and height will be
       * greater than 0. Asynchronous image loading (particularly when
       * downloading from a server) can dramatically improve performance.
    *
    extension parameter is used to determine the image type in * cases where the image filename does not end with a proper extension. * Specify the extension as the second parameter to requestImage(). * * ( end auto-generated ) * * @webref image:loading_displaying * @param filename name of the file to load, can be .gif, .jpg, .tga, or a handful of other image types depending on your platform * @param extension the type of image to load, for example "png", "gif", "jpg" * @see PImage * @see PApplet#loadImage(String, String) */ public PImage requestImage(String filename, String extension) { // Make sure saving to this file completes before trying to load it // Has to be called on main thread, because P2D and P3D need GL functions if (g != null) { g.awaitAsyncSaveCompletion(filename); } PImage vessel = createImage(0, 0, ARGB); AsyncImageLoader ail = new AsyncImageLoader(filename, extension, vessel); ail.start(); return vessel; } // /** // * @nowebref // */ // public PImage requestImage(String filename, String extension, Object params) { // PImage vessel = createImage(0, 0, ARGB, params); // AsyncImageLoader ail = // new AsyncImageLoader(filename, extension, vessel); // ail.start(); // return vessel; // } /** * By trial and error, four image loading threads seem to work best when * loading images from online. This is consistent with the number of open * connections that web browsers will maintain. The variable is made public * (however no accessor has been added since it's esoteric) if you really * want to have control over the value used. For instance, when loading local * files, it might be better to only have a single thread (or two) loading * images so that you're disk isn't simply jumping around. */ public int requestImageMax = 4; volatile int requestImageCount; private static final String ASYNC_IMAGE_LOADER_THREAD_PREFIX = "ASYNC_IMAGE_LOADER"; class AsyncImageLoader extends Thread { String filename; String extension; PImage vessel; public AsyncImageLoader(String filename, String extension, PImage vessel) { // Give these threads distinct name so we can check whether we are loading // on the main/background thread; for now they are all named the same super(ASYNC_IMAGE_LOADER_THREAD_PREFIX); this.filename = filename; this.extension = extension; this.vessel = vessel; } @ Override public void run() { while (requestImageCount == requestImageMax) { try { Thread.sleep(10); } catch (InterruptedException e) { } } requestImageCount++; PImage actual = loadImage(filename, extension); // An error message should have already printed if (actual == null) { vessel.width = -1; vessel.height = -1; } else { vessel.width = actual.width; vessel.height = actual.height; vessel.format = actual.format; vessel.pixels = actual.pixels; vessel.pixelWidth = actual.width; vessel.pixelHeight = actual.height; vessel.pixelDensity = 1; } requestImageCount--; } }
  • there's requestImage also

    https://processing.org/reference/requestImage_.html

    but looking at the source of loadImage there's already a try catch around the loading code

    https://github.com/processing/processing/blob/e83ae2054a3d3c52b1663385673fdfb26abcc96b/core/src/processing/core/PApplet.java#L5412

    so there is no exception.

    but you could clone that code...

  • so something like this would work?

    PImage webImg;
    String url;
    int c;
    
    void setup() {
      size(600,400);
      c = 322;
      url = new String();
      // working URL
      url = geturl(c);
      background(0);
      webImg = null;
      webImg = requestImage(url, "jpg");
    }
    
    void draw() {
      
       if (webImg != null) {
         println("Image downloaded");
         if(webImg.width > 0) {
           if( webImg.width > webImg.height) {
             webImg.resize(width,0);
           } else {
             webImg.resize(0,height);
           }
           
           image(webImg,0,0);
           webImg = null;
           url = geturl(++c);
           webImg = requestImage(url, "jpg");
         }
    
       }
    }
    
    String geturl(int c) {
      return "https://" + "www.coloradowebcam.net/webcam/aspensq03/aspensq0301" + Integer.toString(c) + ".jpg";
    }
    
  • so something like this would work?

    The best thing to do is just try it. What happened when you ran that code?

  • It worked but i mean is this the ideal solution for this case scenario? Does it allocate loads of ram? Should I explicitly invoke the garbage collector after a fixed number of frames? any suggestion since it needs to run for two weeks?

  • Like we said before, the biggest problem is loading images from inside the draw() function. At the very least, you should slow down your FPS so this isn't firing 60 times per second.

    Other than that, the best thing you can do is run it for a while and monitor the memory usage. Use a profiler and leave it running overnight or something.

Sign In or Register to comment.