in src/engine/actvolume/actvol.js [704:943]
getHistogram() {
const MAX_COLOR = 255;
let i;
// clear histogram
for (i = 0; i < AV_NUM_COLORS; i++) {
this.m_histogram[i] = 0;
}
const TWO = 2;
// scan central image part
const SCAN_RANGE = 4;
const zMin = Math.floor(this.m_zDim / TWO - SCAN_RANGE);
const zMax = Math.floor(this.m_zDim / TWO + SCAN_RANGE);
const yMin = Math.floor(this.m_yDim / TWO - SCAN_RANGE);
const yMax = Math.floor(this.m_yDim / TWO + SCAN_RANGE);
let x, y, z;
let numPixels = 0;
for (z = zMin; z < zMax; z++) {
const zOff = z * this.m_xDim * this.m_yDim;
for (y = yMin; y < yMax; y++) {
const yOff = y * this.m_xDim;
for (x = 0; x < this.m_xDim; x++) {
const off = x + yOff + zOff;
let val = this.m_imageGauss[off];
val = val < MAX_COLOR ? val : MAX_COLOR;
this.m_histogram[Math.floor(val)]++;
numPixels++;
} // for (x)
} // for (y)
} // for (z)
// get probabilities of each color in histogram
for (i = 0; i < AV_NUM_COLORS; i++) {
const h = this.m_histogram[i];
this.m_colorProbability[i] = h / numPixels;
}
// smooth prob
const GAUSS_RAD = 5;
const GAUSS_SIGMA = 1.6;
ActiveVolume.smoothArray(this.m_colorProbability, AV_NUM_COLORS, GAUSS_RAD, GAUSS_SIGMA);
//
// Histogram looks like this
// Brain
//
// +----------------------->
// *** ********
// * ** **** *
// * * * * *
// * * * *
// * *
// *
// *
// *
// *
// Lungs
// +----------------------->
// ***** **********
// * * *
// * * *
// ** * *
// * *
// * *
// * *
// *
//
// Find las local maximum: this is most frequent bright (white) color intensity
let j;
let indBrightColor = -1;
const DIST_DETECT_LOC_MAX = 9;
for (i = AV_NUM_COLORS - DIST_DETECT_LOC_MAX; i > DIST_DETECT_LOC_MAX; i--) {
let isLocMax = 1;
let isLarger = 0;
for (j = i - DIST_DETECT_LOC_MAX; j <= i + DIST_DETECT_LOC_MAX; j++) {
if (this.m_colorProbability[i] > this.m_colorProbability[j]) {
isLarger = 1;
}
if (this.m_colorProbability[i] < this.m_colorProbability[j]) {
isLocMax = 0;
break;
}
} // for (j) around i
if (isLocMax && isLarger) {
indBrightColor = i;
break;
}
}
if (indBrightColor === -1) {
console.log('Bright color cant be detected !');
}
// console.log(`indBrightColor = ${indBrightColor}`);
// Find first local maximum
let indDarkColor = -1;
for (i = 0; i < indBrightColor; i++) {
let isLocMax = true;
let isLarger = false;
const indScanMin = i - DIST_DETECT_LOC_MAX >= 0 ? i - DIST_DETECT_LOC_MAX : 0;
const indScanMax = i + DIST_DETECT_LOC_MAX <= MAX_COLOR ? i + DIST_DETECT_LOC_MAX : MAX_COLOR;
for (j = indScanMin; j <= indScanMax; j++) {
if (this.m_colorProbability[i] > this.m_colorProbability[j]) {
isLarger = true;
}
if (this.m_colorProbability[i] < this.m_colorProbability[j]) {
isLocMax = false;
break;
}
} // for (j) around i
if (isLocMax && isLarger) {
indDarkColor = i;
break;
}
} // for (i) ind dark color
if (indDarkColor === -1) {
console.log('indDarkColor should not be -1');
}
if (indDarkColor >= indBrightColor) {
console.log('indDarkColor should not less then indBrightColor');
}
// console.log(`indDarkColor = ${indDarkColor}`);
// Half of bright color is barrier to detect "black" / "white" change
const indBrightHalf = Math.floor(indBrightColor / TWO);
// clear histogram
for (i = 0; i < AV_NUM_COLORS; i++) {
this.m_histogram[i] = 0;
}
// Get histogram of part image
numPixels = 0;
for (z = zMin; z < zMax; z++) {
const zOff = z * this.m_xDim * this.m_yDim;
for (y = yMin; y < yMax; y++) {
const yOff = y * this.m_xDim;
let isExitFromBrightZoneDetected = false;
let numPixelsDarkZone = 0;
for (x = 0; x < this.m_xDim - 1; x++) {
const off = x + yOff + zOff;
const valCur = Math.floor(this.m_imageGauss[off + 0]);
const valNex = Math.floor(this.m_imageGauss[off + 1]);
const isCurGreat = valCur > indBrightHalf ? 1 : 0;
const isNexLess = valNex <= indBrightHalf ? 1 : 0;
if ((isCurGreat & isNexLess) !== 0) {
isExitFromBrightZoneDetected = true;
continue;
}
if (isExitFromBrightZoneDetected) {
const TOO_MUCH_BRIGHT_ZONE = 40;
if (numPixelsDarkZone > TOO_MUCH_BRIGHT_ZONE) {
break;
}
if (isCurGreat) {
break;
}
numPixelsDarkZone++;
const valOrig = Math.floor(this.m_pixelsSrc[off + 0]);
this.m_histogram[valOrig]++;
numPixels++;
} // if was exit from bright zone
} // for (x)
} // for (y)
} // for (z)
// get probabilities of each color in histogram
for (i = 0; i < AV_NUM_COLORS; i++) {
const h = this.m_histogram[i];
this.m_colorProbability[i] = h / numPixels;
}
// smooth prob
const GAUSS_RAD_FOR_LOC = 8;
const GAUSS_SIGMA_FOR_LOC = 2.4;
ActiveVolume.smoothArray(this.m_colorProbability, AV_NUM_COLORS, GAUSS_RAD_FOR_LOC, GAUSS_SIGMA_FOR_LOC);
// Find first local maximum
indDarkColor = -1;
for (i = 0; i < indBrightColor; i++) {
let isLocMax = true;
let isLarger = false;
const indScanMin = i - DIST_DETECT_LOC_MAX >= 0 ? i - DIST_DETECT_LOC_MAX : 0;
const indScanMax = i + DIST_DETECT_LOC_MAX <= MAX_COLOR ? i + DIST_DETECT_LOC_MAX : MAX_COLOR;
for (j = indScanMin; j <= indScanMax; j++) {
if (this.m_colorProbability[i] > this.m_colorProbability[j]) {
isLarger = true;
}
if (this.m_colorProbability[i] < this.m_colorProbability[j]) {
isLocMax = false;
break;
}
} // for (j) around i
if (isLocMax && isLarger) {
indDarkColor = i;
break;
}
} // for (i) bright color
// console.log(`indDarkColor = ${indDarkColor}`);
// Find next local min
let indDarkColorMax = -1;
for (i = indDarkColor + 1; i < indBrightColor; i++) {
let isLocMin = true;
let isLess = false;
const indScanMin = i - DIST_DETECT_LOC_MAX >= 0 ? i - DIST_DETECT_LOC_MAX : 0;
const indScanMax = i + DIST_DETECT_LOC_MAX <= MAX_COLOR ? i + DIST_DETECT_LOC_MAX : MAX_COLOR;
for (j = indScanMin; j <= indScanMax; j++) {
if (this.m_colorProbability[i] < this.m_colorProbability[j]) {
isLess = 1;
}
if (this.m_colorProbability[i] > this.m_colorProbability[j]) {
isLocMin = false;
break;
}
} // for (j) around i
if (isLocMin && isLess) {
indDarkColorMax = i;
break;
}
}
if (indDarkColorMax <= indDarkColor) {
console.log('indDarkColorMax should be more indDarkColor!');
}
if (indDarkColorMax >= indBrightColor) {
console.log('indDarkColorMax should be less indBrightColor!');
}
console.log(`ActiveVolume. Dark colors range is [${indDarkColor}, ${indDarkColorMax}]`);
// Make smooth step function for range [indDarkColor .. indDarkColorMax]
for (i = 0; i < AV_NUM_COLORS; i++) {
this.m_colorProbability[i] = ActiveVolume.smoothStep(indDarkColor, indDarkColorMax, i);
}
// Debug here
indDarkColorMax++;
return 1;
}