Hope
Hope

Reputation: 15

How to get the average color of a specific area in a webcam feed (Processing/JavaScript)?

I'm using Processing to get a webcam feed from my laptop. In the top left corner, I have drawn a rectangle over the displayed feed. I'm trying to get the average color of the webcam, but only in the region contained by that rectangle.

I keep getting color (0, 0, 0), black, as the result.

Thank you all!

PS sorry if my code seems messy..I'm new at Processing and so I don't know if this might be hard to read or contain bad practices. Thank you.

import processing.video.*;

Capture webcam;
Capture cap;
PImage bg_img;

color bgColor = color(0, 0, 0);

int rMargin = 50;
int rWidth = 100;

color input = color(0, 0, 0);
color background = color(255, 255, 255);
color current;
int bgTolerance = 5;

void setup() {
  size(1280,720);

  // start the webcam
  String[] inputs = Capture.list();
  if (inputs.length == 0) {
    println("Couldn't detect any webcams connected!");
    exit();
  }
  webcam = new Capture(this, inputs[0]);

  webcam.start();

}


void draw() {
  if (webcam.available()) {

    // read from the webcam
    webcam.read();

    image(webcam, 0,0);
    webcam.loadPixels();

    noFill();
    strokeWeight(2);
    stroke(255,255, 255);
    rect(rMargin, rMargin, rWidth, rWidth);

    int yCenter = (rWidth/2) + rMargin;
    int xCenter = (rWidth/2) + rMargin;
    // rectMode(CENTER);

    int rectCenterIndex = (width* yCenter) + xCenter;

    int r = 0, g = 0, b = 0;

    //for whole image:
    //for (int i=0; i<bg_img.pixels.length; i++) {
    //    color c = bg_img.pixels[i];
    //    r += c>>16&0xFF;
    //    g += c>>8&0xFF;
    //    b += c&0xFF;
    //}
    //r /= bg_img.pixels.length;
    //g /= bg_img.pixels.length;
    //b /= bg_img.pixels.length;

    //CALCULATE AVG COLOR:
    int i;
for(int x = 50; x <= 150; x++){
   for(int y = 50; y <= 150; y++){
           i = (width*y) + x;
           color c = webcam.pixels[i];
           r += c>>16&0xFF;
           g += c>>8&0xFF;
           b += c&0xFF;
       }
    }
    r /= webcam.pixels.length;
    g /= webcam.pixels.length;
    b /= webcam.pixels.length;

    println(r + " " + g + " " + b);
  }
}

Upvotes: 1

Views: 1141

Answers (1)

George Profenza
George Profenza

Reputation: 51867

You're so close, but missing out one important aspect: the number of pixels you're sampling.

Notice in the example code that is commented out for a full image you're dividing by the full number of pixels (pixels.length).

However, in your adapted version you want to compute the average colour of only a subsection of the full image which means a smaller number of pixels.

You're only sampling an area that is 100x100 pixels meaning you need to divide by 10000 instead of webcam.pixels.length (1920x1000). That is why you get 0 as it's integer division. This is what I mean in code:

 int totalSampledPixels = rWidth * rWidth;
 r /= totalSampledPixels;
 g /= totalSampledPixels;
 b /= totalSampledPixels;

Full tweaked sketch:

import processing.video.*;

Capture webcam;
Capture cap;
PImage bg_img;

color bgColor = color(0, 0, 0);

int rMargin = 50;
int rWidth = 100;
int rHeight = 100;

color input = color(0, 0, 0);
color background = color(255, 255, 255);
color current;
int bgTolerance = 5;

void setup() {
  size(1280,720);

  // start the webcam
  String[] inputs = Capture.list();
  if (inputs.length == 0) {
    println("Couldn't detect any webcams connected!");
    exit();
  }
  webcam = new Capture(this, inputs[0]);

  webcam.start();

}


void draw() {
  if (webcam.available()) {

    // read from the webcam
    webcam.read();

    image(webcam, 0,0);
    webcam.loadPixels();

    noFill();
    strokeWeight(2);
    stroke(255,255, 255);
    rect(rMargin, rMargin, rWidth, rHeight);

    int yCenter = (rWidth/2) + rMargin;
    int xCenter = (rWidth/2) + rMargin;
    // rectMode(CENTER);

    int rectCenterIndex = (width* yCenter) + xCenter;

    int r = 0, g = 0, b = 0;

    //for whole image:
    //for (int i=0; i<bg_img.pixels.length; i++) {
    //    color c = bg_img.pixels[i];
    //    r += c>>16&0xFF;
    //    g += c>>8&0xFF;
    //    b += c&0xFF;
    //}
    //r /= bg_img.pixels.length;
    //g /= bg_img.pixels.length;
    //b /= bg_img.pixels.length;

    //CALCULATE AVG COLOR:
    int i;
    for(int x = 0; x <= width; x++){
       for(int y = 0; y <= height; y++){
         if (x >= rMargin && x <= rMargin + rWidth && y >= rMargin && y <= rMargin + rHeight){

           i = (width*y) + x;
           color c = webcam.pixels[i];
           r += c>>16&0xFF;
           g += c>>8&0xFF;
           b += c&0xFF;

         }
       }
    }
    //divide by just the area sampled (x >= 50 && x <= 150 && y >= 50 && y <= 150 is a 100x100 px area) 
    int totalSampledPixels = rWidth * rHeight;

    r /= totalSampledPixels;
    g /= totalSampledPixels;
    b /= totalSampledPixels;

    fill(r,g,b);
    rect(rMargin + rWidth, rMargin, rWidth, rHeight);

    println(r + " " + g + " " + b);
  }
}

Bare in mind this is averaging in the RGB colour space which is not the same as perceptual colour space. For example, if you average red and yellow you'd expect orange, but in RGB, a bit of red and green makes yellow.

Hopefully the RGB average is good enough for what you need, otherwise you may need to convert from RGB to CIE XYZ colour space then to Lab colour space to compute the perceptual average (then convert back to XYZ and RGB to display on screen). If that is something you're interested in trying, you can find an older answer demonstrating this in openFrameworks (which you'll notice can be similar to Processing in simple scenarios).

Upvotes: 1

Related Questions