Skip to content

Instantly share code, notes, and snippets.

@loretoparisi
Created October 23, 2015 22:01
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save loretoparisi/c147ca437ab9d5e163b7 to your computer and use it in GitHub Desktop.
Save loretoparisi/c147ca437ab9d5e163b7 to your computer and use it in GitHub Desktop.
Using JavaScript and k-means to find the dominant colors in images
<html>
<head>
<!-- adapted from http://charlesleifer.com/blog/using-python-and-k-means-to-find-the-dominant-colors-in-images/ -->
<script type="text/javascript">
function euclidean(p1, p2) {
var s = 0;
for (var i = 0, l = p1.length; i < l; i++) {
s += Math.pow(p1[i] - p2[i], 2)
}
return Math.sqrt(s);
}
function calculateCenter(points, n) {
var vals = []
, plen = 0;
for (var i = 0; i < n; i++) { vals.push(0); }
for (var i = 0, l = points.length; i < l; i++) {
plen++;
for (var j = 0; j < n; j++) {
vals[j] += points[i][j];
}
}
for (var i = 0; i < n; i++) {
vals[i] = vals[i] / plen;
}
return vals;
}
function kmeans(points, k, min_diff) {
plen = points.length;
clusters = [];
seen = [];
while (clusters.length < k) {
idx = parseInt(Math.random() * plen);
found = false;
for (var i = 0; i < seen.length; i++ ) {
if (idx === seen[i]) {
found = true;
break;
}
}
if (!found) {
seen.push(idx);
clusters.push([points[idx], [points[idx]]]);
}
}
while (true) {
plists = [];
for (var i = 0; i < k; i++) {
plists.push([]);
}
for (var j = 0; j < plen; j++) {
var p = points[j]
, smallest_distance = 10000000
, idx = 0;
for (var i = 0; i < k; i++) {
var distance = euclidean(p, clusters[i][0]);
if (distance < smallest_distance) {
smallest_distance = distance;
idx = i;
}
}
plists[idx].push(p);
}
var diff = 0;
for (var i = 0; i < k; i++) {
var old = clusters[i]
, list = plists[i]
, center = calculateCenter(plists[i], 3)
, new_cluster = [center, (plists[i])]
, dist = euclidean(old[0], center);
clusters[i] = new_cluster
diff = diff > dist ? diff : dist;
}
if (diff < min_diff) {
break;
}
}
return clusters;
}
function rgbToHex(rgb) {
function th(i) {
var h = parseInt(i).toString(16);
return h.length == 1 ? '0'+h : h;
}
return '#' + th(rgb[0]) + th(rgb[1]) + th(rgb[2]);
}
function process_image(img, ctx) {
var points = [];
ctx.drawImage(img, 0, 0, 200, 200);
data = ctx.getImageData(0, 0, 200, 200).data;
for (var i = 0, l = data.length; i < l; i += 4) {
var r = data[i]
, g = data[i+1]
, b = data[i+2];
points.push([r, g, b]);
}
var results = kmeans(points, 3, 1)
, hex = [];
for (var i = 0; i < results.length; i++) {
hex.push(rgbToHex(results[i][0]));
}
return hex;
}
function analyze(img_elem) {
var ctx = document.getElementById('canvas').getContext('2d')
, img = new Image();
img.onload = function() {
var results = document.getElementById('results');
results.innerHTML = 'Waiting...';
var colors = process_image(img, ctx)
, p1 = document.getElementById('c1')
, p2 = document.getElementById('c2')
, p3 = document.getElementById('c3');
p1.style.backgroundColor = colors[0];
p2.style.backgroundColor = colors[1];
p3.style.backgroundColor = colors[2];
results.innerHTML = 'Done';
}
img.src = img_elem.src;
}
</script>
</head>
<body>
<h3>Click an image below to analyze its colors</h3>
<canvas id="canvas" style="display: none;" width="200" height="200"></canvas>
<div>
<p id="results">Results will be displayed here</p>
<span id="c1">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span>
<span id="c2">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span>
<span id="c3">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span>
</div>
<div>
<img onclick="javascript:analyze(this);" src="akira_800x800.jpg" style="width: 250px;" />
<img onclick="javascript:analyze(this);" src="akira-cycle-2_800x800.png" style="width: 250px;" />
<img onclick="javascript:analyze(this);" src="akira-neo-tokyo-6_800x800.png" style="width: 250px;" />
<img onclick="javascript:analyze(this);" src="akira-neo-tokyo-7_800x800.png" style="width: 250px;" />
</div>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment