updatePixels() on P3D texture

Hi

Is it possible to updatePixels() of a texture in P3D? Here is what I have but I’m only getting a black image or no image - works just fine if I load an image from file. I have tried moving setTexture to draw() to force reloading the image but still no difference.

Declared globals.

PImage theimage;
PShape cube;

In my setup()

theimage = createImage(1080, 720, RGB); //have tried RGB (image is black) and ARGB (no image)
cube = createShape(BOX, 112);
cube.setFill(color(255, 255, 255));
cube.setStroke(false);
cube.setTexture(theimage);

In my draw();

// All my image processing on theimage happens here - it works just fine

updatePixels();
camimage.updatePixels();

translate(width / 2, height / 2);
shape(cube, 25, 25);

Processing 3.5.4.

It is possible. I have copied the provided parts of your code into a project and ran it, encountering no issues:

PImage theimage;
PShape cube;
float scale = 0.005;
PVector speed = new PVector(0.1, 0.2);

void setup() {
  size(500, 500, P3D);
  
  theimage = createImage(width, height, RGB);
  cube = createShape(BOX, 250);
  cube.setFill(color(255));
  cube.setStroke(false);
  cube.setTexture(theimage);
}

void draw() {
  background(200);
  loadPixels();
  theimage.loadPixels();
  color[] p = theimage.pixels; // not necessary, makes it easier to code
  for (int i = 0; i < p.length; i++) {
    int x = i % theimage.width;
    int y = i / theimage.width;
    p[i] = pixels[i] * color(noise((x + millis() * speed.x) * scale, (y + millis() * speed.y) * scale) * 255);
  }
  theimage.updatePixels();
  translate(width / 2, height / 2);
  rotateX(millis() * 0.0002);
  rotateY(millis() * 0.0004);
  rotateZ(millis() * 0.0008);
  shape(cube, 25, 25);
}

If this sketch is similar in principle to what you are doing, then the issue likely lies in code not provided in your post. Some ideas I have for where things could be going wrong are as such:

  1. There is a mismatch of variable names between “theimage” and “camimage”.
  2. The pixels array for the main canvas will only contain the information for things drawn before loadPixels().
  3. I’ve also encountered bugs with P3D when using extra P2D or P3D PGraphics.

I have limited information, so these are only guesses based on related topics. It would be helpful to have more code.

Hi

Thanks for your reply. Apologies, yes the variable name was a typo in my post - that didn’t appear in the original. I did a similar test to yours today using noise and updating the PImage doesn’t seem to be the issue after all - it seems to be how I’m setting it, copying pixels from a Capture, that is causing the problem. I’ve adapted your code to include mine and where it’s failing (see comments in code below). I think I’ve got it down to a few possibilities:

  1. I’m doing something incorrect with loadPixels() for the Capture image (the image from read() isn’t ready maybe?);
  2. There is some issue with P3D - even using image() to display theimage fails but is working just fine without P3D;
  3. Images from a Capture are not the same format as PImage (not very probable I think).

Option 1 seems the most likely - if I texture the cube with cam directly, it works.

import processing.video.*;

PImage theimage;
PShape cube;
float scale = 0.005;
PVector speed = new PVector(0.1, 0.2);
Capture cam;

void setup() {
  size(500, 500, P3D);

  String[] cameras = Capture.list();
  cam = new Capture(this, 540, 360, cameras[1]);
  cam.start();

  theimage = createImage(540, 360, RGB);
  cube = createShape(BOX, 250);
  cube.setFill(color(255));
  cube.setStroke(false);
  cube.setTexture(theimage);
}

void draw() {
  background(200);


  if (cam.available() == true) cam.read();

  loadPixels();
  theimage.loadPixels();
  cam.loadPixels();

  color[] p = theimage.pixels; // not necessary, makes it easier to code
  color[] p2 = cam.pixels;
  
  for (int i = 0; i < p.length; i++)
  {
    // Same as yours - works fine.
    //p[i] = color(random(255), random(255), random(255));
    
    // As soon as I start to set the pixel from the camera it does not work.
    p[i] = p2[i];
    
    // Pixels from the camera are 0 and seem to be unset. If I do something 
    // similar without P3D and just use image() I don't have this issue.
    println("cam colour: " + p2[i]);
  }
  theimage.updatePixels();


  translate(width / 2, height / 2);
  rotateX(millis() * 0.0002);
  rotateY(millis() * 0.0004);
  rotateZ(millis() * 0.0008);
  shape(cube, 25, 25);
}

I found the problem. For some reason, the cam image doesn’t return any data until it has been drawn by the image() function once. You don’t even have to read() from the capture, just draw it with image() in setup() and it works. Here’s a modification of your modification to showcase it:

import processing.video.*;

PImage theimage;
PShape cube;
float scale = 0.005;
PVector speed = new PVector(0.1, 0.2);
Capture cam;

void setup() {
  size(960, 960, P3D);
  String[] cameras = Capture.list();
  printArray(cameras);
  cam = new Capture(this, 1280, 960, cameras[0]);
  cam.start();
  theimage = createImage(1280, 960, RGB);
  theimage.setLoaded(false);
  cube = createShape(BOX, 250);
  cube.setFill(color(255));
  cube.setStroke(false);
  cube.setTexture(theimage);
  // cam doesn't load until it is drawn by image() (either main or separate PGraphics)
  image(cam, 0, 0); 
}

void draw() {
  background(200);
  if (cam.available() == true) cam.read();

  boolean easy = key == ' ';
  if(easy) {
    int dx = cam.width - theimage.width;
    int dy = cam.height - theimage.height;
    theimage.copy(cam, dx, dy, cam.width - dx, cam.height - dy, 0, 0, theimage.width, theimage.height);
  } else { // possibly faster?
    theimage.loadPixels();
    cam.loadPixels();
    if(cam.pixels.length == theimage.pixels.length)
    System.arraycopy(cam.pixels, 0, theimage.pixels, 0, theimage.pixels.length);
    theimage.updatePixels();
  }
  // from this test, there does not seem to be a discernable performance difference between using copy() and arrayCopy()
  // copy is also better for this task, since it can deal with differing image sizes
  // but both are better than using a for loop

  // modification to theimage can occur here safely

  translate(width / 2, height / 2);
  rotateX(millis() * 0.0002);
  rotateY(millis() * 0.0004);
  rotateZ(millis() * 0.0008);
  shape(cube, 25, 25);
}

This sort of makes sense - capture pulls video frames image by image from the camera, so perhaps there is a buffer for this that only holds one image (linked to image(), which clears that buffer). The first image that comes from the camera is always black as the camera is kicking in so perhaps it was working all the time but just repeatedly showing that first image! Why it only needs to be done once in setup() is bizarre.

There are lots of ‘perhaps’ here but if I do println(cam.width); I do get a returned width and not a NullPointerException when there is no image.

Anyone here know the mechanics of image arrays, Capture and Gstreamer?
Thanks for this! I would have spent another few days tracking this down.