Created
December 18, 2012 21:49
-
-
Save harrislapiroff/4332363 to your computer and use it in GitHub Desktop.
Selects colors from an image.
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
/* | |
* ColorExtractor | |
* | |
* Usage: | |
* | |
* extractor = new ColorExtractor("/path/to/image.jpg"); | |
* extractor.ready(function (data) { | |
* document.body.styles.backgroundColor = data['edge_color'].hex_for_css(); | |
* }); | |
* | |
*/ | |
(function () { | |
"use strict"; | |
var ColorExtractor, Color, | |
average_color, mode_color; | |
// ColorExtractor | |
ColorExtractor = window.ColorExtractor = function () { | |
// ColorExtractor takes a single image argument in the form of | |
// either a string URL or an image element. | |
return this._init.apply(this, arguments); | |
}; | |
ColorExtractor.prototype = { | |
// Some Attributes | |
_canvas: null, | |
_image: null, | |
_edge_tolerance: 5, | |
_ready_fns: [], | |
_complete: false, | |
_payload: {}, | |
// Some Methods | |
_init: function (image_) { | |
var that = this, | |
image, canvas; | |
// instantiate the canvas for use later | |
canvas = this._canvas = document.createElement('canvas'); | |
// instantiate the image | |
if (typeof image_ === "string") { | |
// if image_ is a url, create the image element/ | |
image = this._image = new Image(); | |
image.src = image_; | |
// I'm fairly sure the image does *not* need to be added | |
// to the page in order to load. | |
} else { | |
// otherwise, assume image_ is already an image element. | |
image = this._image = image_; | |
} | |
if (image.complete) { | |
// if the image is ready, calculate the colors immediately | |
this.calculate_colors(); | |
} else { | |
// otherwise, bind to the image's ready event | |
image.addEventListener("load", function () { that.calculate_colors.call(that); }); | |
} | |
}, | |
calculate_colors: function () { | |
var canvas = this._canvas, | |
image = this._image, | |
context = canvas.getContext('2d'), | |
return_data = {}; | |
// make the canvas dimensions the same as the image | |
canvas.width = image.width; | |
canvas.height = image.height; | |
// draw the image onto the canvas | |
context.drawImage(this._image, 0, 0, this._image.width, this._image.height); | |
return_data['edge_color'] = this.find_edge_color(); | |
// fire the ready functions | |
this._ready(return_data); | |
// mark complete | |
this._complete = true; | |
// cache the payload | |
this._payload = return_data | |
return return_data; | |
}, | |
find_edge_color: function (tolerance) { | |
var tolerance = tolerance || this._edge_tolerance, | |
canvas = this._canvas, | |
context = canvas.getContext('2d'), | |
colors = [], | |
n, w, s, e; | |
// get west side | |
w = context.getImageData(0, 0, tolerance, canvas.height - tolerance).data; | |
// get south side | |
s = context.getImageData(0, canvas.height - tolerance, canvas.width - tolerance, tolerance).data; | |
// get east side | |
e = context.getImageData(canvas.width - tolerance, tolerance, tolerance, canvas.height - tolerance).data; | |
// get north side | |
n = context.getImageData(tolerance, 0, canvas.width - tolerance, tolerance).data; | |
// flatten all the pixel color values into a single array | |
for (var i in [w, s, e, n]) { | |
var arr = [w, s, e, n][i] | |
for (var j=0; j<arr.length; j+=4) { | |
colors.push(new Color(arr[j], arr[j+1], arr[j+2], arr[j+3])) | |
} | |
} | |
// run that array through the color averager | |
return mode_color(colors); | |
}, | |
_ready: function (payload) { | |
var fns = this._ready_fns; | |
// fire the ready functions | |
for (var i=0; i<fns.length; i++) { | |
fns[i](payload); | |
} | |
console.log(payload) | |
}, | |
ready: function (fn) { | |
// register a function to be fired after the colors have been calculated | |
this._ready_fns.push(fn); | |
// if calculation has already occurred, fire the function | |
if (this._complete) fn(this._payload); | |
return fn; | |
}, | |
}; | |
// Color | |
Color = function () { | |
// Color takes four arguments in the form of | |
// r, g, b, a values out of 255. | |
return this._init.apply(this, arguments); | |
}; | |
Color.prototype = { | |
_r: 0, | |
_g: 0, | |
_b: 0, | |
_a: 0, | |
_init: function (r, g, b, a) { | |
this._r = r || this._r; | |
this._g = g || this._g; | |
this._b = b || this._b; | |
this._a = a || this._a; | |
}, | |
rgba_for_css: function () { | |
return "rgba(" + [this._r, this._g, this._b, this._a].join(",") + ")"; | |
}, | |
hex_for_css: function () { | |
return "#" + [ | |
this._r > 15 ? Math.floor(this._r).toString(16) : "0" + Math.floor(this._r).toString(16), | |
this._g > 15 ? Math.floor(this._g).toString(16) : "0" + Math.floor(this._g).toString(16), | |
this._b > 15 ? Math.floor(this._b).toString(16) : "0" + Math.floor(this._b).toString(16) | |
].join(""); | |
} | |
}; | |
// Utility Functions | |
average_color = function (array) { | |
var _r = 0, | |
_g = 0, | |
_b = 0, | |
_a = 0; | |
for (var i=0; i<array.length; i++){ | |
_r += array[i]._r; | |
_g += array[i]._g; | |
_b += array[i]._b; | |
_a += array[i]._a; | |
} | |
return new Color(_r/array.length, _g/array.length, _b/array.length, _a/array.length); | |
}; | |
mode_color = function (array) { | |
var _r = 0, | |
_g = 0, | |
_b = 0, | |
_a = 0, | |
color_count = {}, | |
sorted_color_count = [], | |
index, new_color; | |
// count the # of pixels of each distinct color | |
for (var i=0; i<array.length; i++){ | |
index = array[i]._r + "," + array[i]._g + "," + array[i]._b; | |
if (!color_count[index]) color_count[index] = 1; | |
color_count[index]++; | |
} | |
// add each count > 2 to sorted_color_count | |
for (var key in color_count){ | |
if (color_count.hasOwnProperty(key)) { | |
if (color_count[key] > 2) { | |
sorted_color_count.push([key, color_count[key]]); | |
} | |
} | |
} | |
// sort the sorted_color_count array | |
sorted_color_count.sort(function (a, b) { return b[1] - a[1]; }) | |
new_color = sorted_color_count[0][0].split(','); | |
return new Color(parseInt(new_color[0]), parseInt(new_color[1]), parseInt(new_color[2]), 255); | |
}; | |
}()); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment