"Get" as color detector?

Hello Forum :slight_smile:

On Processing, I love this program for detecting colors:

color c;

void setup (){
  size (200,200);
}

void draw() {
  background (0);
  fill (255);
  rect (0,0,100,200); 
  c = get (mouseX,mouseY);
  
  if (c == -1){
    print ("the color is white ");
  }  else if (c == -16777216){
    fill (0,255,0);
    print ("the color is black "); 
 }
}

But, I am seriously struggling to convert this simple program to p5js. It seems that ā€˜get’ works a bit differently with p5js.

Also, can anyone give me more context on what format the color codes are in this Processing sketch?

I think you have use some of the functions like red( c),green( c),blue( c),brightness( c) to find out the exact color.

Hello,

get() in P5.js reads the color into an array:
https://p5js.org/reference/#/p5/get

Some code I patched together to explore this:

Example
let c = [0, 0, 0, 0];
let c1 = [255, 255, 255, 255];
let c2 = [0, 0, 0, 255];

let state = false;

function setup() 
  {
  createCanvas(400, 400);
  print ("Begin...");    
  }

function draw() 
  {
  background (0);

  fill (255);
  rect (0, 0, 200, 200); 
  
  c = get(mouseX, mouseY);   
  print(c);
  
  state = isEqual1();
  //print(state);
  if (state)
    {
    print ("the color is white ");
    state=false;  
    }
    
  state = isEqual2();
  //print(state);
  if (state)
    {
    print ("the color is black ");
    state=false;  
    }    
  }
  
function isEqual1() 
  { 
  // comapring each element of array 
   for(var i=0;i<c.length;i++)
     {
     if(c[i] != c1[i]) 
       {
       return false; 
       }        
    return true; 
    } 
  }   

function isEqual2() 
  { 
  // comapring each element of array 
   for(var i=0;i<c.length;i++)
     {
     if(c[i] != c2[i]) 
       {
       return false; 
       }        
    return true; 
    } 
  }   

Example Update - Better function
let c = [0, 0, 0, 0];
let c1 = [255, 255, 255, 255];
let c2 = [0, 0, 0, 255];

let state = false;

function setup() 
  {
  createCanvas(400, 400);
  print ("Begin...");    
  }

function draw() 
  {
  background (0);

  fill (255);
  rect (0, 0, 200, 200); 
  
  c = get(mouseX, mouseY);   
  print(c);
  
  state = isEqual(c, c1);
  //print(state);
  if (state)
    {
    print ("the color is white ");
    state=false;  
    }
    
  state = isEqual(c, c2);
  //print(state);
  if (state)
    {
    print ("the color is black ");
    state=false;  
    }    
  }
  
function isEqual(col1, col2) 
  { 
  // comparing each element of array 
   for(var i=0;i<c.length;i++)
     {
     if(col1[i] != col2[i])       //If any one is != then it is false
       {
       return false; 
       }        
    return true; 
    } 
  }   

This was just an initial exploration and I compared arrays.
This can be done much better…

Borrowed some elements from here:
How to compare two arrays in JavaScript? - GeeksforGeeks

:)

2 Likes

get()

"Get a region of pixels, or a single pixel, from the canvas.

Returns an array of [R,G,B,A] values for any pixel or grabs a section of an image."

2 Likes

Actually very differently I’m afraid! :cold_sweat:

Processing’s get() returns a 32-bit (4-byte) signed int aRGB color value:

While in p5js get() returns an array containing 4 values ordered as RGBa:

If you don’t mind using pixels[] in place of get():

I’ve got this project which is already finished (but still untested & unpublished) where we can invoke getProcessingPixels() passing an image (or its corresponding pixels[]) as its argument, and then it returns an Int32Array proxy viewer of it:

That returned Int32Array proxy viewer should behave just like a Processing’s aRGB pixels[]:

So you can more easily convert Processing’s sketches relying on pixels[] to p5js: :partying_face:

// https://Discourse.processing.org/t/get-as-color-detector/19912/5
// GoToLoop (2020-Apr-19)

'use strict';

function getProcessingPixels(imgOrPixels) {
  const { buffer } = imgOrPixels.pixels && imgOrPixels.pixels || imgOrPixels;

  return new Proxy(
    new Int32Array(buffer), getProcessingPixels.INT32_aRGB_HANDLE
  );
}

getProcessingPixels.isLittleEndian = function () {
  return new Uint8Array(Uint32Array.of(0x12345678).buffer)[0] === 0x78;
};

getProcessingPixels.int32aRGBLittleEndianHandle = {
  set(obj, prop, val) { // aRGB -> aBGR (RGBa)
    const idx = +prop.toString();

    if (idx >= 0 && idx < obj.length && Number.isInteger(idx)) {
      const red = val >> 0o20 & 0xff, blue = (val & 0xff) << 0o20;
      val = val & 0xff00ff00 | red | blue;
    }

    return obj[prop] = val, true;
  },

  get(obj, prop) { // aBGR (RGBa) -> aRGB
    const idx = +prop.toString(), val = obj[prop];

    if (idx >= 0 && idx < obj.length && Number.isInteger(idx)) {
      const red = (val & 0xff) << 0o20, blue = val >> 0o20 & 0xff;
      return val & 0xff00ff00 | red | blue;
    }

    return typeof val === 'function' && val.bind(obj) || val;
  }
};

