Skip to content

Instantly share code, notes, and snippets.

@takashi
Created May 21, 2014 18:17
Show Gist options
  • Save takashi/a57d48230af5b481d1c9 to your computer and use it in GitHub Desktop.
Save takashi/a57d48230af5b481d1c9 to your computer and use it in GitHub Desktop.
Get three characteristic colors in image(like itunes covers).
(function(global){
/**
* @constructor
*/
function Colorize(callback, opt_src) {
if (opt_src) {
this.image = document.createElement('image');
this.image.src = opt_src;
} else {
this.image = document.getElementById(Colorize.TARGET_ID);
}
this.canvas = document.createElement('canvas');
this.context = this.canvas.getContext('2d');
this.edgeColor;
this.topColors = [];
this.image.onload = function() {
// reduce size to quicken calculation.
var iw = this.image.width,
ih = this.image.height,
ch = ih * 36 / iw;
this.canvas.width = 36;
this.canvas.height = ch;
// cut the border
this.context //.drawImage(this.image,0,0);
.drawImage(this.image, iw * 0.03, ih * 0.03, iw * 0.97, ih * 0.97, 0, 0, this.canvas.width, this.canvas.height);
// get pixelArray
this.getColor(callback);
}.bind(this)
}
Colorize.TARGET_ID = 'js-colorize';
var p = Colorize.prototype;
/**
* Get pixel array and convert those to 8bit colors.
*/
p.getColor = function(callback) {
var canvas = this.canvas;
var w = canvas.width;
var h = canvas.height;
var imageData = this.context.getImageData(0,0,w,h);
var pixelArray = imageData.data;
var hist = this.colorHistogram = new Uint32Array(512);
var edge = this.edgeColor = new Uint32Array(512);
var x, y, pos, color;
for (y = 0; y < h; ++y) {
for (x = 0; x < w; ++x) {
pos = (y * w + x) * 4;
// reduction but 36px?
color =
this.rgb2int(pixelArray[pos], pixelArray[pos+1], pixelArray[pos+2]);
hist[color]++;
if (x === 0 || x === w-1 || y === 0 || y === h-1) {
edge[color]++;
}
}
}
callback(this);
};
/**
* Ensure background color.
* @return {[type]} [description]
*/
p.ensureSuperiorFromEdge = function() {
var hist = this.colorHistogram;
var edge = this.edgeColor;
var color = 0;
for (var i = 0, imageLength = edge.length; i < imageLength; ++i) {
if (edge[i] > 0 && hist[i] > hist[color]) {
color = i;
}
}
this.edgeColor = this.int2rgb(color);
return this.edgeColor;
};
/**
* Ensure superior color from all.
* @return {[type]} [description]
*/
p.ensureSuperiorFromAll = function() {
if (!this.edgeColor) {
this.ensureSuperiorFromEdge();
}
var edgeColor = this.edgeColor;
var intEdgeColor = this.rgb2int.apply(this, edgeColor);
var yuvEdgeColor = this.rgb2Ycrcb.apply(this, edgeColor);
var hist = this.colorHistogram;
var distance, prevHist, max;
var colors = [];
var results = [];
var analyzed = new Uint32Array(512);
var topDistanse = 0;
var prevValue = 0
Array.prototype.forEach.call(hist, function(value, color){
if (color === intEdgeColor) {
return;
}
// ??
if (this.topColors.length) {
var topDistance =
topDistance ||
this.getEucideanDistance(
this.rgb2Ycrcb.apply(this, this.topColors[0]),
this.rgb2Ycrcb.apply(this, this.int2rgb(color))
);
}
distance = this.getEucideanDistance(
this.rgb2Ycrcb.apply(this, this.int2rgb(color)),
yuvEdgeColor
);
if (topDistance !== 0 && topDistance > 50) {
if (value > prevValue && distance > 50) {
max = color
prevValue = value;
}
} else {
if (value > prevValue && distance > 50) {
max = color
prevValue = value;
}
}
},this)
// add top colors.
var c = this.int2rgb(max)
this.topColors.push(c)
// remove top color for next loop.
this.colorHistogram[max] = 0;
return c;
};
/**
* Get Encidean distacen between two color.
* @param {Array} yuv1 yuv color array.
* @param {Array} yuv2 yuv color array.
* @return {Number} Eucidean distance.
*/
p.getEucideanDistance = function(yuv1, yuv2) {
return Math.sqrt(Math.pow(yuv1[0] - yuv2[0], 2) + Math.pow(yuv1[1] - yuv2[1], 2) + Math.pow(yuv1[2] - yuv2[2], 2));
};
/**
* Convert rgb to yuv.
* @param {Number} red red of rgb.
* @param {Number} green green of rgb.
* @param {Number} blue blue of rgb.
* @return {Array} yuv.
*/
p.rgb2Ycrcb = function(red, green, blue) {
var y = (0.29891*red) + (0.58661*green) + (0.11448*blue);
var cb = -(0.16874 * red) - (0.33126 * green) + (0.500 * blue);
var cr = (0.50000 * red) - (0.41869 * green) - (0.08131 * blue);
return [y, cb, cr];
};
p.ycrcb2Rgb = function(y, cb, cr) {
var red = y + 1.40200 * cr;
var green = y - 0.34414 * cb - 0.71414 * cr;
var blue = y + 1.77200 * cb;
return [red, green, blue];
};
p.rgb2int = function(red, green, blue) {
return (
((red >> 5) << 6) |
((green >> 5) << 3) |
((blue >> 5) << 0)
);
};
p.int2rgb = function(color) {
return [
((color >> 6 & 0x7) << 5) + 16,
((color >> 3 & 0x7) << 5) + 16,
((color >> 0 & 0x7) << 5) + 16
];
};
global.Colorize = Colorize;
})(this);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment