getHistogram()

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;
  }