Fading and Zoom (image slideshow)

Just to say how appreciative I am of the input here - totally blown away by your knowledge, clarity and consideration.

I’ve landed on the below - and using some images based on microorganisms and adding Blob detection the result is looking pretty splendid. I’ll soon be working towards sending OSC messages from selected Blob values - mentioning this because there might be implications for my next question.

///////////////////////////////////////////////////////// IMPORT LIBS

import blobDetection.*;

import java.util.Date;
import java.util.stream.Stream;
import java.util.stream.Collectors;
import java.util.Objects;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.nio.file.Path;

import milchreis.imageprocessing.*; //unused right now!@!@

import netP5.*;
import oscP5.*;
import processing.video.*;

///////////////////////////////////////////////////////// Blob Variables

PImage Blobimg;
BlobDetection theBlobDetection;
PImage img;

///////////////////////////////////////////////////////// Camera Variables (for BlobDetection)

Capture cam;
boolean newFrame=false;

///////////////////////////////////////////////////////// Graphics Variables

PGraphics canvas;

///////////////////////////////////////////////////////// Img Variables

ArrayList<PImage> images;
int   currentImage=0;
int   lastImageIndex =  0;
int   nextImageIndex =  1;
int   cImageIndex =  1;
float alphaValue     =  0;
//float alphaShift     =  0.0004; // very slow
//float alphaShift     =  0.004; //  slow
float alphaShift     =  0.008; //  MEDIUM
//float alphaShift     =  0.04; //  fast

int randomIndexFade;
float zoomFactor = 0.1;

///////////////////////////////////////////////////////// OSC / Network Variables
OscP5 oscP5;
NetAddress myRemoteLocation;

void setup() { /////////////////////////////////////////////////////////////// setup starts
  //fullScreen(P3D,0);

  //size(1920,1080, P3D); // HDTV 1080, FullHD, 1080p
  //size(1280, 720, P3D); //HD
  size(1024,576, P3D);
  
  canvas = createGraphics(width, height);
  
  cam = new Capture(this,160,120); // for Blob
  cam.start();
  
  // BlobDetection
  // img which will be sent to detection (a smaller copy of the cam frame);
  Blobimg = new PImage(80,60); 
  theBlobDetection = new BlobDetection(Blobimg.width, Blobimg.height);
  theBlobDetection.setPosDiscrimination(true);
  theBlobDetection.setThreshold(0.08); // will detect bright areas whose luminosity > 0.1;

  try (Stream<Path> stream = Files.list(Paths.get(dataPath("")))) {
    images = stream                                            // get a list of file objects as stream
      .filter(Files::isRegularFile)                            // filter for regular files
      .map(Path::toAbsolutePath)                               // Get the Filename
      .map(Path::toString)                                     // convert to a path string
      .filter(str -> str.matches(".*(?i)(\\.(jpg|png))$"))     // filter for wanted extensions
      .peek(str -> print("try loading image [" + str + "]: ")) // print info which image should be loaded
      .map(str -> loadImage(str))                              // make it a PImage
      .peek(pimg -> println((pimg==null || pimg.width==-1) ? "failed!" : "pass!")) // print loding info message  
      .filter(Objects::nonNull)                                // filter for successfully loaded images
      .filter(pimg -> pimg.width != -1)                        // filter for successfully loaded images
      .collect(Collectors.toCollection(ArrayList::new));       // convert to ArrayList of PImage
  }
  catch(Exception e) {
    println("Warning: " + e.getMessage());
    images = new ArrayList<>();
  }
    if (images.size() < 1)
    print("No valid images found!");
    else
    print("images found: " + images.size());
    
    myRemoteLocation = new NetAddress("192.168.2.10",12000); // to BlackIpad TouchOSC
    
    noStroke();
    frameRate(60);   

} //////////////////////////////////////////////////////////////////////////// setup ends

void captureEvent(Capture cam) //////////////////////////////////////////// captureEvent()
{
  cam.read();
  newFrame = true;
}

