How to Iterate over 2D region with 1D pixels array

the snippet code below is doing color tracking and get the average position and draw circle on it, the screen size (640, 480). how to do iteration in one dimensional array in for loop within part of the screen, for example i want to do tracking/iteration from 0 to 100 of x axis and from 0 to 100 of y axis. thank you

PGraphics  posBuffer;

for(int i = 0; i < posBuffer.pixels.length; i++){
// encoded so blue is > 0 if a pixel is within threshold
    if(blue(posBuffer.pixels[i]) > 0){
        count++;
        // processing takes 0-1 (float) color values from shader to 0-255 (int) values for color
        // to decode, we need to divide the color by 255 to get the original value
        avg.add(red(posBuffer.pixels[i]) / 255.0, green(posBuffer.pixels[i]) / 255.0);
    }
}
1 Like

Hi @doni,

Interesting question! :wink:

You should change the title to: “Iterate over 2D region with 1D pixels array”

The Processing tutorial on images has the solution to your issue:

In the above example, because the colors are set randomly, we didn’t have to worry about where the pixels are onscreen as we access them, since we are simply setting all the pixels with no regard to their relative location. However, in many image processing applications, the XY location of the pixels themselves is crucial information. A simple example of this might be, set every even column of pixels to white and every odd to black. How could you do this with a one dimensional pixel array? How do you know what column or row any given pixel is in?

In programming with pixels, we need to be able to think of every pixel as living in a two dimensional world, but continue to access the data in one (since that is how it is made available to us). We can do this via the following formula:

  1. Assume a window or image with a given WIDTH and HEIGHT.
  2. We then know the pixel array has a total number of elements equaling WIDTH * HEIGHT.
  3. For any given X, Y point in the window, the location in our 1 dimensional pixel array is: LOCATION = X + Y*WIDTH

And this sketch is an example on how to get a 1D index out of 2D coordinates:

1 Like

the doc is to long to read but ill try. thank you

I quoted the specific part of the documentation :wink:
And look at the second link which is a real world code example.

I have made this kind of “utilitie” once: :slight_smile:


void setup() {
  size(400, 400);
  smooth();
}

void draw() {

  PVector p = toBiDimensional(toUniDimensional(mouseX, mouseY, 400), 400); 
  ellipse(p.x, p.y, 4, 4);
  
  loadPixels();
  pixels[toUniDimensional(mouseX, mouseY, 400)] = color(255, 0, 255);
  updatePixels();
  
  println(mouseX+ "  " + mouseY);
  println(toUniDimensional(mouseX, mouseY, 400));
  println(p);
}


PVector toBiDimensional(int index, int wid)
{
  return new PVector(index%wid, int(index/wid));
}

int toUniDimensional(int x, int y, int wid)
{
  return y*wid+x;
}

2 Likes

i could limit iteration as following

for (int x = 0; x < posBuffer.width && x < 100; x++ ) {
  for (int y = 240; y < posBuffer.height; y++ ) {
  
    }
}

but how to do that with this for loop

for(int i = 0; i < posBuffer.pixels.length; i++)

If you want to check the first 100 pixels of both the width and height, why not just check the first 10,000 (which is 100x100)?

if i do as following

it will track from index 0 to index 10000. the area from 0 to 10000 will be top part of the image/ screen.

but if i do as following

for (int x = 0; x < video.width && x < 100; x ++ ) {
    for (int y = 240; y < video.height; y ++ ) {
//----
    }
}

i could do tracking in left side part and bottom part of the image/ screen so exclude tracking from 101 to width (640) which is right part of the image/ screen and 0 to 239 of y axis of the image/screen which is top part of the screen and this is i am looking for

i could do tracking in left side part and bottom part of the image/ screen so exclude tracking from 101 to width (640) which is right part of the image/ screen and 0 to 239 of y axis of the image/screen which is top part of the screen and this is i am looking for

Are these different areas? You asked for the first 100x100 in the original post.

i am sorry. but i mentioned " for example". ok lets say my area tracking will be 0 to 100 of x axis and 0 to 100 of y axis, how to do that in 1D array? or should i convert 1D array to 2d array first

I don’t understand your interest in converting an image or a graphic to a one dimensional array. By definition 640x480 is a two dimensional array. The pixels in that image or graphic are arranged in rows and columns, ie two dimensions. An image of 640x480 has 307,200 pixels. Once we know what segment of that you want to study we should be able to offset into that grid to get the pixels that you want. I assume (which is dangerous) that the pixels are put in by row as opposed to being put in by columns. If that is true and we know the id number for each pixel you should be able to select a range of pixels to manipulate.

import processing.video.*;
PShader colorFinder, colorPosShader;
PGraphics overlay, posBuffer;
// Variable for capture device
Capture video;

// A variable for the color we are searching for.
color trackColor; 
float threshold = 0.1;

void setup() {
  //size(320, 240);
  size(640, 480, P2D);
  overlay = createGraphics(width, height, P2D);
  posBuffer = createGraphics(width, height, P2D);
  colorFinder = loadShader("colorDetect.glsl");
  colorPosShader = loadShader("colorPos.glsl");
  printArray(Capture.list());
  video = new Capture(this, width, height);
  video.start();
  video.loadPixels();
  // Start off tracking for red
  trackColor = color(255, 0, 0);
}

void captureEvent(Capture video) {
  // Read image from the camera
  video.read();
}

void draw() {
  colorFinder.set("threshold", threshold);
  colorFinder.set("targetColor", red(trackColor) / 255.0, green(trackColor) / 255.0, blue(trackColor) / 255.0, 1.0);
  colorPosShader.set("threshold", threshold);
  colorPosShader.set("targetColor", red(trackColor) / 255.0, green(trackColor) / 255.0, blue(trackColor) / 255.0, 1.0);
  overlay.beginDraw();
  overlay.shader(colorFinder);
  overlay.image(video, 0, 0);
  overlay.endDraw();
  posBuffer.beginDraw();
  posBuffer.shader(colorPosShader);
  posBuffer.image(video, 0, 0);
  posBuffer.endDraw();
  //compute average position by looking at pixels from position buffer
  posBuffer.loadPixels();
  PVector avg = new PVector(0, 0);
  int count = 0;
  for(int i = 0; i < posBuffer.pixels.length; i++){
    // encoded so blue is > 0 if a pixel is within threshold
    if(blue(posBuffer.pixels[i]) > 0){
      count++;
      // processing takes 0-1 (float) color values from shader to 0-255 (int) values for color
      // to decode, we need to divide the color by 255 to get the original value
      avg.add(red(posBuffer.pixels[i]) / 255.0, green(posBuffer.pixels[i]) / 255.0);
    }
  }
  if(count > 0){
    // we have the sum of positions, so divide by the number of additions
    avg.div((float) count);
    // convert 0-1 position to screen position
    avg.x *= width;
    avg.y *= height;
  } else {
    // appear offscreen
    avg = new PVector(-100, -100);
  }
  image(overlay, 0, 0);
  fill(trackColor);
  stroke(0);
  circle(avg.x, avg.y, 16);
  fill(0, 50);
  noStroke();
  rect(0, 0, 150, 30);
  fill(150);
  text("Framerate: " + frameRate, 0, 11);
  text("Threshold: " + threshold, 0, 22);
}

void mousePressed() {
  // Save color where the mouse is clicked in trackColor variable
  video.loadPixels();
  int loc = mouseX + mouseY*video.width;
  trackColor = video.pixels[loc];
}

void mouseWheel(MouseEvent e){
  threshold -= e.getCount() * 0.01;
  threshold = constrain(threshold, 0, 1);
}

this is the whole picture of the code

I was unable to run the demo that you posted due to a null pointer exception:

In lieu of working on your project I propose that we work on a simpler demo using any .jpg image that you might have on your system and then running the following code:

int counter = 0;

void setup() {
  size(400, 400);
  PImage img = loadImage("myImg.jpg");
  image(img, 0, 0, 400, 400);
  loadPixels();
  color pink = color(255, 102, 204);
  for (int y = 0; y < height; y++) {
    for (int x = 0; x < width; x++) {
      if (x > 100 && y > 240) {
        pixels[counter] = pink;
      }
      counter++;
    }
  }
  updatePixels();
}

void draw() {
}

The only thing required is to place your image into the sketch folder so that Processing can find it. What you should see is that any pixel with an x-coordinate > 100 and a y-coordinate > 240 is painted pink. On my system this corresponds to the lower right corner of the image. Please see if you get the same thing and let me know if this is similar to what you want to do.

1 Like

