rothko
Version:
what color is your landscape
597 lines (567 loc) • 22.2 kB
JavaScript
/* Circular1D Histogram */
function circularHistogram1D(length, init){
var circularHist1D;
if(length instanceof Array){
circularHist1D = length;
}else {
init = typeof init !== 'undefined' ? init : 0;
circularHist1D = [];
for(var x = 0; x< length; ++x){
circularHist1D[x] = init;
}
}
circularHist1D.circularIndex = circularHistogram1DFunctions.circularIndex;
circularHist1D.max = circularHistogram1DFunctions.max;
circularHist1D.min = circularHistogram1DFunctions.min;
circularHist1D.cv = circularHistogram1DFunctions.cv;
circularHist1D.medianSmoothing = circularHistogram1DFunctions.medianSmoothing;
circularHist1D.gaussianSmoothing = circularHistogram1DFunctions.gaussianSmoothing;
circularHist1D.flatten = circularHistogram1DFunctions.flatten;
circularHist1D.findPeaks = circularHistogram1DFunctions.findPeaks;
circularHist1D.normalize = circularHistogram1DFunctions.normalize;
return circularHist1D;
}
var circularHistogram1DFunctions = {
circularIndex: function(idx, assign){
if( idx >= 0 && idx < this.length){
if(assign){
this[idx] = assign;
}
return this[idx];
}else if(idx < 0){
return this.circularIndex(idx + this.length);
}else{
return this.circularIndex(idx - this.length);
}
},
max: function(cmp){
return Math.max.apply(null, this);
},
min: function(cmp){
return Math.min.apply(null, this);
},
cv: function(kernel){
var _length = this.length;
var resultHist = new circularHistogram1D(_length);
var half_kLength = kernel.length/2;
var kRange = parseInt(half_kLength);
for( var _Idx = 0; _Idx< _length; ++_Idx){
for( var k_Idx = -kRange; k_Idx < half_kLength; ++k_Idx){
var _value;
_value = this.circularIndex(_Idx + k_Idx);
// if( _Idx + k_Idx < 0 ){
// _value = this[0];
// } else if( _Idx + k_Idx > _length -1) {
// _value = this[_length-1];
// } else {
// _value = this[_Idx + k_Idx];
// }
// console.log(resultHist.circularIndex(_Idx))
resultHist.circularIndex(_Idx, (resultHist.circularIndex(_Idx) + _value * kernel[k_Idx + kRange]));
// console.log("first");
}
resultHist.circularIndex(_Idx, Math.round(resultHist.circularIndex(_Idx) * 1000)/1000);
}
return resultHist;
},
medianSmoothing : function(kSize, repeat){
repeat = typeof repeat !== "undefined"? repeat : 1;
var resultHist = this;
var kernel = [];
for( var idx = 0; idx < kSize; ++idx ){
kernel[idx] = 1/kSize;
}
for(var i=0; i< repeat; ++i){
resultHist = resultHist.cv(kernel);
}
return resultHist;
},
gaussianSmoothing : function(kSize, repeat){
repeat = typeof repeat !== "undefined"? repeat : 1;
var resultHist = this;
var kernel;
switch(kSize) {
case 1 :
kernel = [1];
break;
case 3 :
kernel = [1,2,1];
break;
case 5 :
kernel = [1,4,6,4,1];
break;
case 7 :
kernel = [1,6,15,20,15,6,1];
break;
default :
throw new Error("HAHAHA kSize should be 1,3,5, or 7");
}
var kernelSum = kernel.reduce(function(p, c){ return p+c; });
for(var i=0; i< kernel.length; ++i){
kernel[i] = kernel[i]/kernelSum;
}
for(var i=0; i< repeat; ++i){
resultHist = resultHist.cv(kernel);
}
return resultHist;
},
flatten: function(saturate){
var resultHist = new circularHistogram1D(this.length);
saturate = saturate * this.max();
for( var i = 0; i< this.length; ++i){
if( this[i] > saturate ) resultHist[i] = this[i];
}
return resultHist;
},
findPeaks: function(count){
var self = this;
var peaks = [];
var total = 0;
var state = 0; //0 normal // 1 increase // 2 decrease
// var minDataIndex = this.indexOf(this.min()); // min is zero, ordinally.
var min = this.min();
var minDataIndex;
for(var i=0; i< self.length; ++i){
if(this[i] === min){
minDataIndex = i;
break;
}
}
var x;
var peak = {};
for(x = minDataIndex; x< self.length + minDataIndex; ++x){
total+= self.circularIndex(x);
//피크의 시작은 넣되 끝은 넣지않는다.
switch(state){
case 0:
if(self.circularIndex(x) < self.circularIndex(x+1)) {
peak.start = x;
peak.size = self.circularIndex(x);
state = 1;
}
break;
case 1: // was increased
if(self.circularIndex(x) !== self.circularIndex(x+1)){
peak.size += self.circularIndex(x);
if(self.circularIndex(x) > self.circularIndex(x+1)){
peak.x = x;
state = 2;
} else {
}
} else {
state = 0;
peak = {};
}
break;
case 2: // was decreased
if(self.circularIndex(x) > self.circularIndex(x+1)) { //still Decrease
peak.size += self.circularIndex(x);
} else {
peak.end = x;
peaks[peaks.length] = peak;
peak = {}
if(self.circularIndex(x) < self.circularIndex(x+1)) {
peak.start = x;
peak.size = self.circularIndex(x);
state = 1;
}
else {
state = 0;
}
}
break;
default:
throw new Error("findpeak : unknown state");
}
}
// for(var i= 0; i< peaks.length; ++i){
// console.log(peaks[i].x);
// }
if('start' in peak) {
peak.end = x;
if(!('x' in peak)){
peak.x = x-1;
}
peaks[peaks.length] = peak;
}
peaks.sort(function(f,b){ return b.size - f.size });
for(var i = 0; i<peaks.length; ++i){
peaks[i].start = this.normalize(peaks[i].start);
peaks[i].end = this.normalize(peaks[i].end)
peaks[i].x = this.normalize(peaks[i].x)
peaks[i].rate = peaks[i].size/total;
}
return peaks;
function isPeak(x){
return self.circularIndex(x-1) < self.circularIndex(x) &&
self.circularIndex(x) > self.circularIndex(x+1);
}
},
normalize: function(idx){
if( idx < 0 ){
return this.normalize(idx + this.length)
}else if( idx > this.length ){
return this.normalize(idx - this.length);
}else{
return idx;
}
}
}
/* Histogram */
function histogram1D(length, init){
var hist1D;
if(length instanceof Array){
hist1D = length;
}else {
init = typeof init !== 'undefined' ? init : 0;
hist1D = [];
for(var x = 0; x< length; ++x){
hist1D[x] = init;
}
}
hist1D.max = histogram1DFunctions.max;
hist1D.min = histogram1DFunctions.min;
hist1D.cv = histogram1DFunctions.cv;
hist1D.medianSmoothing = histogram1DFunctions.medianSmoothing;
hist1D.gaussianSmoothing = histogram1DFunctions.gaussianSmoothing;
hist1D.flatten = histogram1DFunctions.flatten;
hist1D.findPeaks = histogram1DFunctions.findPeaks;
return hist1D;
}
var histogram1DFunctions = {
max : function(cmp){
return Math.max.apply(null, this);
},
min : function(cmp){
return Math.min.apply(null, this);
},
cv : function(kernel){
var _length = this.length;
var resultHist = new histogram1D(_length);
var half_kLength = kernel.length/2;
var kRange = parseInt(half_kLength);
for( var _Idx = 0; _Idx< _length; ++_Idx){
for( var k_Idx = -kRange; k_Idx < half_kLength; ++k_Idx){
var _value;
if( _Idx + k_Idx < 0 ){
_value = this[0];
} else if( _Idx + k_Idx > _length -1) {
_value = this[_length-1];
} else {
_value = this[_Idx + k_Idx];
}
resultHist[_Idx] += _value * kernel[k_Idx + kRange];
}
resultHist[_Idx] = Math.round(resultHist[_Idx] * 1000)/1000;
}
return resultHist;
},
medianSmoothing : function(kSize, repeat){
repeat = typeof repeat !== "undefined"? repeat : 1;
var resultHist = this;
var kernel = [];
for( var idx = 0; idx < kSize; ++idx ){
kernel[idx] = 1/kSize;
}
for(var i=0; i< repeat; ++i){
resultHist = resultHist.cv(kernel);
}
return resultHist;
},
gaussianSmoothing : function(kSize, repeat){
repeat = typeof repeat !== "undefined"? repeat : 1;
var resultHist = this;
var kernel;
switch(kSize) {
case 1 :
kernel = [1];
break;
case 3 :
kernel = [1,2,1];
break;
case 5 :
kernel = [1,4,6,4,1];
break;
case 7 :
kernel = [1,6,15,20,15,6,1];
break;
default :
throw new Error("HAHAHA kSize should be 1,3,5, or 7");
}
var kernelSum = kernel.reduce(function(p, c){ return p+c; });
for(var i=0; i< kernel.length; ++i){
kernel[i] = kernel[i]/kernelSum;
}
for(var i=0; i< repeat; ++i){
resultHist = resultHist.cv(kernel);
}
return resultHist;
},
flatten : function(saturate){
var resultHist = new histogram1D(this.length);
saturate = saturate * this.max();
for( var i = 0; i< this.length; ++i){
if( this[i] > saturate ) resultHist[i] = this[i];
}
return resultHist;
},
findPeaks : function(count){
var self = this;
var peaks = [];
var total = 0;
var state = 0; //0 normal // 1 increase // 2 decrease
var x;
var peak = {};
for(x = 0; x< self.length ; ++x){
total+= self[x];
//피크의 시작은 넣되 끝은 넣지않는다.
switch(state){
case 0:
if(self[x] < self[x+1]) {
peak.start = x;
peak.size = self[x];
state = 1;
}else if( x === 0 && self[x] > self[x+1] ) { // first and Decrease
peak.start = x;
peak.size = self[x];
peak.x = x;
state = 2;
}
break;
case 1: // was increased
if(self[x] !== self[x+1]){
peak.size += self[x];
if(self[x] > self[x+1]){
peak.x = x;
state = 2;
}
} else {
state = 0;
peak = {};
}
break;
case 2: // was decreased
if(self[x] > self[x+1]) { //still Decrease
peak.size += self[x];
} else {
peak.end = x;
peaks[peaks.length] = peak;
peak = {}
if(self[x] < self[x+1]) {
peak.start = x;
peak.size = self[x];
state = 1;
} else {
state = 0;
}
}
break;
default:
throw new Error("findpeak : unknown state");
}
}
if('start' in peak) {
peak.end = x;
if(!('x' in peak)){
peak.x = x-1;
}
peaks[peaks.length] = peak;
}
//wow. this is peak.
// if(isPeak.call(this,x)){
// var r, l;
// //let's find left and right end.
// var size = this[x];
// for(r = x+1; r < this.length && this[r] > this[r+1] ; ++r){
// size += this[r];
// }
// for(l = x-1; 0 <= l && this[l] > this[l-1] ; --l){
// size += this[l];
// }
// //push to peaks array.
// peaks[peaks.length] = { x : x, size : size, start : l, end :r };
// }
peaks.sort(function(f,b){ return b.size - f.size });
for(var i = 0; i<peaks.length; ++i){
peaks[i].rate = Math.round(peaks[i].size/total * 1000)/1000;
}
return peaks;
function isPeak(x){
return (x-1 < 0 || this[x-1] < this[x]) &&
(x+1 > this.length || this[x] > this[x+1]);
}
}
}
/* 2Dhistogram */
function histogram2D(width, height, init){
init = typeof init !== 'undefined' ? init : 0;
this.width = width;
this.height = height;
for(var x = 0; x < width; ++x){
this[x] = [];
for(var y = 0; y <height; ++y){
this[x][y] = init;
}
}
};
histogram2D.prototype.max = function(cmp){
var max = 0;
for(var i = 0; i < this.width; ++i){
var iMax = Math.max.apply(null, this[i]);
if(max < iMax) max = iMax;
}
return max;
};
histogram2D.prototype.min = function(cmp){
var min = 0;
for(var i = 0; i < this.width; ++i){
var iMin = Math.min.apply(null, this[i]);
if(min < iMin) min = iMin;
}
return min;
};
histogram2D.prototype.loop = function(doing){
for(var x =0; x< this.width; ++x){
for(var y =0; y< this.height; ++y){
doing.call(this,x,y);
}
}
};
histogram2D.prototype.cv = function(mat, saturate){
saturate = typeof saturate !== "undefined" ? saturate : 1;
var resultHist = new histogram2D(this.width, this.height);
var matSize = parseInt(Math.sqrt(mat.length));
var cvRange = parseInt(matSize/2);
for(var x =0; x< this.width; ++x){
for(var y =0; y< this.height; ++y){
if( x >= cvRange && y >= cvRange &&
x < this.width - cvRange && y < this.height - cvRange ){
for(var i = -cvRange; i <= cvRange; ++i ){
for(var j = -cvRange; j<= cvRange; ++j ){
var matIndex = (i+cvRange)*matSize + j + cvRange;
resultHist[x][y] += this[x+i][y+j] * mat[matIndex];
}
}
}
//소수점 6번째 자리까지.
resultHist[x][y] = Math.round(resultHist[x][y]*1000000)/1000000;
}
}
return resultHist;
};
histogram2D.prototype.medianSmoothing = function(repeat){
repeat = typeof repeat !== "undefined"? repeat : 1;
var resultHist = this;
var mat = [1,1,1,1,1,
1,1,1,1,1,
1,1,1,1,1,
1,1,1,1,1,
1,1,1,1,1];
var matSum = mat.reduce(function(p, c){ return p+c; });
for(var i=0; i< mat.length; ++i){
mat[i] = mat[i]/matSum;
}
for(var i=0; i< repeat; ++i){
resultHist = resultHist.cv(mat);
}
return resultHist;
};
histogram2D.prototype.flatten = function(saturate){
var resultHist = new histogram2D(this.width, this.height);
saturate = saturate * this.max();
for( var x = 0; x< this.width; ++x){
for( var y =0; y< this.height; ++y){
if( this[x][y] > saturate ) resultHist[x][y] = this[x][y];
}
}
return resultHist;
};
histogram2D.prototype.binary = function toBinary2DHist(saturate){
var resultHist = new histogram2D(this.width, this.height);
saturate = saturate * this.max();
for( var x = 0; x< this.width; ++x){
for( var y =0; y< this.height; ++y){
if( this[x][y] > saturate ) resultHist[x][y] = 1;
}
}
return resultHist;
};
histogram2D.prototype.findPeaks = function(){
var peaks = [];
var total = 0;
for(var x = 0; x < this.width; ++x){
for(var y =0; y< this.height; ++y){
total += this[x][y];
var r,l,u,d;
if(isPeak.call(this,x,y)){
var size = this[x][y];
for(r = x+1; r < this.width && this[r][y] > this[r+1][y] ; ++r){
for(u = y+1; u < this.height && this[r][u] > this[r][u+1]; ++u){
size += this[r][u];
}
for(d = y-1; 0 <= d && this[r][d] > this[r][d-1]; --d){
size += this[r][d];
}
}
for(l = x-1; 0 <= l && this[l] > this[l-1] ; --l){
for(u = y+1; u < this.height && this[l][u] > this[l][u+1]; ++u){
size += this[l][u];
}
for(d = y-1; 0 <= d && this[l][d] > this[l][d-1]; --d){
size += this[l][d];
}
}
peaks[peaks.length] = {x: x, y: y, height: this[x][y], size: size};
}
}
}
peaks.sort(function(f,b){ return b.size - f.size });
// for(var i = 0; i<peaks.length; ++i){
// peaks[i].rate = peaks[i].size/total;
// }
return peaks;
function isPeak(x,y){
var ul =
(x<=0|| y>=this.height-1) ||
(this[x][y] > this[x-1][y+1]) ? true : false;
var uu =
(y>=this.height-1) ||
(this[x][y] > this[x][y+1])? true : false;
var ur =
(x >= this.width-1 || y>=this.height-1) ||
(this[x][y] > this[x+1][y+1])? true : false;
var ll =
(x<=0) ||
(this[x][y] > this[x-1][y])? true : false;
var rr =
(x >= this.width-1) ||
(this[x][y] > this[x+1][y])? true : false;
var dl =
(x<=0 || y<=0) ||
(this[x][y] > this[x-1][y-1])? true : false;
var dd =
(y<=0) ||
(this[x][y] > this[x][y-1])? true : false;
var dr =
(x >= this.width-1 || y<=0) ||
(this[x][y] > this[x+1][y-1])? true : false;
// if( x <= 0 ){
//
// }
// if( x >= this.length-1 ){
//
// }
// if( y <= 0){
//
// }
// if( y >= this[0].length-1 ){
//
// }
return ul && uu && ur && ll && rr && dl && dd && dr;
}
};
var histogramAnalyze = {
histogram1D : histogram1D,
circularHistogram1D : circularHistogram1D,
histogram2D : histogram2D
}