void draw() { ////////////////////////////////////////////////////////////////// draw loop
  background(0);
  
    if (images.size() > 0) {
    surface.setTitle("Image: " + currentImage);
    image(images.get(currentImage), 0, 0, width, height);
    if (frameCount % 120 == 0)
      currentImage = (currentImage + 1) % images.size();
  } else {
    text("No valid images found!", width/2, height/2);
    return;
  }

  int ListSize = images.size();
  float RandomIndex = random(ListSize);
  int RandomIndexInt = round (RandomIndex);

  fade(lastImageIndex, nextImageIndex, alphaValue, nextImageIndex % 2 != 0);
  if (alphaValue >= 1.0) {
    lastImageIndex = nextImageIndex;
    nextImageIndex = (lastImageIndex + RandomIndexInt) % images.size();
    cImageIndex = (lastImageIndex + RandomIndexInt) % images.size();
    alphaValue = 0;
  }
  alphaValue += alphaShift;
  
  if (newFrame)
  {
    newFrame=false;
    image(cam,0,0,0,0);
    Blobimg.copy(canvas, 0, 0, canvas.width, canvas.height, 0, 0, Blobimg.width, Blobimg.height);
    theBlobDetection.computeBlobs(Blobimg.pixels);
    drawBlobsAndEdges(true,true);
  }  
  
} ///////////////////////////////////////////////////////////////////////// draw loop end

void fade(int l, int n, float a, boolean zi) { //////////////////////////// fade Start

    canvas.beginDraw();
    canvas.background(0);
    canvas.pushMatrix();
    
    //canvas.image(outAB, 0,0); 

  if (l >= 0) {
    float invAlpha = 1.0-a;
    canvas.tint(#0000ff,invAlpha * 255);
    PImage limg=images.get(l);
    canvas.imageMode(CENTER);
    canvas.image(limg, width/2, height/2, width, height);
  }
    canvas.tint(#0000ff, a * 255);
    PImage nimg=images.get(n);
    canvas.imageMode(CENTER);
    canvas.image(nimg, width/2, height /2, width, height);

    canvas.popMatrix();
    canvas.endDraw();
    image(canvas, 0, 0);
  
} //////////////////////////////////////////////////////////////////////////////// fade End

void drawBlobsAndEdges(boolean drawBlobs, boolean drawEdges) { ////////////// BlobsAndEdges 

 strokeWeight(width /999); 

  Blob b;
  EdgeVertex eA,eB;
  for (int n=0 ; n<theBlobDetection.getBlobNb() ; n++)
  {
    b=theBlobDetection.getBlob(n);
    if (b!=null)
    {
      if (drawEdges) // Edges
      { 
        smooth();
        strokeWeight(3);
        stroke(255, 255, 255, 127);
        for (int m=0;m<b.getEdgeNb();m++)
        { eA = b.getEdgeVertexA(m); eB = b.getEdgeVertexB(m);
          if (eA !=null && eB !=null)
          line(eA.x * width, eA.y * height, eB.x * width, eB.y * height );
        
          //println(theBlobDetection.getBlobNb()+ " blobs"); use the first 8 blobs at any given time 
          //- send their x and y as SC
  }
      }
      
      if (drawBlobs) // Blobs
        noFill();
      {
        strokeWeight(1);
        stroke(0, 127, 255, 127);
        
        rect(b.xMin * width,b.yMin * height, b.w * width,b.h * height);}
        
        fill(#00ccff, 127);
        textSize(9);
        text(b.xMin, b.xMin * width,b.yMin * height);
        println(b.xMin + " x   " + b.yMin + " y ");

    }
      }
} /////////////////////////////////////////////////////////////////////// BlobsAndEdges end

The next nut to crack, (and my brain melts at even trying to formulate the question) relates to this code’s role in the larger project I’m working on. GoToLoop and Chrisir might recall GoTo’s awesome solution to an earlier question How should I organise this? 256 array with conditional content - Class? - #4 by GoToLoop Is it possible to put this Slideshow into an abstract class? Perhaps not because now “those cool stuffs don’t fit into a method”?