getProcessingPixels.int32aRGBBigEndianHandle = {
  set(obj, prop, val) { // aRGB -> RGBa
    const idx = +prop.toString();

    if (idx >= 0 && idx < obj.length && Number.isInteger(idx))
      val = val >>> 0o30 | val << 0o10;

    return Reflect.set(obj, prop, val);
  },

  get(obj, prop) { // RGBa -> aRGB
    const idx = +prop.toString(), val = Reflect.get(obj, prop);

    if (idx >= 0 && idx < obj.length && Number.isInteger(idx))
      return val << 0o30 | val >>> 0o10;

    return typeof val === 'function' && val.bind(obj) || val;
  }
};

getProcessingPixels.LITTLE_ENDIAN = getProcessingPixels.isLittleEndian();
getProcessingPixels.BIG_ENDIAN = !getProcessingPixels.LITTLE_ENDIAN;

getProcessingPixels.INT32_aRGB_HANDLE =
  getProcessingPixels.LITTLE_ENDIAN &&
  getProcessingPixels.int32aRGBLittleEndianHandle ||
  getProcessingPixels.int32aRGBBigEndianHandle;
2 Likes

Processing and p5.js color work very differently, and that is surprising. There is a lot of great information on how / why above, but here is a walkthrough of a solution to your students using get() as a color detector in their projects.

In Processing (Java mode),

get() and color() return the same things (the color datatype, a 32-bit int representing ARGB), which can be compared using ==.

Let’s detect if the mouse is on white:

// check mouse color
void setup() {
  size(100,100);
  rect(25,25,50,50);
}

void draw() {
  int cwhite = color(255);
  int cmouse = get(mouseX, mouseY);
  if(cmouse==cwhite) {
    println(frameCount, cmouse);
  }
}

In p5.js (JavaScript),

get() and color() return DIFFERENT things, AND, those things can’t be compared with equals – to each other, OR to themselves(!!). get() returns an array of floats [R, G, B, A]. color() returns a p5.Color object, which contains multiple arrays, including one with four 0-255 values, called ā€œlevelsā€. In JavaScript, arrays and objects with the same contents are not found equal with ==.

To adapt the Processing sketch to p5.js, we need two things:

  1. define our color to check for as a flat array, [R, G, B, A], so it will match get(). Either declare it directly [] or use color(my_arguments).levels.
  2. create an equals function to compare two colors. There are many ways to do this, but we’ll use one that is easy to read and fast.
// check mouse color
function setup() {
  createCanvas(100, 100);
  rect(25,25,50,50);
}

function draw() {
  let cmouse = get(mouseX, mouseY); // [R, G, B, A]
  let cwhite = [255,255,255,255];
  // let cwhite = color(255).levels; 
  if(eqcolor(cmouse, cwhite)) {
    print(frameCount, cmouse);
  }
}

function eqcolor(a, b) {
  return a[0] == b[0] && a[1] == b[1] && a[2] == b[2] && a[3] == b[3];
}
<iframe src="https://editor.p5js.org/jeremydouglass/sketches/JsVeospiC" width="100%" height="510" allowfullscreen frameborder="0" marginwidth="0" marginheight="0"></iframe>
2 Likes

Thank you so much everyone!!! Wow!!!

This is so tremendously helpful for teaching! Thank you!

1 Like

Quick question about syntax/curly bracket structure :slight_smile:

brackets

Does the use of ā€œreturnā€ change the need for an opening curly bracket with the if conditional?

Sorry, if doesn’t need {} when the condition is only a single line. But that is confusing for learners – I’ll fix in the examples above.

Also:

How to do it without an equality function.

Instead of a custom equality function:

if(eqcolor(cmouse, cwhite))

drop eqcolor and use .toString() instead:

if(cmouse == cwhite.toString())

Example sketch:

https://editor.p5js.org/jeremydouglass/sketches/wNZhvyugn

This works because in JavaScript,

print([1,2] == [1,2])  // false
print([1,2] == '1,2')  // true

Sigh. I honestly don’t like doing this because even though the code is shorter learners find it deeply, deeply confusing, so they forget the .toString() and get frustrated when it doesn’t work. But that might be a question of taste.

1 Like

Awesome - got to keep things consistent for myself and my students. Much appreciated!

Actually b/c the whole expression is evaluated as 1 boolean, we can simply use 1 return only for it:

function eqColor(a, b) {
  return a[0] == b[0] && a[1] == b[1] && a[2] == b[2] && a[3] == b[3];
}

So there’s no need for 1-statement if nor else in this case.

Another alternative version using for:

function eqColor(a, b) {
  for (var i = 0; i < a.length; ++i)  if (a[i] != b[i])  return false;
  return true;
}
1 Like

Good point. I’ll going to update the examples with that.

The for version is more versatile, and also fails-fast – but I wanted to make it explicit for learners that we expect to match exactly four term pairs.

2 Likes