Skip to content

Instantly share code, notes, and snippets.

@kirbysayshi
Last active August 29, 2015 14:24
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save kirbysayshi/38082126afe2f7f38bbc to your computer and use it in GitHub Desktop.
Save kirbysayshi/38082126afe2f7f38bbc to your computer and use it in GitHub Desktop.
requirebin sketch
var jug = require('image-juggler');
var Clusterer = require('ncolorpalette-clusterer');
var input = document.createElement('input');
input.type = 'file';
var output = document.createElement('canvas');
var outputCtx = output.getContext('2d');
var status = document.createElement('p');
document.body.appendChild(status);
document.body.appendChild(input);
document.body.appendChild(output);
input.addEventListener('change', function(e) {
var file = e.target.files[0];
if (!file) {
throw new Error('No file or invalid file was selected');
}
jug.fileToImage(file, function(err, img) {
document.body.appendChild(img);
jug.imageToCanvas(img, output, function(err, cvs) {
var imageData = outputCtx.getImageData(0, 0, output.width, output.height);
var COLOR_COUNT = 25;
var startEndColors = [
65, 0, 245, 255,
235, 30, 50, 255
]
var palette = generateGradientPalette(COLOR_COUNT, startEndColors);
console.log(palette);
var c = new Clusterer(imageData.data, {
// how many clusters to find
clusters: COLOR_COUNT,
// input data is logically grouped by 4 indices (r,g,b,a)
dataFactor: 4,
// `false` is faster, but will lock the event loop on large images
async: true
});
var start = window.performance.now();
c.solve(
function progress(c, iterationCount) {
if (iterationCount % 5 === 0) status.textContent = 'Iterations: ' + iterationCount;
},
function complete(c, iterationCount) {
var p = document.createElement('p');
p.textContent = 'Finished in ' + iterationCount + ' iterations, ' + (window.performance.now() - start) + 'ms';
console.log(iterationCount)
document.body.insertBefore(p, document.body.firstChild);
// apply in place
c.applyPalette(palette, imageData.data);
// Then do something with it:
outputCtx.putImageData(imageData, 0, 0);
});
});
})
}, false)
function generateGradientPalette(count, startEnd) {
var startR = startEnd[0];
var startG = startEnd[1];
var startB = startEnd[2];
var startA = startEnd[3];
var endR = startEnd[4];
var endG = startEnd[5];
var endB = startEnd[6];
var endA = startEnd[7];
var palette = [];
palette.push(startR, startG, startB, startA);
var interpCount = count - 2;
for (var i = 0; i < interpCount; i++) {
var d = i / interpCount;
var r = Math.floor(startR * d + endR * (1-d));
var g = Math.floor(startG * d + endG * (1-d));
var b = Math.floor(startB * d + endB * (1-d));
var a = Math.floor(startA * d + endA * (1-d));
palette.push(r, g, b, a);
}
palette.push(endR, endG, endB, endA);
return palette;
}
require=function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s}({"image-juggler":[function(require,module,exports){function imageToCanvas(image,opt_cvs,cb){if(!cb){cb=opt_cvs;opt_cvs=null}var cvs=opt_cvs||document.createElement("canvas");var ctx=cvs.getContext("2d");cvs.width=image.width;cvs.height=image.height;ctx.drawImage(image,0,0);cb(null,cvs)}function imageToArrayBuffer(image,cb){var reader=new FileReader;reader.onload=function(){cb(reader.error,reader.result)};imageToCanvas(image,function(err,cvs){cvs.toBlob(reader.readAsArrayBuffer.bind(reader))})}function fileToArrayBuffer(file,cb){var reader=new FileReader;reader.onload=function(){cb(reader.error,reader.result)};reader.readAsArrayBuffer(file)}function imageDataToImage(imageData,opt_image,cb){if(!cb){cb=opt_image;opt_image=null}var img=opt_image||document.createElement("img");var cvs=document.createElement("canvas");var ctx=cvs.getContext("2d");cvs.width=imageData.width;cvs.height=imageData.height;ctx.putImageData(imageData,0,0);canvasToImage(cvs,img,cb)}function fileToImage(file,opt_image,cb){if(!cb){cb=opt_image;opt_image=null}var img=opt_image||document.createElement("img");var url=URL.createObjectURL(file);img.onload=function(){URL.revokeObjectURL(url);cb(null,img)};img.src=url}function canvasToImage(canvas,opt_image,cb){if(!cb){cb=opt_image;opt_image=null}var url;var img=opt_image||document.createElement("img");img.onload=function(){URL.revokeObjectURL(url);cb(null,img)};canvas.toBlob(function(blob){url=URL.createObjectURL(blob);img.src=url})}function blobToImage(blob,opt_image,cb){if(!cb){cb=opt_image;opt_image=null}var img=opt_image||document.createElement("img");var url=URL.createObjectURL(blob);img.onload=function(){URL.revokeObjectURL(url);cb(null,img)};img.src=url}function pixelsToCanvas(pixels,opt_canvas,cb){if(!cb){cb=opt_canvas;opt_canvas=null}var cvs=opt_canvas||document.createElement("canvas");var width=pixels.width||cvs.width;var height=pixels.height||cvs.height;cvs.width=width;cvs.height=height;var ctx=cvs.getContext("2d");var imgdata=ctx.createImageData(width,height);for(var i=0;i<pixels.length;i++){imgdata.data[i]=pixels[i]}ctx.putImageData(imgdata,0,0);cb(null,cvs)}exports.imageToCanvas=imageToCanvas;exports.imageToArrayBuffer=imageToArrayBuffer;exports.imageDataToImage=imageDataToImage;exports.fileToArrayBuffer=fileToArrayBuffer;exports.fileToImage=fileToImage;exports.canvasToImage=canvasToImage;exports.blobToImage=blobToImage},{}]},{},[]);require=function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s}({1:[function(require,module,exports){function AllocatedArray(maxLength,opt_type){this._length=0;this.data=new(opt_type||Uint32Array)(maxLength)}AllocatedArray.prototype.push=function(value){var len=arguments.length;if(len===1){this.data[this._length]=value;return this._length+=1}for(var i=0;i<len;i++){this.data[this._length]=arguments[i];this._length+=1}return this._length};AllocatedArray.prototype.length=function(){return this._length};AllocatedArray.prototype.remove=function(index){var value=this.data[index];this.data[index]=this.data[this._length-1];this._length-=1;return value};AllocatedArray.prototype.get=function(index){return this.data[index]};AllocatedArray.prototype.toArray=function(opt_array){var len=this._length;var arr=opt_array||new Array(len);for(var i=0;i<len;i++){arr[i]=this.data[i]}arr.length=len;return arr};module.exports=AllocatedArray},{}],"ncolorpalette-clusterer":[function(require,module,exports){var ADTA=require("allocated-dynamic-typedarray");function Clusterer(pixels,opts){opts=opts||{};var count=opts.clusters||4;this.dataFactor=opts.dataFactor||4;this.async=opts.async===false?false:true;this.dist=opts.comparator||rgbaDist2;this.pixels=pixels;this.clusters=[];this.means=this.defaultInitialMeans(count);var maxClusterSize=this.pixels.length/this.dataFactor;for(var i=0;i<count;i++){this.clusters.push(new ADTA(maxClusterSize))}for(var i=0;i<pixels.length;i+=this.dataFactor){this.clusterForPixel(i).push(i)}}module.exports=Clusterer;Clusterer.prototype.clusterForPixel=function(dataIdx){var min=Number.MAX_VALUE;var target=-1;for(var i=0;i<this.means.length;i+=4){var dist=this.dist(this.means[i+0],this.means[i+1],this.means[i+2],this.means[i+3],this.pixels[dataIdx+0],this.pixels[dataIdx+1],this.pixels[dataIdx+2],this.pixels[dataIdx+3]);if(dist<min){min=dist;target=i}}return this.clusters[target/this.dataFactor]};Clusterer.prototype.updateMeans=function(){var means=this.means;var clusters=this.clusters;var sourceData=this.pixels;var self=this;clusters.forEach(function(cluster,meanIdx){var r=0,g=0,b=0;for(var i=0;i<cluster.length();i++){var sourceIdx=cluster.get(i);r+=sourceData[sourceIdx+0];g+=sourceData[sourceIdx+1];b+=sourceData[sourceIdx+2]}var meanR=Math.floor(r/cluster.length())||0;var meanG=Math.floor(g/cluster.length())||0;var meanB=Math.floor(b/cluster.length())||0;means[meanIdx*self.dataFactor+0]=meanR;means[meanIdx*self.dataFactor+1]=meanG;means[meanIdx*self.dataFactor+2]=meanB})};Clusterer.prototype.updateClusters=function(){var clusters=this.clusters;var means=this.means;var sourceData=this.pixels;var movementCount=0;for(var i=0;i<clusters.length;i++){var cluster=clusters[i];for(var j=0;j<cluster.length();j++){var didx=cluster.get(j);var targetCluster=this.clusterForPixel(didx);if(targetCluster!==cluster){targetCluster.push(cluster.get(j));cluster.remove(j);movementCount+=1;j--}}}return movementCount};Clusterer.prototype.solve=function(progress,complete){var means=this.updateMeans.bind(this);var clusters=this.updateClusters.bind(this);var self=this;var count=0;if(this.async){(function next(){setTimeout(function(){means();var moved=clusters();count+=1;if(moved>0){progress(self,count);next()}else{complete(self,count)}},0)})()}else{var moved=1;while(moved>0){means();moved=clusters();count+=1;progress(self,count)}complete(self,count)}};Clusterer.prototype.defaultInitialMeans=function(count){var means=[];for(var i=0;i<count;i++){var ratio=i/count;var r=ratio*255;var g=ratio*255;var b=ratio*255;var a=1;means.push(r,g,b,a)}return means};Clusterer.prototype.applyPalette=function(palette,opt_output){var out=opt_output||new Uint8ClampedArray(this.pixels.length);for(var i=0;i<this.clusters.length;i++){var cluster=this.clusters[i];var colorIdx=i%palette.length*this.dataFactor;for(var j=0;j<cluster.length();j++){var p=cluster.get(j);for(var k=0;k<this.dataFactor;k++){out[p+k]=palette[colorIdx+k]}}}return out};function rgbaDist2(r1,g1,b1,a1,r2,g2,b2,a2){var r=r1-r2;var g=g1-g2;var b=b1-b2;var a=a1-a2;return r*r+g*g+b*b+a*a}},{"allocated-dynamic-typedarray":1}]},{},[]);var jug=require("image-juggler");var Clusterer=require("ncolorpalette-clusterer");var input=document.createElement("input");input.type="file";var output=document.createElement("canvas");var outputCtx=output.getContext("2d");var status=document.createElement("p");document.body.appendChild(status);document.body.appendChild(input);document.body.appendChild(output);input.addEventListener("change",function(e){var file=e.target.files[0];if(!file){throw new Error("No file or invalid file was selected")}jug.fileToImage(file,function(err,img){document.body.appendChild(img);jug.imageToCanvas(img,output,function(err,cvs){var imageData=outputCtx.getImageData(0,0,output.width,output.height);var COLOR_COUNT=25;var startEndColors=[65,0,245,255,235,30,50,255];var palette=generateGradientPalette(COLOR_COUNT,startEndColors);console.log(palette);var c=new Clusterer(imageData.data,{clusters:COLOR_COUNT,dataFactor:4,async:true});var start=window.performance.now();c.solve(function progress(c,iterationCount){if(iterationCount%5===0)status.textContent="Iterations: "+iterationCount},function complete(c,iterationCount){var p=document.createElement("p");p.textContent="Finished in "+iterationCount+" iterations, "+(window.performance.now()-start)+"ms";console.log(iterationCount);document.body.insertBefore(p,document.body.firstChild);c.applyPalette(palette,imageData.data);outputCtx.putImageData(imageData,0,0)})})})},false);function generateGradientPalette(count,startEnd){var startR=startEnd[0];var startG=startEnd[1];var startB=startEnd[2];var startA=startEnd[3];var endR=startEnd[4];var endG=startEnd[5];var endB=startEnd[6];var endA=startEnd[7];var palette=[];palette.push(startR,startG,startB,startA);var interpCount=count-2;for(var i=0;i<interpCount;i++){var d=i/interpCount;var r=Math.floor(startR*d+endR*(1-d));var g=Math.floor(startG*d+endG*(1-d));var b=Math.floor(startB*d+endB*(1-d));var a=Math.floor(startA*d+endA*(1-d));palette.push(r,g,b,a)}palette.push(endR,endG,endB,endA);return palette}
{
"name": "requirebin-sketch",
"version": "1.0.0",
"dependencies": {
"image-juggler": "1.0.0",
"ncolorpalette-clusterer": "2.0.1"
}
}
<!-- contents of this file will be placed inside the <body> -->
<!-- contents of this file will be placed inside the <head> -->
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment