Pause cycling of images with spacebar and creating ArrayList with PImage

Hello,

I have created a sketch that cycles through images, but when I hit the spacebar, I want it to stop cycling through the images and remain on that particular image until I hit the return key. I don't know how to implement that. Here is my code:

String basedir = "/Users/admin/Desktop/Borderscape_OF/clean"; 
String fileext = ".jpg"; 

PImage img;
int i = 0;
String[] filenames;
int imgSize = 182;

java.io.File folder = new java.io.File(dataPath(basedir));
java.io.FilenameFilter extfilter = new java.io.FilenameFilter() {
  boolean accept(File dir, String name) {
    return name.toLowerCase().endsWith(fileext);
  }
};

void setup() {
  fullScreen();
  frameRate(7);
  background(0);

  //pull from string
  filenames = folder.list(extfilter);
  img = loadImage(basedir+"/"+filenames[0]);
  surface.setSize(img.width, img.height); // Takes the size of the first image in the folder.
}

void draw() {
   keyPressed();

  img = loadImage(basedir+"/"+filenames[i]);
  float x = random(0, 255);
  tint(255, x);
  image(img, 0, 0);
  i++;

  if (i + 1 == filenames.length) {
    i = 0;
  }
}

void keyPressed() {
  if (key == 'x') {
    basedir = "/Users/admin/Desktop/Borderscape_OF/glitched";
  }
  if (key == 's') {
    basedir = "/Users/admin/Desktop/Borderscape_OF/clean";
  }

}

Answers

  • edited March 2018 Answer ✓

    use a boolean variable that you define before setup()

    name it isCycling or so

    boolean isCycling =true;
    

    now in keyPressed on space ' ' set isCycling = false;

    on RETURN isCycling = true;

    If you're making cross-platform projects, note that the ENTER key is commonly used on PCs and Unix and the RETURN key is used instead on Macintosh. Check for both ENTER and RETURN to make sure your program will work for all platforms.

    This boils down to

    if(key==RETURN || key==ENTER) 
            isCycling = true;
    

    in draw

    if(isCycling)   
       i++;
    

    if(isCycling) is short for if(isCycling==true)

    Chrisir ;-)

  • It works!! Thank you for your quick response :)

  • edited March 2018

    technically, you should never load an image in draw() since it runs 60 times per second, so you're loading the same image again and again which slows your computer down a lot.

    better make an array of images and fill it once and for all in setup() and just use the array in draw(). (a bit similar to you array filenames)

    before setup():

    PImage[] images;
    

    at the end of setup()

    images = new PImage[filenames.length];
    
    for(int i = 0; i<filenames.length;i++) {
         images [i] = loadImage (basedir+"/"+filenames[i]);  // load all in one go here
    }
    

    in draw()

       // img = loadImage(basedir+"/"+filenames[i]); // NO GO !!!
    
       ...
    
       image(images [i] , 0, 0);
    
  • When I tried implementing those changes, it comes up blank with no error message. Here is my code:

    String basedir = "/Users/admin/Desktop/Borderscape_OF/clean"; 
    String fileext = ".jpg"; 
    
    int i = 0;
    String[] filenames;
    int imgSize = 182;
    
    boolean isCycling = true;
    
    java.io.File folder = new java.io.File(dataPath(basedir));
    java.io.FilenameFilter extfilter = new java.io.FilenameFilter() {
      boolean accept(File dir, String name) {
        //return name.toLowerCase().endsWith(fileext);
        return name.endsWith(fileext);
      }
    };
    
    PImage[] images;
    
    void setup() {
      fullScreen();
      frameRate(7);
      background(0);
    
      filenames = folder.list(extfilter);
      images = new PImage[filenames.length];
      for (int i = 0; i<filenames.length; i++) {
        images [i] = loadImage(basedir+"/"+filenames[i]);  // load all in one go here
      }
    }
    
    void draw() {
      keyPressed();
    
      //img = loadImage(basedir+"/"+filenames[i]);
    
      float x = random(0, 255);
      tint(255, x);
          image(images [i], 0, 0);
    
      if (isCycling) {
        i++;
      }
    
      if (i + 1 == filenames.length) {
        i = 0;
      }
    }
    
    void keyPressed() {
      if (key == 'x') {
        basedir = "/Users/admin/Desktop/Borderscape_OF/glitched";
      }
      if (key == 's') {
        basedir = "/Users/admin/Desktop/Borderscape_OF/clean";
      }
      if (key == ' ') {
        isCycling = false;
      }
      if (key == RETURN || key == ENTER) {
        isCycling = true;
      }
    }
    
  • I let it sit and I got this:

    An OutOfMemoryError means that your code is either using up too much memory because of a bug (e.g. creating an array that's too large, or unintentionally loading thousands of images), or that your sketch may need more memory to run. If your sketch uses a lot of memory (for instance if it loads a lot of data files) you can increase the memory available to your sketch using the Preferences window.

  • Ok, increased the memory to 5000 and it works now. Yay! Is there a more efficient way to load a high quantity of images that doesn't take up so much memory as in an array? I have about 200 images.

  • Sorry, I just see your posts now; well done!

    You solved it.

    Not sure about memory though. You could resize them to make them smaller but i don’t know if this helps

    You could load them In the background or just load chunks of 50 or so during run time (from draw)

    Not my expertise

  • I have read elsewhere in the forum that a good way to do this is to use an ArrayList (https://forum.processing.org/two/discussion/767/dynamic-image-array), but I am not sure how to create an ArrayList of images that calls up images from a folder. I have checked the processing referencing page (https://processing.org/reference/ArrayList.html), though I don't know how to change from calling a single object like a particle to calling images from a folder.

    I have changed the following before setup:

    String basedir = "/Users/admin/Desktop/Borderscape_OF/test"; 
    String fileext = ".jpg";
    //PImage[] images;
    ArrayList<PImage> imgList = new ArrayList<PImage>();
    PImage img;
    int i;
    String[] filenames;
    boolean isCycling = true;
    boolean glitch = false;
    boolean saveimage = false;
    
    java.io.File folder = new java.io.File(dataPath(basedir));
    java.io.FilenameFilter extfilter = new java.io.FilenameFilter() {
      boolean accept(File dir, String name) {
        return name.toLowerCase().endsWith(fileext);
      }
    };
    

    and have added the following to setup:

      filenames = folder.list(extfilter);
      imgList = new ArrayList<PImage>();
    
      for (int h = 0; h < 10; h++) {
        imgList.add(new PImage());
    

    but I don't know how to get it to call the images from the folder.

  • this is your draw

    void draw() {
      keyPressed();
    
      //img = loadImage(basedir+"/"+filenames[i]);
    
      float x = random(0, 255);
      tint(255, x);
      image(imgList.get(i), 0, 0);
    
      if (isCycling) {
        i++;
      }
    
      if (i + 1 == imgList .size()) {  // LOAD 10 initial images here !!!!!
        i = 0;
      }
    }//function draw
    
  • edited March 2018 Answer ✓

    and this is your setup()

    both not tested.

    The idea

    Now, either you just load 200 images in one go and don't care about memory.

    Or you load on the fly while browsing the images.

    Then in draw you need a mechanism for that.

    That means here are the images and you have a position i in draw (the current image, I will denote with "|" here) :

    01234567891011121314151617
    xxxxxxxxxxxxxxxxxxxxxxxxxxx
                         |  
    

    We always want only 10 images in the list so the current image and 9 images further right.

    Now in your approach the user can go only right with | i in draw().

    That means when we are only 4 images away from the end, we should load 10 images more. So that is a new function similar to that in setup below but with using i:

      for (int h = i; h < i+10; h++) {
        PImage imageTemp = loadImage(basedir+"/"+filenames[h]);
        imgList.add(imageTemp);
      }//for   
    

    Now, also we need to get rid of images left from the position | i.

    That needs to be done (before adding) in a new function in a backward for loop

      for (int h = i-1; h >=0; h--) {
          imgList.remove(i);
      } 
    

    In theory this could be done in a new thread which runs parallel to the main draw().

    But your new setup() :

    void setup() {
      fullScreen();
      frameRate(7);
      background(0);
    
      filenames = folder.list(extfilter);
      imgList = new ArrayList<PImage>();
    
      for (int h = 0; h < 10; h++) {
        PImage imageTemp = loadImage(basedir+"/"+filenames[h]);
        imgList.add(imageTemp);
      }//for   
      //
    }//function setup() 
    
  • It's working! Thank you for taking the time to answer my queries.

  • Spoke too soon :) It's cycling through the ten images, but where do I put the backwards remove loop?

  • THE backward for loop could be directly before the for loop to adding images

    To check the size() of ArrayList: could be println(...list.size()); in draw()

  • I did print line and it cycles through ten, but it is still the same 10 images.

    I have my set up as follows:

    void setup() {
      fullScreen();
      frameRate(7);
      background(0);
    
      filenames = folder.list(extfilter);
      imgList = new ArrayList<PImage>();
    
     for (int h = i-1; h >=0; h--) {
        imgList.remove(i);
    } 
    
      for (int h = i; h < i+10; h++) {
        PImage imageTemp = loadImage(basedir+"/"+filenames[h]);
        imgList.add(imageTemp);
      }
    }
    

    Maybe I haven't declared i correctly? I just have it before setup as

    int i;

  • ??

    Lines 9 to 16 belong in draw () !!!

    The idea is that we load images throughout but not always but only when i + 2 > upper bound

  • edited August 2018

    This is a demonstration managing multiple images, version 1.

    Kf

    //Multiple-image management using a ring container of defined size n and re-fill triggered by 
    //image size limit available.
    
    //REFERENCE: https://forum.processing.org/two/discussion/comment/119781/#Comment_119781
    
    //By KF @ 31-Mar-2018
    
    //Suggested improvements: 
    //   Optimization between size, re-fill trigger values which is machine-dependent.
    //   Rewrite re-fill operation so to use a separate thread
    
    String basedir = "C:\\Users\\C\\Downloads\\data"; 
    String fileext = ".jpg";
    int filesInMEM=10;
    
    ImageRingContainer imageManager;
    
    PImage img=null;
    boolean switchImages=true;  //Enables/disables image increment operation
    int lastTime=0;             //For time profiling 
    
    void setup() {
      size(600, 400);
      lastTime=millis();
      imageManager=new ImageRingContainer(filesInMEM, basedir, fileext);
      img=imageManager.get();
    }
    
    void draw() {
      background(0);
      if (img!=null)
        image(img, 0, 0);
    
      if (switchImages && frameCount%30==0)
        img=imageManager.get();
    }
    
    void keyPressed() {
    
    
      switch(key) {
      case ' ':
        println("Automatic Image Dispaly: "+( (switchImages=!switchImages)==true ?"Enabled":"Disabled"));
        break;
      case 'f':
        imageManager.printFilenames();
        break;
      default:
        break;
      };
    }
    
    
    /**
     * This class loads a defined number of images on the fly.
     * You provide a folder with a series of iamges to load.
     * The program stores the available file names to load.
     * The core contains a ring structure where n number of images
     * are loaded. Images access are based on a head/tail indices.
     * The head keep track where the next image will be extracted.
     * The tail keeps track where the next set of images will be 
     * loaded.
     * When the available number of images is less than the minimum
     * specified size, the code triggers a refill operation where a 
     * new set of images are loaded on the tail of the ring.
     * If the refill operation exhausts all the available images to 
     * show, the code will start reusing the images again from the 
     * initial list.
    */
    
    class ImageRingContainer {
    
      int n;                    //size of ring buffer
      int min_available_limit;  //Min number of available images. Otherwise trigger image loading
      String path;              //Location of images
      String extFilter;
      String[] filenames;       //Current list of image filenames available
      ArrayList<PImage> imgCon; //Current iamges loaded
    
      //RING type FIFO
      //Images are inserted from the back (at tail) and extracted on the front (at the head)
      int headIdx;  //Position where an img is poped 
      int tailIdx;  //Position where an image is push
    
      int cIdx;  //Current index on filename's array
      int verbosity;
    
    
      ImageRingContainer(int size, String basePath, String ext) {
    
        n=size;
        min_available_limit=n/3;
    
        cIdx=0;
        headIdx=0;
        tailIdx=0;
    
        path=basePath;
        extFilter=ext;    
        verbosity=1;
    
        if (!path.endsWith("\\"))
          path+="\\";
    
        imgCon = new ArrayList<PImage>(n);
        for (int i = 0; i < n; i++) 
          imgCon.add(null);
    
        loadFileNames();
        upLoadImages(n);
      }
    
      int getActiveSize() {
        if (tailIdx>headIdx)
          return tailIdx-headIdx;
        else
          return n-headIdx+tailIdx;
      }
    
      void loadFileNames() {
        java.io.File folder = new java.io.File(dataPath(basedir));
        filenames = folder.list(extfilter);
      }  
    
      synchronized void upLoadImages(int loadN) {
    
        //Load to a maximum of ring size
        if (loadN>n) {            
          println("WARNING: Attempt to load more images (" + loadN + ") than allowable size: " + n);
          loadN=n;
        }
    
        //If we consummed all the available filenames, then start from the beginning
        if (cIdx==filenames.length) {
          cIdx=0;
        }
    
        //Check if we have enough images to load
        if (cIdx+loadN>filenames.length) {
          loadN=filenames.length-cIdx;
        }
    
        //
        for (int i = 0; i < loadN; i++) {
          tailIdx=cIdx%n;
          PImage ci = loadImage(path+filenames[cIdx++]);  
          ci.resize(0, height);
          imgCon.set(tailIdx, ci);
        }
      }
    
      synchronized PImage get() {    
    
        if (headIdx==tailIdx) {
          println("WARNING: Recycling image container list: Head caught up or passed tail");
        }
    
        if (getActiveSize()<min_available_limit)
          upLoadImages(min_available_limit);
    
        if (verbosity>0) {
          println("cidx: "+cIdx+" head@"+headIdx+" tail@"+tailIdx+" delta Time: "+(millis()-lastTime));
        }
        lastTime=millis();
    
        PImage ci = imgCon.get(headIdx);
        headIdx = (headIdx+1)%n;
    
        return ci;
      }
    
    
    
      java.io.FilenameFilter extfilter = new java.io.FilenameFilter() {
        boolean accept(File dir, String name) {
          return name.toLowerCase().endsWith(extFilter);
        }
      };
    
      void printFilenames() {
        for (int i = 0; i < filenames.length; i++) {
          println(nf(i, 2, 0)+" => "+path+filenames[i]);
        }
      }
    }
    
    
    //cidx: 10 head@0 tail@9 delta Time: 467
    //cidx: 10 head@1 tail@9 delta Time: 485
    //cidx: 10 head@2 tail@9 delta Time: 501
    //cidx: 10 head@3 tail@9 delta Time: 498
    //cidx: 10 head@4 tail@9 delta Time: 501
    //cidx: 15 head@5 tail@4 delta Time: 713
    //cidx: 15 head@6 tail@4 delta Time: 484
    //cidx: 15 head@7 tail@4 delta Time: 500
    //cidx: 15 head@8 tail@4 delta Time: 500
    //cidx: 15 head@9 tail@4 delta Time: 499
    //cidx: 20 head@0 tail@9 delta Time: 753
    //cidx: 20 head@1 tail@9 delta Time: 486
    //cidx: 20 head@2 tail@9 delta Time: 499
    //cidx: 20 head@3 tail@9 delta Time: 501
    //cidx: 20 head@4 tail@9 delta Time: 498
    //cidx: 25 head@5 tail@4 delta Time: 975
    //cidx: 25 head@6 tail@4 delta Time: 485
    //cidx: 25 head@7 tail@4 delta Time: 501
    //cidx: 25 head@8 tail@4 delta Time: 500
    //cidx: 25 head@9 tail@4 delta Time: 501
    //cidx: 30 head@0 tail@9 delta Time: 736
    //cidx: 30 head@1 tail@9 delta Time: 486
    //cidx: 30 head@2 tail@9 delta Time: 501
    //cidx: 30 head@3 tail@9 delta Time: 498
    //cidx: 30 head@4 tail@9 delta Time: 501
    //cidx: 35 head@5 tail@4 delta Time: 1036
    //cidx: 35 head@6 tail@4 delta Time: 485
    //cidx: 35 head@7 tail@4 delta Time: 500
    //cidx: 35 head@8 tail@4 delta Time: 500
    //cidx: 35 head@9 tail@4 delta Time: 499
    //cidx: 40 head@0 tail@9 delta Time: 803
    //cidx: 40 head@1 tail@9 delta Time: 485
    //cidx: 40 head@2 tail@9 delta Time: 498
    //cidx: 40 head@3 tail@9 delta Time: 501
    //cidx: 40 head@4 tail@9 delta Time: 498
    //cidx: 45 head@5 tail@4 delta Time: 802
    //cidx: 45 head@6 tail@4 delta Time: 485
    //cidx: 45 head@7 tail@4 delta Time: 500
    //cidx: 45 head@8 tail@4 delta Time: 499
    //cidx: 45 head@9 tail@4 delta Time: 499
    //cidx: 50 head@0 tail@9 delta Time: 947
    //cidx: 50 head@1 tail@9 delta Time: 485
    //cidx: 50 head@2 tail@9 delta Time: 500
    //cidx: 50 head@3 tail@9 delta Time: 500
    //cidx: 50 head@4 tail@9 delta Time: 501
    //cidx: 55 head@5 tail@4 delta Time: 623
    //cidx: 55 head@6 tail@4 delta Time: 488
    //cidx: 55 head@7 tail@4 delta Time: 500
    //cidx: 55 head@8 tail@4 delta Time: 499
    //cidx: 55 head@9 tail@4 delta Time: 502
    //cidx: 60 head@0 tail@9 delta Time: 650
    //cidx: 60 head@1 tail@9 delta Time: 485
    //cidx: 60 head@2 tail@9 delta Time: 500
    //cidx: 60 head@3 tail@9 delta Time: 502
    //cidx: 60 head@4 tail@9 delta Time: 498
    //cidx: 61 head@5 tail@0 delta Time: 524
    //cidx: 5 head@6 tail@4 delta Time: 668
    //cidx: 5 head@7 tail@4 delta Time: 485
    //cidx: 5 head@8 tail@4 delta Time: 501
    //cidx: 5 head@9 tail@4 delta Time: 499
    //cidx: 10 head@0 tail@9 delta Time: 731
    //cidx: 10 head@1 tail@9 delta Time: 486
    //cidx: 10 head@2 tail@9 delta Time: 499
    //cidx: 10 head@3 tail@9 delta Time: 500
    //cidx: 10 head@4 tail@9 delta Time: 500
    //cidx: 15 head@5 tail@4 delta Time: 676
    //cidx: 15 head@6 tail@4 delta Time: 485
    //cidx: 15 head@7 tail@4 delta Time: 500
    

    http://www.java-gaming.org/topics/game-loops/24220/view.html

    https://forum.processing.org/two/discussion/comment/121981/#Comment_121981

    www.programering.com/a/MzN1czNwATQ.html

Sign In or Register to comment.