in src/engine/loaders/voltools.js [411:519]
static contrastEnchanceUnsharpMask(pixelsSrc, xDim, yDim, zDim, pixelsDst, radSmooth, sigmaSmooth, multiplier, needConsoleLog = false) {
// check input arguments
if (typeof xDim !== 'number' || typeof yDim !== 'number' || typeof zDim !== 'number') {
return VolumeTools.VOLTOOLS_ERROR_BAD_NUMBER;
}
if (typeof pixelsSrc !== 'object') {
return VolumeTools.VOLTOOLS_ERROR_BAD_ARRAY;
}
if (typeof pixelsDst !== 'object') {
return VolumeTools.VOLTOOLS_ERROR_BAD_ARRAY;
}
if (multiplier < 1.0) {
return VolumeTools.VOLTOOLS_ERROR_BAD_MULTIPLIER;
}
const MAX_POSSIBLE_MULTIPLIER = 256.0;
if (multiplier >= MAX_POSSIBLE_MULTIPLIER) {
return VolumeTools.VOLTOOLS_ERROR_BAD_MULTIPLIER;
}
if (xDim <= 1 || yDim <= 1 || zDim <= 1) {
return VolumeTools.VOLTOOLS_ERROR_BAD_DIMENSION;
}
const MAX_POSSIBLE_DIM = 4096;
if (xDim >= MAX_POSSIBLE_DIM || yDim >= MAX_POSSIBLE_DIM || zDim >= MAX_POSSIBLE_DIM) {
return VolumeTools.VOLTOOLS_ERROR_BAD_DIMENSION;
}
const TWO = 2;
const side = TWO * radSmooth + 1;
const side3 = side * side * side;
const xyDim = xDim * yDim;
// allocate gauss convolution 3d matrix
const gaussWeights = new Float32Array(side3);
// flll gauss convolution matrix
const koefSpatial = 1.0 / (TWO * sigmaSmooth * sigmaSmooth);
let weightSum = 0.0;
let off = 0;
for (let z = 0; z < side; z++) {
const dz = z - radSmooth;
for (let y = 0; y < side; y++) {
const dy = y - radSmooth;
for (let x = 0; x < side; x++) {
const dx = x - radSmooth;
const dist2 = 1.0 * dx * dx + dy * dy + dz * dz;
const w = 1.0 / Math.exp(dist2 * koefSpatial);
gaussWeights[off] = w;
weightSum += gaussWeights[off];
off++;
} // for (x)
} // for (y)
} // for (z)
// Normalize gauss matrix
const scale = 1.0 / weightSum;
for (let x = 0; x < side3; x++) {
gaussWeights[x] *= scale;
}
// Process image
for (let zc = 0; zc < zDim; zc++) {
for (let yc = 0; yc < yDim; yc++) {
for (let xc = 0; xc < xDim; xc++) {
let valSmoothed = 0.0;
let offConv = 0;
for (let dz = -radSmooth; dz <= +radSmooth; dz++) {
let z = zc + dz;
z = z < 0 ? 0 : z >= zDim ? zDim - 1 : z;
const zOff = z * xyDim;
for (let dy = -radSmooth; dy <= +radSmooth; dy++) {
let y = yc + dy;
y = y < 0 ? 0 : y >= yDim ? yDim - 1 : y;
const yOff = y * xDim;
for (let dx = -radSmooth; dx <= +radSmooth; dx++) {
let x = xc + dx;
x = x < 0 ? 0 : x >= xDim ? xDim - 1 : x;
const w = gaussWeights[offConv];
offConv++;
const offImage = x + yOff + zOff;
const val = pixelsSrc[offImage];
valSmoothed += w * val;
} // for (dx)
} // for (dy)
} // for (dz)
// now valSmoothed is smoothed pixel intensity at (xc, yc)
off = xc + yc * xDim + zc * xyDim;
let val = pixelsSrc[off];
const valDif = val - valSmoothed;
const MIN_DIF_UNSHARP_VAL = 2.0;
const valAdd = valDif > MIN_DIF_UNSHARP_VAL || valDif < -MIN_DIF_UNSHARP_VAL ? valDif * multiplier : 0.0;
let valSharpened = val + valAdd;
valSharpened = valSharpened < 0.0 ? 0.0 : valSharpened;
val = Math.floor(valSharpened);
const MAX_COLOR = 255;
val = val > MAX_COLOR ? MAX_COLOR : val;
pixelsDst[off] = val;
if (needConsoleLog) {
const MASK_MANY = 16383;
if ((off & MASK_MANY) === 0) {
const HUNDR = 100.0;
const ratioPrc = (off * HUNDR) / (xDim * yDim * zDim);
console.log(`contrastEnchanceUnsharpMask: ${ratioPrc} %`);
}
} // if ned console
} // for (xc)
} // for (yc)
} // for (zc)
return VolumeTools.VOLTOOLS_ERROR_OK;
}