116 lines
3.4 KiB
JavaScript
116 lines
3.4 KiB
JavaScript
export class Normalizer {
|
|
constructor() {
|
|
this.settings = {
|
|
none: {},
|
|
min_max: { min: 0.0, max: 1000.0 },
|
|
z_score: {},
|
|
percentile: { low: 1.0, high: 1.0 },
|
|
};
|
|
}
|
|
|
|
process(buffer, size, mode) {
|
|
if (!buffer || size <= 0) return;
|
|
|
|
switch (mode) {
|
|
case 0: this._applyNone(buffer, size); break;
|
|
case 1: this._applyMinMax(buffer, size); break;
|
|
case 2: this._applyZScore(buffer, size); break;
|
|
case 3: this._applyPercentile(buffer, size); break;
|
|
}
|
|
}
|
|
|
|
_applyNone(buffer, size) {
|
|
let vMin = Infinity, vMax = -Infinity;
|
|
for (let i = 0; i < size; i++) {
|
|
if (buffer[i] < vMin) vMin = buffer[i];
|
|
if (buffer[i] > vMax) vMax = buffer[i];
|
|
}
|
|
const range = (vMax - vMin) + 1e-8;
|
|
for (let i = 0; i < size; i++) {
|
|
buffer[i] = (buffer[i] - vMin) / range;
|
|
}
|
|
}
|
|
|
|
_applyMinMax(buffer, size) {
|
|
const s = this.settings.min_max;
|
|
const range = (s.max - s.min) + 1e-8;
|
|
for (let i = 0; i < size; i++) {
|
|
let val = (buffer[i] - s.min) / range;
|
|
buffer[i] = Math.max(0, Math.min(1, val));
|
|
}
|
|
}
|
|
|
|
_applyZScore(buffer, size) {
|
|
let sum = 0;
|
|
for (let i = 0; i < size; i++) sum += buffer[i];
|
|
const mean = sum / size;
|
|
|
|
let sumSqDiff = 0;
|
|
for (let i = 0; i < size; i++) {
|
|
const diff = buffer[i] - mean;
|
|
sumSqDiff += diff * diff;
|
|
}
|
|
const stdDev = Math.sqrt(sumSqDiff / size);
|
|
|
|
if (stdDev < 1e-10) {
|
|
for (let i = 0; i < size; i++) buffer[i] = 0.5;
|
|
return;
|
|
}
|
|
|
|
for (let i = 0; i < size; i++) {
|
|
const z = (buffer[i] - mean) / stdDev;
|
|
let norm = (z + 3) / 6;
|
|
buffer[i] = Math.max(0, Math.min(1, norm));
|
|
}
|
|
}
|
|
|
|
_applyPercentile(buffer, size) {
|
|
const s = this.settings.percentile;
|
|
|
|
let vMin = Infinity, vMax = -Infinity;
|
|
for (let i = 0; i < size; i++) {
|
|
if (buffer[i] < vMin) vMin = buffer[i];
|
|
if (buffer[i] > vMax) vMax = buffer[i];
|
|
}
|
|
|
|
const range = vMax - vMin;
|
|
if (range < 1e-10) return;
|
|
|
|
const binCount = 1024;
|
|
const hist = new Int32Array(binCount);
|
|
for (let i = 0; i < size; i++) {
|
|
const bin = Math.floor(((buffer[i] - vMin) / range) * (binCount - 1));
|
|
hist[bin]++;
|
|
}
|
|
|
|
const lowPixelsToCut = size * (s.low / 100);
|
|
const highPixelsToCut = size * (s.high / 100);
|
|
|
|
let cutMin = vMin;
|
|
let cutMax = vMax;
|
|
|
|
let acc = 0;
|
|
for (let i = 0; i < binCount; i++) {
|
|
acc += hist[i];
|
|
if (acc >= lowPixelsToCut) {
|
|
cutMin = vMin + (i / binCount) * range;
|
|
break;
|
|
}
|
|
}
|
|
|
|
acc = 0;
|
|
for (let i = binCount - 1; i >= 0; i--) {
|
|
acc += hist[i];
|
|
if (acc >= highPixelsToCut) {
|
|
cutMax = vMin + (i / binCount) * range;
|
|
break;
|
|
}
|
|
}
|
|
|
|
const newRange = (cutMax - cutMin) + 1e-10;
|
|
for (let i = 0; i < size; i++) {
|
|
let val = (buffer[i] - cutMin) / newRange;
|
|
buffer[i] = Math.max(0, Math.min(1, val));
|
|
}
|
|
}
|
|
} |