if you don’t mind following is my first code that i am trying to adapt to 1D array

    import processing.video.*;
    Capture video;
    
    float threshold = 210;
    color trackColor; 
    PVector target;
    
    void setup() {
     
    size(640, 480);
    video = new Capture(this, width, height);
    video.start();
     trackColor = color(160,0,0); // Start off tracking for red    
    }
    
    void captureEvent(Capture video) {
    // Read image from the camera
    video.read();
    }
    
    void draw() {
    loadPixels();
    video.loadPixels();
    image(video, 0, 0);
    float avgX = 0;
    float avgY = 0;
     int count = 0;
    
    for (int x = 0; x < video.width && x < 100; x ++ ) {
    for (int y = 240; y < video.height; y ++ ) {
    int loc = x + y*video.width;   
    color currentColor = video.pixels[loc];
    float r1 = red(currentColor);
    float g1 = green(currentColor);
    float b1 = blue(currentColor);
    float r2 = red(trackColor);
    float g2 = green(trackColor);
    float b2 = blue(trackColor);
    
    // Using euclidean distance to compare colors
    float d = distSq(r1, g1, b1, r2, g2, b2);
    if (d < threshold) {
    stroke(255);
    strokeWeight(1);
    point(x,y);
     
    avgX += x;
    avgY += y;
     count++;
    }
    }
    }
    if (count > 0) { 
    avgX = avgX / count;
    avgY = avgY / count;
    // Draw a circle at the tracked pixel
    fill(trackColor);
    strokeWeight(4.0);
    stroke(0);
    ellipse(avgX, avgY, 20, 20);
    text("brightnesslevel: " + trackColor, 20, 60);
    text("FPS: " + frameRate, 20, 80);
    }
    target = new PVector (avgX, avgY);
    }
    
    float distSq(float x1,float y1, float z1, float x2, float y2, float z2){
    float d = (x2-x1)*(x2-x1) + (y2-y1)*(y2-y1) + (z2-z1)*(z2-z1);
    return d;
    }
    
    void mousePressed() {
    // Save color where the mouse is clicked in trackColor variable
    int loc = mouseX + mouseY*video.width;
    trackColor = video.pixels[loc];
    }

The second code posted runs ok and after hooking up my web camera shows my image sitting in front of the computer. When the mouse is clicked the pixel color under the cursor is used to fill a small circle in the lower left hand corner of the screen. How does that relate to the question that you asked? What are you hoping to achieve that this code doesn’t already do? You keep talking about a one dimensional array and I fail to see the connection.

achive by this snippet

for (int x = 0; x < video.width && x < 100; x ++ ) {
    for (int y = 240; y < video.height; y ++ ) {

and that is i want to with following code

 for(int i = 0; i < posBuffer.pixels.length; i++){
    // encoded so blue is > 0 if a pixel is within threshold
    if(blue(posBuffer.pixels[i]) > 0){
      count++;
      // processing takes 0-1 (float) color values from shader to 0-255 (int) values for color
      // to decode, we need to divide the color by 255 to get the original value
      avg.add(red(posBuffer.pixels[i]) / 255.0, green(posBuffer.pixels[i]) / 255.0);
    }
  }

i think is needed to change this

posBuffer.pixels.length

or this

posBuffer.pixels

to become 2D array something like this

CopyposBuffer[x][y]

so the 2D array could receive x ,y limited value which iterate in for loop

Try something like this:

  for (int x = 0; x < video.width && x < 100; x ++ ) {
    for (int y = 240; y < video.height; y ++ ) {
    int loc = x + y*video.width;   
    if(blue(video.pixels[loc]) > 0){
      count++;
      // processing takes 0-1 (float) color values from shader to 0-255 (int) values for color
      // to decode, we need to divide the color by 255 to get the original value
      avg.add(red(video.pixels[loc]) / 255.0, green(video.pixels[loc]) / 255.0);  
    }
  }
}

I can’t run it because my system doesn’t know what avg.add(); is.

When run with this line REMmed out, the circle is now at the top of the screen. Is that what you want to change?

i try already with this

 for (int x = 0; x < video.width && x < 100; x ++ ) {
    for (int y = 240; y < video.height; y ++ ) {
   int i = y * video.width + x;
        if(blue(posBuffer.pixels[i]) > 0){
    count++;
    // processing takes 0-1 (float) color values from shader to 0-255 (int) values for color
    // to decode, we need to divide the color by 255 to get the original value
    avg.add(red(posBuffer.pixels[i]) / 255.0, green(posBuffer.pixels[i]) / 255.0);
  }  
    }
  }

the circle movement is already limited by the x , y value in the iteration but the color tracked is still drawn over x ,y value

What’s the problem now? I didn’t understand your last comment.

If you want the circle to be directly over the pixel sampled then do something like this:

  float locX = 0;
  float locY = 0;

  for (int x = 0; x < video.width && x < 100; x ++ ) {
    for (int y = 240; y < video.height; y ++ ) {
      int loc = x + y*video.width;
      locX = mouseX;
      locY = mouseY;
      if (blue(video.pixels[loc]) > 0) {
        count++;
        // processing takes 0-1 (float) color values from shader to 0-255 (int) values for color
        // to decode, we need to divide the color by 255 to get the original value
        // avg.add(red(video.pixels[loc]) / 255.0, green(video.pixels[loc]) / 255.0);
      }
    }
  }

...

ellipse(locX, locY, 20, 20);