Created
May 21, 2014 18:17
-
-
Save takashi/a57d48230af5b481d1c9 to your computer and use it in GitHub Desktop.
Get three characteristic colors in image(like itunes covers).
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(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