mheavers
mheavers

Reputation: 30158

Finding the closest indexed color value to the current color in javascript / p5.js

I have an array of "indexed" RGBA color values, and for any given image that I load, I want to be able to run through all the color values of the loaded pixels, and match them to the closest of my indexed color values. So, if the pixel in the image had a color of, say, RGBA(0,0,10,1), and the color RGBA(0,0,0,1) was my closest indexed value, it would adjust the loaded pixel to RGBA(0,0,0,1).

I know PHP has a function imagecolorclosest

int imagecolorclosest( $image, $red, $green, $blue )

Does javascript / p5.js / processing have anything similar? What's the easiest way to compare one color to another. Currently I can read the pixels of the image with this code (using P5.js):

let img;
function preload() {
  img = loadImage('assets/00.jpg');
}

function setup() {
  image(img, 0, 0, width, height);
  let d = pixelDensity();
  let fullImage = 4 * (width * d) * (height * d);
  loadPixels();
  for (let i = 0; i < fullImage; i+=4) {
    let curR = pixels[i];
    let curG = pixels[i]+1;
    let curB = pixels[i+2];
    let curA = pixels[i+3];
  }
  updatePixels();
}

Upvotes: 3

Views: 1296

Answers (2)

Rabbid76
Rabbid76

Reputation: 210909

Each color consists 3 color channels. Imagine the color as a point in a 3 dimensional space, where each color channel (red, green, blue) is associated to one dimension. You've to find the closest color (point) by the Euclidean distance. The color with the lowest distance is the "closest" color.

In p5.js you can use p5.Vector for vector arithmetic. The Euclidean distance between to points can be calculated by .dist(). So the distance between points respectively "colors" a and b can be expressed by:

let a = createVector(r1, g1, b1);
let b = createVector(r2, g2, b2);
let distance = a.dist(b);

Use the expression somehow like this:

colorTable = [[r0, g0, b0], [r1, g1, b1] ... ];

int closesetColor(r, g, b) {
    let a = createVector(r, g, b);
    let minDistance;
    let minI;
    for (let i=0; i < colorTable; ++i) {
        let b = createVector(...colorTable[i]);
        let distance = a.dist(b);
        if (!minDistance || distance < minDistance) {
            minI = i; minDistance = distance;
        }
    }
    return minI;
}

function setup() {
    image(img, 0, 0, width, height);
    let d = pixelDensity();
    let fullImage = 4 * (width * d) * (height * d);
    loadPixels();

    for (let i = 0; i < fullImage; i+=4) {
        let closestI = closesetColor(pixels[i], pixels[i+1], pixels[i+2])
        pixels[i]   = colorTable[closestI][0];
        pixels[i+1] = colorTable[closestI][1];
        pixels[i+2] = colorTable[closestI][2];
    }
    updatePixels();
}

Upvotes: 2

nshoo
nshoo

Reputation: 977

If I understand you correctly you want to keep the colors of an image within a certain limited pallet. If so, you should apply this function to each pixel of your image. It will give you the closest color value to a supplied pixel from a set of limited colors (indexedColors).

// example color pallet (no alpha)
indexedColors = [
  [0, 10, 0],
  [0, 50, 0]
];

// Takes pixel with no alpha value
function closestIndexedColor(color) {
  var closest = {};
  var dist;
  for (var i = 0; i < indexedColors.length; i++) {
    dist = Math.pow(indexedColors[i][0] - color[0], 2);
    dist += Math.pow(indexedColors[i][1] - color[1], 2);
    dist += Math.pow(indexedColors[i][2] - color[2], 2);
    dist = Math.sqrt(dist);

    if(!closest.dist || closest.dist > dist){
        closest.dist = dist;
      closest.color = indexedColors[i];
    }
  }
  // returns closest match as RGB array without alpha
  return closest.color;
}

// example usage
closestIndexedColor([0, 20, 0]); // returns [0, 10, 0]

It works the way that the PHP function you mentioned does. If you treat the color values as 3d coordinate points then the closet colors will be the ones with the smallest 3d "distance" between them. This 3d distance is calculated using the distance formula:

3d distance formula

Upvotes: 2

Related Questions