pixels() conceptual questions :|

edited September 2016 in Programming Questions

I had some quick conceptual questions regarding pixels() which I'm having trouble grasping.

So basically I want to get the r, g and b values of my current mouse position. Now, I want this sketch to work irrespective of the window size (which happens to be 600x900 at the moment) - so I'm resizing using the resize function. Do i need to use loc = x + y*width differently now? And a little confused how to translate that to my current mouse position as well. Haven't really used PImage till now in Processing even though I've been using P5 for generative art for a while now. Some nice links would also be welcome, while the snippets in the reference give some ideas it's not really conceptually enlightening for bigger projects.

Tagged:

Answers

  • If you have a PImage object of width W and height H then it's colour data is stored in a one dimensional integer array of size W*H and the index of the array element corresponding to the position x, y is calculated from idx = x + y * W

    If you resize the image to width W1 and height H1 then the array length will be changed to W1*H1 and the formula becomes idx = x + y *W1

    Since PImage is an offscreen buffer then your talking about the mouse position does not make a lot of sense. You need to clarify what you mean.

  • Think that helps me a little, but not completely. Here's what I'm hoping to achieve, in plain text.

    1. Load an image into PImage.
    2. Check if the width and height and if they're bigger than my sketch dimensions then resize them to fit, while maintaining the original image ratio. (this is not a problem - but placing the image dead centre once resized is an issue, I think imageMode(CENTER) will help me here.
    3. Load the image as the background.
    4. If I mouse over the sketch with the image visible, get the value of r, g and b at the current mouse position (and not of the background/other sketch elements). I might be later wanting to get averaged out values around an area of the mouse pointer but that comes in later.

    The convolution filter example helps me out a little, but as I said, conceptually I can't grasp how to achieve this in a processing sketch.

  • edited September 2016

    @GoToLoop, no that doesn't entirely work as I want to keep the canvas (sketch?) size constant.

    @quark + @GoToLoop let me post my updated code once I write it. Pretty sure it's not going to work so then you can help me out :)

    Much appreciated!

  • edited September 2016

    So you have a sketch of size width and height. You also have a PImage that you have resized to fit the display while maintaining the aspect ratio. Assume that the of size this image w and h.

    then because it fits the display either one or both of these statements must be true-

    width == w
    height == h
    

    Since the image is centred in the display we can calculate the top-left corner (tlX and tlY) of the image with these statements

    tlX = (width - w) / 2;  
    tlY = (height - h) / 2; 
    

    We can calculate the mouse position relative to the top-left corner of the image

    posX = mouseX - tlX;
    posY = mouseY - tlY;
    

    Since the image may not fill the whole screen we need to test posX and posY

    if(posX >= 0 && posX < w && posY >= 0 && posY < h){
      // inside image so calculate index to pixel array
      int idx = posX + posY * w;
    }
    
  • @quark seems right at first glance! I'll be testing over tonight and tomorrow, please give me some time to get back to you! Thanks!

  • edited September 2016

    @quark, okay so here I am at the moment. 2 problems, basically,

    1. Can't seem to proportionally scale the image if the width and height are different (marked in comments). Math seems to beat me here, cannot come up with a function to change h or w accordingly.

    2. And once I run it, the value of idx doesn't seem to change, or change slowly/not in sync with my mouse movement. What am I doing wrong here?

      PImage image;
      
      int w = 600;
      int h = 600;
      
      int t1X = (width - w)/2;
      int t1Y = (height - h)/2;
      
      
      void setup() {
        size(600, 900);
        background(0);
        image = loadImage("test-04.png");
        frameRate(12);
      }
      
      void draw() {
        background(0);
      
        //Checking image dimensions
        if (image.width>image.height) {
          //Need to reset h here somehow relative to w?
        }
        if (image.height>image.width) {
          image.resize(0, h);
          //Need to reset w here somehow relative to h?
        } else {
          image.resize(w, h);
        }
      
        //Loading image as background
        image(image, (width-w)/2, (height-h)/2);
      
        //Mouse
        int posX = mouseX - t1X;
        int posY = mouseY - t1Y;
      
        //Calculating the index pixel
        if (posX >= 0 && posX < w && posY >= 0 && posY < h) {
          // inside image so calculate index to pixel array
          int idx = posX + posY * w;
          //Printing value of idx to check
          println(idx);
        }
      }
      
  • These lines

    int t1X = (width - w)/2;
    int t1Y = (height - h)/2;
    

    Should be done a bit different. The values of width and height are defined by size within your setup function call. Consider defining those values after the call to setup:

    int t1X;
    int t1Y; 
    
    void setup() {
      size(600, 900);
      t1X = (width - w)/2;
      t1Y = (height - h)/2;
      background(0);
      image = loadImage("test-04.png");
      frameRate(12);
    }
    

    This does not answer your question, but more of keeping sane code.

    Related to your question, you should try screenX and screenY:

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

    I hope this helps,

    Kf

  • Answer ✓

    First of all you should not be loading images inside the draw() method because this is executed 60 times a second.

    The solution to getting the new image size is to
    1) calculate the scale factor needed to just fill the display horizontally
    2) calculate the scale factor needed to just fill the display vertically
    3) use the smaller of the 2 scale factors calculated.

    It doesn't matter whether the image is smaller or larger that the display as this algorithm works with both.

    The sketch below demonstrates how to do this. It also gets the pixel colour under the mouse and uses it for the background colour. Change line 1 with your filename.

    String imgFname = "cartoon-fish-6.jpg";
    PImage image;
    // These realte to the size and position of the resized image.
    int tlX, tlY;
    int w, h;
    // Is the mouse over the image on the screen.
    boolean isOverImage = false;
    int pixelColour;
    
    void setup() {
      size(800, 480);
      cursor(CROSS);
      pixelColour = color(0);
    
      resizeImageToFitDisplay(imgFname);
    }
    
    // Get and resize the image so that it just fits the display without
    // affecting its aspect ratio.
    void resizeImageToFitDisplay(String fname) {
      image = loadImage(fname);
      println("Image original size = ", image.width, image.height);
      float imgW = image.width, imgH = image.height;
      float scale = min(width/imgW, height/imgH);
      image.resize(round(scale * imgW), round(scale * imgH));
      // Get resized image data : width, height and 
      // top-left corner position
      w = image.width;
      h = image.height;
      tlX = (width - w)/2;
      tlY = (height - h)/2;
      println("Image new size = ", w, h);
    }
    
    void draw() {
      background(pixelColour);
      // Displaying image as large as possible to fit display
      image(image, tlX, tlY);
      // If over image show position with a circle
      if (isOverImage) {
        strokeWeight(3);
        stroke(0);
        noFill();
        ellipse(mouseX, mouseY, 11, 11);
        noCursor();
      } else { 
        cursor(CROSS);
      }
    }
    
    void mouseMoved() {
      // Get mouse position relative to top-left corner of image
      int posX = mouseX - tlX;
      int posY = mouseY - tlY;
      //Calculate the index pixel and change the background colour to match.
      if (posX >= 0 && posX < w && posY >= 0 && posY < h) {
        // inside image so calculate index to pixel array
        int idx = posX + posY * w;
        //Printing value of idx to check
        image.loadPixels();
        pixelColour = image.pixels[idx];
        isOverImage = true;
      } else {
        isOverImage = false;
      }
    }
    
  • @quark Thank you so so much!

Sign In or Register to comment.