private static void watershedPostProcess(FastBitmap ip) {
   byte[] pixels = ip.getGrayData();
   int size = ip.getWidth() * ip.getHeight();
   for (int i = 0; i < size; i++) {
     if ((pixels[i] & 255) < 255) pixels[i] = (byte) 0;
   }
 }
  @Override
  public void applyInPlace(FastBitmap fastBitmap) {

    if (fastBitmap.isRGB()) {
      FastBitmap layerA = new FastBitmap(fastBitmap);
      FastBitmap layerB = new FastBitmap(fastBitmap);

      SaturationCorrection sc = new SaturationCorrection(saturationCorrection);
      sc.applyInPlace(layerA);

      layerB.setImage(layerA);
      Invert i = new Invert();
      i.applyInPlace(layerB);

      GaussianBlur gb = new GaussianBlur(sigma, size);
      gb.applyInPlace(layerB);

      Blend b = new Blend(layerB, Blend.Algorithm.ColorDodge);
      b.applyInPlace(layerA);

      GammaCorrection gc = new GammaCorrection(gamma);
      gc.applyInPlace(layerA);

      b.setAlgorithm(Blend.Algorithm.Overlay);
      b.setOverlay(layerA);
      b.applyInPlace(fastBitmap);
    }
  }
  public List<FastRetinaKeypoint> ProcessImage(FastBitmap fastBitmap) {

    if (fastBitmap.isGrayscale()) {
      grayImage = new FastBitmap(fastBitmap);
    } else {
      grayImage = new FastBitmap(fastBitmap);
      grayImage.toGrayscale();
    }

    // 1. Extract corners points from the image.
    List<FastRetinaKeypoint> features = new ArrayList<FastRetinaKeypoint>();
    if (Detector != null) {
      List<IntPoint> corners = Detector.ProcessImage(grayImage);

      for (int i = 0; i < corners.size(); i++)
        features.add(new FastRetinaKeypoint(corners.get(i).x, corners.get(i).y));
    } else {
      List<FeaturePoint> corners = FDetector.ProcessImage(grayImage);

      for (int i = 0; i < corners.size(); i++)
        features.add(new FastRetinaKeypoint(corners.get(i).x, corners.get(i).y));
    }

    // 2. Compute the integral for the given image
    integral = IntegralImage.FromFastBitmap(grayImage);

    // 3. Compute feature descriptors if required
    descriptor = null;
    if (featureType != FastRetinaKeypointDescriptorType.None) {
      descriptor = GetDescriptor();
      descriptor.Compute(features);
    }
    return features;
  }
  /**
   * Computes histograms.
   *
   * @param fastBitmap Image.
   */
  private void ProcessImage(FastBitmap fastBitmap) {

    int width = fastBitmap.getWidth();
    int height = fastBitmap.getHeight();

    if (fastBitmap.isGrayscale()) {
      int[] g = new int[width];
      for (int x = 0; x < height; x++) {
        for (int y = 0; y < width; y++) {
          g[y] += fastBitmap.getGray(x, y);
        }
      }
      gray = new Histogram(g);
    }
    if (fastBitmap.isRGB()) {
      int[] r = new int[width];
      int[] g = new int[width];
      int[] b = new int[width];
      for (int x = 0; x < height; x++) {
        for (int y = 0; y < width; y++) {
          r[y] += fastBitmap.getRed(x, y);
          g[y] += fastBitmap.getGreen(x, y);
          b[y] += fastBitmap.getBlue(x, y);
        }
      }
      red = new Histogram(r);
      green = new Histogram(g);
      blue = new Histogram(b);
    }
  }
Example #5
0
  @Override
  public void applyInPlace(FastBitmap fastBitmap) {
    int width = fastBitmap.getWidth();
    int height = fastBitmap.getHeight();
    int noise = (width * height * noiseAmount) / 200;

    if (fastBitmap.isGrayscale()) {
      for (int i = 0; i < noise; i++) {
        int x = random.nextInt(height);
        int y = random.nextInt(width);

        int[] c = new int[] {0, 255};
        int color = random.nextInt(2);

        fastBitmap.setGray(x, y, c[color]);
      }
    } else if (fastBitmap.isRGB()) {
      for (int i = 0; i < noise; i++) {
        int x = random.nextInt(height);
        int y = random.nextInt(width);

        int[] c = new int[] {0, 255};
        int band = random.nextInt(2);
        int color = random.nextInt(2);

        if (band == 0) {
          fastBitmap.setRed(x, y, c[color]);
        } else if (band == 1) {
          fastBitmap.setGreen(x, y, c[color]);
        } else if (band == 2) {
          fastBitmap.setBlue(x, y, c[color]);
        }
      }
    }
  }
  @Override
  public void applyInPlace(FastBitmap fastBitmap) {

    int size = fastBitmap.getSize();

    if (fastBitmap.isGrayscale()) {

      CalculateMap(inGray, outGray, mapGray);

      for (int x = 0; x < size; x++) {
        fastBitmap.setGray(x, mapGray[fastBitmap.getGray(x)]);
      }
    } else {

      CalculateMap(inRed, outRed, mapRed);
      CalculateMap(inGreen, outGreen, mapGreen);
      CalculateMap(inBlue, outBlue, mapBlue);

      for (int x = 0; x < size; x++) {
        int r = mapRed[fastBitmap.getRed(x)];
        int g = mapGreen[fastBitmap.getGreen(x)];
        int b = mapBlue[fastBitmap.getBlue(x)];

        fastBitmap.setRGB(x, r, g, b);
      }
    }
  }
  @Override
  public void applyInPlace(FastBitmap fastBitmap) {
    if (!fastBitmap.isGrayscale())
      throw new IllegalArgumentException(
          "Binary Watershed only works in grayscale (binary) images");

    Watershed(fastBitmap);
  }
  @Override
  public void applyInPlace(FastBitmap fastBitmap) {

    if (fastBitmap.isRGB()) {
      int size = fastBitmap.getWidth() * fastBitmap.getHeight();
      for (int i = 0; i < size; i++) {
        int r = fastBitmap.getRed(i);
        int g = fastBitmap.getGreen(i);
        int b = fastBitmap.getBlue(i);

        float[] color = ColorConverter.RGBtoHLS(r, g, b);
        int[] newColor = ColorConverter.HSLtoRGB(degree, color[1], color[2]);

        newColor[0] = newColor[0] > 255 ? 255 : newColor[0];
        newColor[0] = newColor[0] < 0 ? 0 : newColor[0];

        newColor[1] = newColor[1] > 255 ? 255 : newColor[1];
        newColor[1] = newColor[1] < 0 ? 0 : newColor[1];

        newColor[2] = newColor[2] > 255 ? 255 : newColor[2];
        newColor[2] = newColor[2] < 0 ? 0 : newColor[2];

        fastBitmap.setRGB(i, newColor);
      }
    } else {
      throw new IllegalArgumentException("Hue modifier only works in RGB images.");
    }
  }
  /**
   * @param id desired filter to be applied to a bitmap
   * @param src bitmap source image to be filtered
   * @return src bitmap with filter applied
   */
  public static Bitmap applyFilter(int id, Bitmap src) {
    FastBitmap img = new FastBitmap(src);
    img.toRGB();
    // Interface for generic filter
    IBaseInPlace filter = null;

    switch (id) {
      case FILTER_NONE:
        break;
      case FILTER_SEPIA:
        filter = new Sepia();
        break;
      case FILTER_GRAYSCALE:
        filter = new Grayscale();
        break;
      case FILTER_EMBOSS:
        filter = new Emboss();
        break;
      case FILTER_INVERT:
        filter = new Invert();
        break;
      case FILTER_BLUR:
        filter = new Blur();
        break;
      case FILTER_SHARPEN:
        filter = new Sharpen();
        break;
      case FILTER_MORPH:
        filter = new Morph();
        break;
      case FILTER_BRIGHTNESS:
        filter = new BrightnessCorrection();
        break;
      case FILTER_GAUSSIAN:
        filter = new GaussianBlur();
        break;

      default:
        break;
    }
    if (filter != null) {
      filter.applyInPlace(img);
    }
    return img.toBitmap();
  }
  @Override
  public void applyInPlace(FastBitmap sourceImage) {
    int width = overlayImage.getWidth();
    int height = overlayImage.getHeight();

    int[] rgbO;
    for (int x = 0; x < height; x++) {
      for (int y = 0; y < width; y++) {
        rgbO = overlayImage.getRGB(x, y);

        if (rgbO[0] >= _replaceColor[0]
            && rgbO[1] >= _replaceColor[1]
            && rgbO[2] >= _replaceColor[2]) {
          // x and y is reverted
          sourceImage.setRGB(x + getyOffset(), y + getxOffset(), rgbO);
        }
      }
    }
  }
  @Override
  public void paint(Graphics g) {
    super.paint(g);

    if (_fastBitmap != null) {
      drawImage(g, _fastBitmap.toImage());
    } else {
      drawName(g, "Display");
    }
  }
  private void CalculateNewSize(FastBitmap fastBitmap) {
    // return same size if original image size should be kept
    if (keepSize) {
      this.newWidth = fastBitmap.getWidth();
      this.newHeight = fastBitmap.getHeight();
      return;
    }

    // angle's sine and cosine
    double angleRad = -angle * Math.PI / 180;
    double angleCos = Math.cos(angleRad);
    double angleSin = Math.sin(angleRad);

    // calculate half size
    double halfWidth = (double) fastBitmap.getWidth() / 2;
    double halfHeight = (double) fastBitmap.getHeight() / 2;

    // rotate corners
    double cx1 = halfWidth * angleCos;
    double cy1 = halfWidth * angleSin;

    double cx2 = halfWidth * angleCos - halfHeight * angleSin;
    double cy2 = halfWidth * angleSin + halfHeight * angleCos;

    double cx3 = -halfHeight * angleSin;
    double cy3 = halfHeight * angleCos;

    double cx4 = 0;
    double cy4 = 0;

    // recalculate image size
    halfWidth =
        Math.max(Math.max(cx1, cx2), Math.max(cx3, cx4))
            - Math.min(Math.min(cx1, cx2), Math.min(cx3, cx4));
    halfHeight =
        Math.max(Math.max(cy1, cy2), Math.max(cy3, cy4))
            - Math.min(Math.min(cy1, cy2), Math.min(cy3, cy4));

    this.newWidth = (int) (halfWidth * 2 + 0.5);
    this.newHeight = (int) (halfHeight * 2 + 0.5);
  }
  /**
   * Calculate binarization threshold for the given image.
   *
   * @param fastBitmap FastBitmap.
   * @return Threshold value.
   */
  public int CalculateThreshold(FastBitmap fastBitmap) {

    ImageStatistics stat = new ImageStatistics(fastBitmap);
    Histogram hist = stat.getHistogramGray();

    int[] histogram = hist.getValues();
    int total = fastBitmap.getWidth() * fastBitmap.getHeight();

    double sum = 0;
    for (int i = 0; i < 256; i++) sum += i * histogram[i];

    double sumB = 0;
    int wB = 0;
    int wF = 0;

    double varMax = 0;
    int threshold = 0;

    for (int i = 0; i < 256; i++) {
      wB += histogram[i];
      if (wB == 0) continue;
      wF = total - wB;

      if (wF == 0) break;

      sumB += (double) (i * histogram[i]);
      double mB = sumB / wB;
      double mF = (sum - sumB) / wF;

      double varBetween = (double) wB * (double) wF * (mB - mF) * (mB - mF);

      if (varBetween > varMax) {
        varMax = varBetween;
        threshold = i;
      }
    }

    return threshold;
  }
  private void Watershed(FastBitmap fastBitmap) {

    DistanceTransform dt = new DistanceTransform(distance);
    float[][] distance = dt.Compute(fastBitmap);

    // Convert 2D to 1D - ImageJ Compatibility
    float[] distance1D = new float[distance.length * distance[0].length];
    int p = 0;
    for (int i = 0; i < fastBitmap.getHeight(); i++) {
      for (int j = 0; j < fastBitmap.getWidth(); j++) {
        distance1D[p++] = distance[i][j];
      }
    }

    // Make directions offsets
    makeDirectionOffsets(distance[0].length);

    int width = fastBitmap.getWidth();
    int height = fastBitmap.getHeight();

    FastBitmap back = new FastBitmap(width, height, FastBitmap.ColorSpace.Grayscale);

    // Pegue as maxima
    long[] maxPoints =
        getSortedMaxPoints(distance, distance1D, back, 0, dt.getMaximumDistance(), -808080.0);

    // Analise e marque as maxima em imagem de background
    float maxSortingError = 1.1f * SQRT2 / 2f;
    analyseAndMarkMaxima(distance1D, back, maxPoints, tolerance, maxSortingError);

    // Transform em 8bit 0..255
    FastBitmap outImage = make8Bit(distance, back, dt.getMaximumDistance(), -808080.0);

    cleanupMaxima(outImage, back, maxPoints);
    watershedSegment(outImage);
    watershedPostProcess(outImage);

    fastBitmap.setImage(outImage);
  }
  private int processLevel(
      int pass,
      FastBitmap ip,
      int[] fateTable,
      int levelStart,
      int levelNPoints,
      int[] coordinates,
      int[] setPointList) {
    int width = ip.getWidth();
    int height = ip.getHeight();
    int xmax = width - 1;
    int ymax = height - 1;
    byte[] pixels = ip.getGrayData();

    int nChanged = 0;
    int nUnchanged = 0;
    for (int i = 0, p = levelStart; i < levelNPoints; i++, p++) {
      int xy = coordinates[p];
      int x = xy & intEncodeXMask;
      int y = (xy & intEncodeYMask) >> intEncodeShift;
      int offset = x + y * width;
      int index = 0; // neighborhood pixel ocupation: index in fateTable
      if (y > 0 && (pixels[offset - width] & 255) == 255) index ^= 1;
      if (x < xmax && y > 0 && (pixels[offset - width + 1] & 255) == 255) index ^= 2;
      if (x < xmax && (pixels[offset + 1] & 255) == 255) index ^= 4;
      if (x < xmax && y < ymax && (pixels[offset + width + 1] & 255) == 255) index ^= 8;
      if (y < ymax && (pixels[offset + width] & 255) == 255) index ^= 16;
      if (x > 0 && y < ymax && (pixels[offset + width - 1] & 255) == 255) index ^= 32;
      if (x > 0 && (pixels[offset - 1] & 255) == 255) index ^= 64;
      if (x > 0 && y > 0 && (pixels[offset - width - 1] & 255) == 255) index ^= 128;
      int mask = 1 << pass;
      if ((fateTable[index] & mask) == mask)
        setPointList[nChanged++] = offset; // remember to set pixel to 255
      else coordinates[levelStart + (nUnchanged++)] = xy; // keep this pixel for future passes
    } // for pixel i
    for (int i = 0; i < nChanged; i++) pixels[setPointList[i]] = (byte) 255;
    return nChanged;
  }
  private FastBitmap make8Bit(
      float[][] distance, FastBitmap back, float globalMax, double threshold) {

    int width = distance[0].length;
    int height = distance.length;
    byte[] types = back.getGrayData();
    threshold = 0.5;
    double minValue = 1;

    double offset =
        minValue
            - (globalMax - minValue)
                * (1. / 253 / 2 - 1e-6); // everything above minValue should become >(byte)0
    double factor = 253 / (globalMax - minValue);

    if (factor > 1) factor = 1; // with EDM, no better resolution

    FastBitmap outIp = new FastBitmap(width, height, FastBitmap.ColorSpace.Grayscale);
    // convert possibly calibrated image to byte without damaging threshold (setMinAndMax would kill
    // threshold)
    byte[] pixels = outIp.getGrayData();
    long v;
    for (int y = 0, i = 0; y < height; y++) {
      for (int x = 0; x < width; x++, i++) {
        float rawValue = distance[y][x]; // ip.getPixelValue(x, y);
        if (rawValue < threshold) pixels[i] = (byte) 0;
        else if ((types[i] & (byte) 8) != 0)
          pixels[i] = (byte) 255; // prepare watershed by setting "true" maxima+surroundings to 255
        else {
          v = 1 + Math.round((rawValue - offset) * factor);
          if (v < 1) pixels[i] = (byte) 1;
          else if (v <= 254) pixels[i] = (byte) (v & 255);
          else pixels[i] = (byte) 254;
        }
      }
    }
    return outIp;
  }
  @Override
  public void applyInPlace(FastBitmap fastBitmap) {

    if (fastBitmap.isRGB()) {
      fastBitmap.toGrayscale();
    }

    if (invert) new Invert().applyInPlace(fastBitmap);

    int size = fastBitmap.getWidth() * fastBitmap.getHeight();

    int min = ImageStatistics.Minimum(fastBitmap);
    int max = ImageStatistics.Maximum(fastBitmap);

    fastBitmap.toRGB();
    for (int i = 0; i < size; i++) {
      int[] rgb = GrayscaleToHeatMap(fastBitmap.getRed(i), min, max);
      fastBitmap.setRGB(i, rgb);
    }
  }
Example #18
0
  @Override
  public void applyInPlace(FastBitmap fastBitmap) {

    int width = fastBitmap.getWidth();
    int height = fastBitmap.getHeight();
    FastBitmap copy = new FastBitmap(fastBitmap);
    if (fastBitmap.isGrayscale()) {

      for (int x = 0; x < height; x++) {
        for (int y = 0; y < width; y++) {
          double mean = 0;
          double var = 0;
          int total = 0;
          for (int i = x - radius; i <= x + radius; i++) {
            for (int j = y - radius; j <= y + radius; j++) {
              if (i >= 0 && i < height && j >= 0 && j < width) {
                mean += copy.getGray(i, j);
                total++;
              }
            }
          }
          mean /= total;
          for (int i = x - radius; i <= x + radius; i++) {
            for (int j = y - radius; j <= y + radius; j++) {
              if (i >= 0 && i < height && j >= 0 && j < width)
                var += Math.pow(copy.getGray(i, j) - mean, 2);
            }
          }
          var /= total - 1;
          if (var < 0) var = 0;
          if (var > 255) var = 255;
          fastBitmap.setGray(x, y, (int) var);
        }
      }
    }
    if (fastBitmap.isRGB()) {

      for (int x = 0; x < height; x++) {
        for (int y = 0; y < width; y++) {
          double meanR = 0, meanG = 0, meanB = 0;
          double varR = 0, varG = 0, varB = 0;
          int total = 0;
          for (int i = x - radius; i <= x + radius; i++) {
            for (int j = y - radius; j <= y + radius; j++) {
              if (i >= 0 && i < height && j >= 0 && j < width) {
                meanR += copy.getRed(i, j);
                meanG += copy.getGreen(i, j);
                meanB += copy.getBlue(i, j);
                total++;
              }
            }
          }
          meanR /= total;
          meanG /= total;
          meanB /= total;
          for (int i = x - radius; i <= x + radius; i++) {
            for (int j = y - radius; j <= y + radius; j++) {
              if (i >= 0 && i < height && j >= 0 && j < width) {
                varR += Math.pow(copy.getRed(i, j) - meanR, 2);
                varG += Math.pow(copy.getGreen(i, j) - meanG, 2);
                varB += Math.pow(copy.getBlue(i, j) - meanB, 2);
              }
            }
          }
          varR /= total - 1;
          varG /= total - 1;
          varB /= total - 1;

          if (varR < 0) varR = 0;
          if (varG < 0) varG = 0;
          if (varB < 0) varB = 0;

          if (varR > 255) varR = 255;
          if (varG > 255) varG = 255;
          if (varB > 255) varB = 255;

          fastBitmap.setRGB(x, y, (int) varR, (int) varG, (int) varB);
        }
      }
    }
  }
  @Override
  public void applyInPlace(FastBitmap fastBitmap) {

    if (fastBitmap.isRGB()) {
      int width = fastBitmap.getWidth();
      int height = fastBitmap.getHeight();
      LinkedList<IntPoint> examList = new LinkedList();

      Color old = new Color(fastBitmap.getRGB(startPoint));

      switch (algorithm) {
        case FourWay:
          if (!Color.isEqual(old, replace)) {
            examList.addFirst(new IntPoint(startPoint));

            while (examList.size() > 0) {
              IntPoint p = examList.removeLast();
              old = new Color(fastBitmap.getRGB(p));

              if (!Color.isEqual(old, replace)) {
                int x = p.x;
                int y = p.y;

                fastBitmap.setRGB(x, y, replace);

                if (y - 1 > 0) {
                  examList.addFirst(new IntPoint(x, y - 1)); // check west neighbor
                }
                if (y + 1 < width) {
                  examList.addFirst(new IntPoint(x, y + 1)); // check east neighbor
                }
                if (x + 1 < height) {
                  examList.addFirst(new IntPoint(x + 1, y)); // check south neighbor
                }
                if (x - 1 > 0) {
                  examList.addFirst(new IntPoint(x - 1, y)); // check north neighbor
                }
              }
            }
          }
          break;

        case EightWay:
          if (!Color.isEqual(old, replace)) {
            examList.addFirst(new IntPoint(startPoint));
            while (examList.size() > 0) {
              IntPoint p = examList.removeFirst();

              if (Color.isEqual(old, replace)) {
                int x = p.x;
                int y = p.y;

                fastBitmap.setRGB(x, y, replace);

                if ((x - 1 > 0) && (y - 1 > 0)) {
                  examList.addFirst(new IntPoint(x - 1, y - 1)); // check north-west neighbor
                }
                if (x - 1 > 0) {
                  examList.addFirst(new IntPoint(x - 1, y)); // check north neighbor
                }
                if ((x + 1 < height) && (y + 1 < width)) {
                  examList.addFirst(new IntPoint(x + 1, y + 1)); // check north-east neighbor
                }
                if (y - 1 > 0) {
                  examList.addFirst(new IntPoint(x, y - 1)); // check west neighbor
                }
                if (y + 1 < width) {
                  examList.addFirst(new IntPoint(x, y + 1)); // check east neighbor
                }
                if ((x + 1 < height) && (y - 1 > 0)) {
                  examList.addFirst(new IntPoint(x + 1, y - 1)); // check south-west neighbor
                }
                if (x + 1 < height) {
                  examList.addFirst(new IntPoint(x + 1, y)); // check south neighbor
                }
                if ((x + 1 < height) && (y + 1 < width)) {
                  examList.addFirst(new IntPoint(x + 1, y + 1)); // check south-east neighbor
                }
              }
            }
          }
          break;
      }
    } else if (fastBitmap.isGrayscale()) {
      int width = fastBitmap.getWidth();
      int height = fastBitmap.getHeight();
      LinkedList<IntPoint> examList = new LinkedList();

      int iGray = fastBitmap.getGray(startPoint);

      int _gray = gray;
      int _Gray = _gray;

      switch (algorithm) {
        case FourWay:
          if (iGray != _Gray) {
            examList.addFirst(new IntPoint(startPoint));
            while (examList.size() > 0) {
              IntPoint p = examList.removeLast();
              _gray = fastBitmap.getGray(p.x, p.y);
              _Gray = _gray;

              if (_Gray == iGray) {
                int x = p.x;
                int y = p.y;

                fastBitmap.setGray(x, y, gray);

                if (y - 1 > 0) {
                  examList.addFirst(new IntPoint(x, y - 1)); // check west neighbor
                }
                if (y + 1 < width) {
                  examList.addFirst(new IntPoint(x, y + 1)); // check east neighbor
                }
                if (x + 1 < height) {
                  examList.addFirst(new IntPoint(x + 1, y)); // check south neighbor
                }
                if (x - 1 > 0) {
                  examList.addFirst(new IntPoint(x - 1, y)); // check north neighbor
                }
              }
            }
          }
          break;

        case EightWay:
          if (iGray != _Gray) {
            examList.addFirst(new IntPoint(startPoint));
            while (examList.size() > 0) {
              IntPoint p = examList.removeFirst();
              _gray = fastBitmap.getGray(p.x, p.y);
              _Gray = _gray;

              if (_Gray == iGray) {
                int x = p.x;
                int y = p.y;

                fastBitmap.setGray(x, y, gray);

                if ((x - 1 > 0) && (y - 1 > 0)) {
                  examList.addFirst(new IntPoint(x - 1, y - 1)); // check north-west neighbor
                }
                if (x - 1 > 0) {
                  examList.addFirst(new IntPoint(x - 1, y)); // check north neighbor
                }
                if ((x + 1 < height) && (y + 1 < width)) {
                  examList.addFirst(new IntPoint(x + 1, y + 1)); // check north-east neighbor
                }
                if (y - 1 > 0) {
                  examList.addFirst(new IntPoint(x, y - 1)); // check west neighbor
                }
                if (y + 1 < width) {
                  examList.addFirst(new IntPoint(x, y + 1)); // check east neighbor
                }
                if ((x + 1 < height) && (y - 1 > 0)) {
                  examList.addFirst(new IntPoint(x + 1, y - 1)); // check south-west neighbor
                }
                if (x + 1 < height) {
                  examList.addFirst(new IntPoint(x + 1, y)); // check south neighbor
                }
                if ((x + 1 < height) && (y + 1 < width)) {
                  examList.addFirst(new IntPoint(x + 1, y + 1)); // check south-east neighbor
                }
              }
            }
          }
          break;
      }
    } else {
      throw new IllegalArgumentException("Flood fill only works in RGB and grayscale images.");
    }
  }
  private long[] getSortedMaxPoints(
      float[][] distance,
      float[] distance1D,
      FastBitmap back,
      float globalMin,
      float globalMax,
      double threshold) {

    // Create the back image
    byte[] types = back.getGrayData();

    int nMax = 0;
    for (int y = 0; y < distance.length; y++) {
      for (int x = 0, i = x + y * distance[0].length; x < distance[0].length; x++, i++) {
        float v = distance[y][x];
        float vTrue = trueEdmHeight(x, y, distance1D, distance[0].length, distance.length);
        if (!(v == globalMin)) {
          if (!(x == 0 || x == distance[0].length - 1 || y == 0 || y == distance.length - 1)) {
            if (!(v < threshold)) {
              boolean isMax = true;
              /* check wheter we have a local maximum.
              Note: For an EDM, we need all maxima: those of the EDM-corrected values
              (needed by findMaxima) and those of the raw values (needed by cleanupMaxima) */
              boolean isInner =
                  (y != 0 && y != distance.length - 1)
                      && (x != 0
                          && x
                              != distance[0].length - 1); // not necessary, but faster than isWithin
              for (int d = 0; d < 8; d++) { // compare with the 8 neighbor pixels
                if (isInner || isWithin(x, y, d, distance[0].length, distance.length)) {
                  float vNeighbor = distance[y + DIR_Y_OFFSET[d]][x + DIR_X_OFFSET[d]];
                  float vNeighborTrue =
                      trueEdmHeight(
                          x + DIR_X_OFFSET[d],
                          y + DIR_Y_OFFSET[d],
                          distance1D,
                          distance[0].length,
                          distance.length);
                  if (vNeighbor > v && vNeighborTrue > vTrue) {
                    isMax = false;
                    break;
                  }
                }
              }
              if (isMax) {
                types[i] = (byte) 1;
                nMax++;
              }
            }
          }
        }
      }
    }

    float vFactor =
        (float) (2e9 / (globalMax - globalMin)); // for converting float values into a 32-bit int
    long[] maxPoints =
        new long[nMax]; // value (int) is in the upper 32 bit, pixel offset in the lower
    int iMax = 0;
    for (int y = 0; y < distance.length; y++) // enter all maxima into an array
    for (int x = 0, pp = x + y * distance[0].length; x < distance[0].length; x++, pp++)
        if (types[pp] == (byte) 1) {
          float fValue = trueEdmHeight(x, y, distance1D, distance[0].length, distance.length);
          int iValue =
              (int) ((fValue - globalMin) * vFactor); // 32-bit int, linear function of float value
          maxPoints[iMax++] = (long) iValue << 32 | pp;
        }
    Arrays.sort(maxPoints); // sort the maxima by value
    return maxPoints;
  }
  @Override
  public void applyInPlace(FastBitmap fastBitmap) {

    int width = fastBitmap.getWidth();
    int height = fastBitmap.getHeight();
    int Xline, Yline;
    int radiusI = (weight.length - 1) / 2;
    int radiusJ = (weight[0].length - 1) / 2;
    int maxArray = calcMax(weight);
    int c;

    FastBitmap copy = new FastBitmap(fastBitmap);

    if (fastBitmap.isGrayscale()) {
      int[] avgL = new int[maxArray];
      int median;
      for (int x = 0; x < height; x++) {
        for (int y = 0; y < width; y++) {
          c = 0;
          for (int i = 0; i < weight.length; i++) {
            Xline = x + (i - radiusI);
            for (int j = 0; j < weight[0].length; j++) {
              Yline = y + (j - radiusJ);
              if ((Xline >= 0) && (Xline < height) && (Yline >= 0) && (Yline < width)) {
                if (weight[i][j] > 0) {
                  for (int k = 0; k < weight[i][j]; k++) {
                    avgL[c] = copy.getGray(Xline, Yline);
                    c++;
                  }
                }
              }
            }
          }
          Arrays.sort(avgL, 0, c);
          // median
          median = c / 2;
          fastBitmap.setGray(x, y, avgL[median]);
        }
      }
    } else if (fastBitmap.isRGB()) {
      int[] avgR = new int[maxArray];
      int[] avgG = new int[maxArray];
      int[] avgB = new int[maxArray];
      int median;

      for (int x = 0; x < height; x++) {
        for (int y = 0; y < width; y++) {
          c = 0;
          for (int i = 0; i < weight.length; i++) {
            Xline = x + (i - radiusI);
            for (int j = 0; j < weight[0].length; j++) {
              Yline = y + (j - radiusJ);
              if ((Xline >= 0) && (Xline < height) && (Yline >= 0) && (Yline < width)) {

                if (weight[i][j] > 0) {
                  for (int k = 0; k < weight[i][j]; k++) {
                    avgR[c] = copy.getRed(Xline, Yline);
                    avgG[c] = copy.getGreen(Xline, Yline);
                    avgB[c] = copy.getBlue(Xline, Yline);
                    c++;
                  }
                }
              }
            }
          }
          Arrays.sort(avgR, 0, c);
          Arrays.sort(avgG, 0, c);
          Arrays.sort(avgB, 0, c);
          // median
          median = c / 2;
          fastBitmap.setRGB(x, y, avgR[median], avgG[median], avgB[median]);
        }
      }
    }
  }
  @Override
  public void applyInPlace(FastBitmap fastBitmap) {

    if (fastBitmap.isGrayscale()) {

      int width = fastBitmap.getWidth();
      int height = fastBitmap.getHeight();
      double oldIradius = (double) (height - 1) / 2;
      double oldJradius = (double) (width - 1) / 2;

      CalculateNewSize(fastBitmap);
      FastBitmap destinationData =
          new FastBitmap(newWidth, newHeight, FastBitmap.ColorSpace.Grayscale);

      // get destination image size
      double newIradius = (double) (newHeight - 1) / 2;
      double newJradius = (double) (newWidth - 1) / 2;

      // angle's sine and cosine
      double angleRad = -angle * Math.PI / 180;
      double angleCos = Math.cos(angleRad);
      double angleSin = Math.sin(angleRad);

      // destination pixel's coordinate relative to image center
      double ci, cj;

      // coordinates of source points and cooefficiens
      double oi, oj, di, dj, k1, k2;
      int oi1, oj1, oi2, oj2;

      // width and height decreased by 1
      int imax = height - 1;
      int jmax = width - 1;

      ci = -newIradius;
      for (int i = 0; i < newHeight; i++) {
        cj = -newJradius;
        for (int j = 0; j < newWidth; j++) {

          // coordinate of the nearest point
          oi = angleCos * ci + angleSin * cj + oldIradius;
          oj = -angleSin * ci + angleCos * cj + oldJradius;

          oi1 = (int) oi;
          oj1 = (int) oj;

          // validate source pixel's coordinates
          if ((oi1 < 0) || (oj1 < 0) || (oi1 >= height) || (oj1 >= width)) {
            // fill destination image with filler
            destinationData.setGray(i, j, fillGray);
          } else {

            di = oi - (double) oi1;
            dj = oj - (double) oj1;

            // initial pixel value
            int g = 0;

            for (int n = -1; n < 3; n++) {
              // get Y cooefficient
              k1 = Interpolation.BiCubicKernel(dj - (double) n);

              oj2 = oj1 + n;
              if (oj2 < 0) oj2 = 0;
              if (oj2 > jmax) oj2 = jmax;

              for (int m = -1; m < 3; m++) {
                // get X cooefficient
                k2 = k1 * Interpolation.BiCubicKernel((double) m - di);

                oi2 = oi1 + m;
                if (oi2 < 0) oi2 = 0;
                if (oi2 > imax) oi2 = imax;

                g += k2 * fastBitmap.getGray(oi2, oj2);
              }
            }

            destinationData.setGray(i, j, Math.max(0, Math.min(255, g)));
          }
          cj++;
        }
        ci++;
      }

      fastBitmap.setImage(destinationData);
      destinationData.recycle();

    } else if (fastBitmap.isRGB()) {
      int width = fastBitmap.getWidth();
      int height = fastBitmap.getHeight();
      double oldIradius = (double) (height - 1) / 2;
      double oldJradius = (double) (width - 1) / 2;

      CalculateNewSize(fastBitmap);
      FastBitmap destinationData = new FastBitmap(newWidth, newHeight, FastBitmap.ColorSpace.RGB);

      // get destination image size
      double newIradius = (double) (newHeight - 1) / 2;
      double newJradius = (double) (newWidth - 1) / 2;

      // angle's sine and cosine
      double angleRad = -angle * Math.PI / 180;
      double angleCos = Math.cos(angleRad);
      double angleSin = Math.sin(angleRad);

      // destination pixel's coordinate relative to image center
      double ci, cj;

      // coordinates of source points and cooefficiens
      double oi, oj, di, dj, k1, k2;
      int oi1, oj1, oi2, oj2;

      // width and height decreased by 1
      int imax = height - 1;
      int jmax = width - 1;

      ci = -newIradius;
      for (int i = 0; i < newHeight; i++) {
        cj = -newJradius;
        for (int j = 0; j < newWidth; j++) {

          // coordinate of the nearest point
          oi = angleCos * ci + angleSin * cj + oldIradius;
          oj = -angleSin * ci + angleCos * cj + oldJradius;

          oi1 = (int) oi;
          oj1 = (int) oj;

          // validate source pixel's coordinates
          if ((oi < 0) || (oj < 0) || (oi >= height) || (oj >= width)) {
            // fill destination image with filler
            destinationData.setRGB(i, j, fillRed, fillGreen, fillBlue);
          } else {

            di = oi - (double) oi1;
            dj = oj - (double) oj1;

            // initial pixel value
            int r = 0;
            int g = 0;
            int b = 0;

            for (int n = -1; n < 3; n++) {
              // get Y cooefficient
              k1 = Interpolation.BiCubicKernel(dj - (double) n);

              oj2 = oj1 + n;
              if (oj2 < 0) oj2 = 0;
              if (oj2 > jmax) oj2 = jmax;

              for (int m = -1; m < 3; m++) {
                // get X cooefficient
                k2 = k1 * Interpolation.BiCubicKernel((double) m - di);

                oi2 = oi1 + m;
                if (oi2 < 0) oi2 = 0;
                if (oi2 > imax) oi2 = imax;

                r += k2 * fastBitmap.getRed(oi2, oj2);
                g += k2 * fastBitmap.getGreen(oi2, oj2);
                b += k2 * fastBitmap.getBlue(oi2, oj2);
              }
            }

            r = Math.max(0, Math.min(255, r));
            g = Math.max(0, Math.min(255, g));
            b = Math.max(0, Math.min(255, b));
            destinationData.setRGB(i, j, r, g, b);
          }
          cj++;
        }
        ci++;
      }

      fastBitmap.setImage(destinationData);
      destinationData.recycle();
    }
  }
  private void analyseAndMarkMaxima(
      float[] edmPixels,
      FastBitmap back,
      long[] maxPoints,
      float tolerance,
      float maxSortingError) {
    int width = back.getWidth();
    int height = back.getHeight();
    byte[] types = (byte[]) back.getGrayData();
    int nMax = maxPoints.length;
    int[] pList = new int[width * height]; // here we enter points starting from a maximum

    for (int iMax = nMax - 1;
        iMax >= 0;
        iMax--) { // process all maxima now, starting from the highest
      int offset0 =
          (int) maxPoints[iMax]; // type cast gets 32 lower bits, where pixel index is encoded
      // int offset0 = maxPoints[iMax].offset;
      if ((types[offset0] & (byte) 4)
          != 0) // this maximum has been reached from another one, skip it
      continue;
      // we create a list of connected points and start the list at the current maximum
      int x0 = offset0 % width;
      int y0 = offset0 / width;
      float v0 = trueEdmHeight(x0, y0, edmPixels, width, height);
      boolean sortingError;
      do { // repeat if we have encountered a sortingError
        pList[0] = offset0;
        types[offset0] |=
            ((byte) 16 | (byte) 2); // mark first point as equal height (to itself) and listed
        int listLen = 1; // number of elements in the list
        int listI = 0; // index of current element in the list
        sortingError = false; // if sorting was inaccurate: a higher maximum was not handled so far
        boolean maxPossible = true; // it may be a true maximum
        double xEqual = x0; // for creating a single point: determine average over the
        double yEqual = y0; //  coordinates of contiguous equal-height points
        int nEqual = 1; // counts xEqual/yEqual points that we use for averaging
        do { // while neigbor list is not fully processed (to listLen)
          int offset = pList[listI];
          int x = offset % width;
          int y = offset / width;

          boolean isInner =
              (y != 0 && y != height - 1)
                  && (x != 0 && x != width - 1); // not necessary, but faster than isWithin
          for (int d = 0; d < 8; d++) { // analyze all neighbors (in 8 directions) at the same level
            int offset2 = offset + dirOffset[d];
            if ((isInner || isWithin(x, y, d, width, height)) && (types[offset2] & (byte) 2) == 0) {
              if (edmPixels[offset2] <= 0) continue; // ignore the background (non-particles)
              if ((types[offset2] & (byte) 4) != 0) {
                maxPossible =
                    false; // we have reached a point processed previously, thus it is no maximum
                // now

                break;
              }
              int x2 = x + DIR_X_OFFSET[d];
              int y2 = y + DIR_Y_OFFSET[d];
              float v2 = trueEdmHeight(x2, y2, edmPixels, width, height);
              if (v2 > v0 + maxSortingError) {
                maxPossible = false; // we have reached a higher point, thus it is no maximum
                // if(x0<25&&y0<20)IJ.write("x0,y0="+x0+","+y0+":stop at higher neighbor from
                // x,y="+x+","+y+", dir="+d+",value,value2,v2-v="+v0+","+v2+","+(v2-v0));
                break;
              } else if (v2 >= v0 - (float) tolerance) {
                if (v2 > v0) { // maybe this point should have been treated earlier
                  sortingError = true;
                  offset0 = offset2;
                  v0 = v2;
                  x0 = x2;
                  y0 = y2;
                }
                pList[listLen] = offset2;
                listLen++; // we have found a new point within the tolerance
                types[offset2] |= (byte) 2;
                if (v2
                    == v0) { // prepare finding center of equal points (in case single point needed)
                  types[offset2] |= (byte) 16;
                  xEqual += x2;
                  yEqual += y2;
                  nEqual++;
                }
              }
            } // if isWithin & not (byte)2
          } // for directions d
          listI++;
        } while (listI < listLen);

        if (sortingError) { // if x0,y0 was not the true maximum but we have reached a higher one
          for (listI = 0; listI < listLen; listI++)
            types[pList[listI]] = 0; // reset all points encountered, then retry
        } else {
          int resetMask = ~(maxPossible ? (byte) 2 : ((byte) 2 | (byte) 16));
          xEqual /= nEqual;
          yEqual /= nEqual;
          double minDist2 = 1e20;
          int nearestI = 0;
          for (listI = 0; listI < listLen; listI++) {
            int offset = pList[listI];
            int x = offset % width;
            int y = offset / width;
            types[offset] &= resetMask; // reset attributes no longer needed
            types[offset] |= (byte) 4; // mark as processed
            if (maxPossible) {
              types[offset] |= (byte) 8;
              if ((types[offset] & (byte) 16) != 0) {
                double dist2 =
                    (xEqual - x) * (double) (xEqual - x) + (yEqual - y) * (double) (yEqual - y);
                if (dist2 < minDist2) {
                  minDist2 = dist2; // this could be the best "single maximum" point
                  nearestI = listI;
                }
              }
            }
          } // for listI
          if (maxPossible) {
            int offset = pList[nearestI];
            types[offset] |= (byte) 32;
          }
        } // if !sortingError
      } while (sortingError); // redo if we have encountered a higher maximum: handle it now.
    } // for all maxima iMax
  }
  private void cleanupMaxima(FastBitmap outIp, FastBitmap typeP, long[] maxPoints) {
    int width = outIp.getWidth();
    int height = outIp.getHeight();
    byte[] pixels = outIp.getGrayData();
    byte[] types = typeP.getGrayData();
    int nMax = maxPoints.length;
    int[] pList = new int[width * height];
    for (int iMax = nMax - 1; iMax >= 0; iMax--) {
      int offset0 =
          (int) maxPoints[iMax]; // type cast gets lower 32 bits where pixel offset is encoded
      if ((types[offset0] & ((byte) 8 | (byte) 64)) != 0) continue;
      int level = pixels[offset0] & 255;
      int loLevel = level + 1;
      pList[0] = offset0; // we start the list at the current maximum

      types[offset0] |= (byte) 2; // mark first point as listed
      int listLen = 1; // number of elements in the list
      int lastLen = 1;
      int listI = 0; // index of current element in the list
      boolean saddleFound = false;
      while (!saddleFound && loLevel > 0) {
        loLevel--;
        lastLen = listLen; // remember end of list for previous level
        listI = 0; // in each level, start analyzing the neighbors of all pixels
        do { // for all pixels listed so far
          int offset = pList[listI];
          int x = offset % width;
          int y = offset / width;
          boolean isInner =
              (y != 0 && y != height - 1)
                  && (x != 0 && x != width - 1); // not necessary, but faster than isWithin
          for (int d = 0; d < 8; d++) { // analyze all neighbors (in 8 directions) at the same level
            int offset2 = offset + dirOffset[d];
            if ((isInner || isWithin(x, y, d, width, height)) && (types[offset2] & (byte) 2) == 0) {
              if ((types[offset2] & (byte) 8) != 0
                  || (((types[offset2] & (byte) 64) != 0) && (pixels[offset2] & 255) >= loLevel)) {
                saddleFound = true; // we have reached a point touching a "true" maximum...
                // if (xList[0]==122) IJ.write("saddle found at level="+loLevel+";
                // x,y="+xList[listI]+","+yList[listI]+", dir="+d);
                break; // ...or a level not lower, but touching a "true" maximum
              } else if ((pixels[offset2] & 255) >= loLevel && (types[offset2] & (byte) 64) == 0) {
                pList[listLen] = offset2;
                // xList[listLen] = x+DIR_X_OFFSET[d];
                // yList[listLen] = x+DIR_Y_OFFSET[d];
                listLen++; // we have found a new point to be processed
                types[offset2] |= (byte) 2;
              }
            } // if isWithin & not (byte)2
          } // for directions d
          if (saddleFound) break; // no reason to search any further
          listI++;
        } while (listI < listLen);
      } // while !levelFound && loLevel>=0
      for (listI = 0;
          listI < listLen;
          listI++) // reset attribute since we may come to this place again
      types[pList[listI]] &= ~(byte) 2;
      for (listI = 0;
          listI < lastLen;
          listI++) { // for all points higher than the level of the saddle point
        int offset = pList[listI];
        pixels[offset] = (byte) loLevel; // set pixel value to the level of the saddle point
        types[offset] |=
            (byte) 64; // mark as processed: there can't be a local maximum in this area
      }
    } // for all maxima iMax
  }
  @Override
  public void applyInPlace(FastBitmap fastBitmap) {

    int width = fastBitmap.getWidth();
    int height = fastBitmap.getHeight();
    int Xline, Yline;
    int lines = CalcLines(radius);
    int maxArray = lines * lines;
    int c;

    FastBitmap copy = new FastBitmap(fastBitmap);

    if (fastBitmap.isGrayscale()) {
      int[] avgL = new int[maxArray];
      for (int x = 0; x < height; x++) {
        for (int y = 0; y < width; y++) {
          c = 0;
          for (int i = 0; i < lines; i++) {
            Xline = x + (i - radius);
            for (int j = 0; j < lines; j++) {
              Yline = y + (j - radius);
              if ((Xline >= 0) && (Xline < height) && (Yline >= 0) && (Yline < width)) {
                avgL[c] = copy.getGray(Xline, Yline);
              } else {
                avgL[c] = copy.getGray(x, y);
              }
              c++;
            }
          }

          Arrays.sort(avgL);

          // alpha trimmed mean
          double mean = 0;
          for (int i = t; i < c - t; i++) {
            mean += avgL[i];
          }

          fastBitmap.setGray(x, y, (int) (mean / (avgL.length - 2 * t)));
        }
      }
    } else if (fastBitmap.isRGB()) {
      int[] avgR = new int[maxArray];
      int[] avgG = new int[maxArray];
      int[] avgB = new int[maxArray];

      for (int x = 0; x < height; x++) {
        for (int y = 0; y < width; y++) {
          c = 0;
          for (int i = 0; i < lines; i++) {
            Xline = x + (i - radius);
            for (int j = 0; j < lines; j++) {
              Yline = y + (j - radius);
              if ((Xline >= 0) && (Xline < height) && (Yline >= 0) && (Yline < width)) {
                avgR[c] = copy.getRed(Xline, Yline);
                avgG[c] = copy.getGreen(Xline, Yline);
                avgB[c] = copy.getBlue(Xline, Yline);
              } else {
                avgR[c] = copy.getRed(x, y);
                avgG[c] = copy.getGreen(x, y);
                avgB[c] = copy.getBlue(x, y);
              }
              c++;
            }
          }

          Arrays.sort(avgR);
          Arrays.sort(avgG);
          Arrays.sort(avgB);

          // alpha trimmed mean
          double meanR = 0, meanG = 0, meanB = 0;
          for (int i = t; i < c - t; i++) {
            meanR += avgR[i];
            meanG += avgG[i];
            meanB += avgB[i];
          }

          meanR /= (avgR.length - 2 * t);
          meanG /= (avgG.length - 2 * t);
          meanB /= (avgB.length - 2 * t);

          fastBitmap.setRGB(x, y, (int) meanR, (int) meanG, (int) meanB);
        }
      }
    }
  }
Example #26
0
 @Override
 public FastBitmap processFilter(FastBitmap value) {
   value.saveAsPNG(_path);
   //  System.out.println("Picture was saved to: " + _path);
   return value;
 }
  private boolean watershedSegment(FastBitmap ip) {
    int width = ip.getWidth();
    int height = ip.getHeight();
    byte[] pixels = ip.getGrayData();
    // Create an array with the coordinates of all points between value 1 and 254
    // This method, suggested by Stein Roervik (stein_at_kjemi-dot-unit-dot-no),
    // greatly speeds up the watershed segmentation routine.

    ImageStatistics is = new ImageStatistics(ip);

    int[] histogram = is.getHistogramGray().getValues();
    int arraySize = width * height - histogram[0] - histogram[255];
    int[] coordinates = new int[arraySize]; // from pixel coordinates, low bits x, high bits y
    int highestValue = 0;
    int maxBinSize = 0;
    int offset = 0;
    int[] levelStart = new int[256];
    for (int v = 1; v < 255; v++) {
      levelStart[v] = offset;
      offset += histogram[v];
      if (histogram[v] > 0) highestValue = v;
      if (histogram[v] > maxBinSize) maxBinSize = histogram[v];
    }
    int[] levelOffset = new int[highestValue + 1];
    for (int y = 0, i = 0; y < height; y++) {
      for (int x = 0; x < width; x++, i++) {
        int v = pixels[i] & 255;
        if (v > 0 && v < 255) {
          offset = levelStart[v] + levelOffset[v];
          coordinates[offset] = x | y << intEncodeShift;
          levelOffset[v]++;
        }
      } // for x
    } // for y
    // Create an array of the points (pixel offsets) that we set to 255 in one pass.
    // If we remember this list we need not create a snapshot of the ImageProcessor.
    int[] setPointList = new int[Math.min(maxBinSize, (width * height + 2) / 3)];
    // now do the segmentation, starting at the highest level and working down.
    // At each level, dilate the particle (set pixels to 255), constrained to pixels
    // whose values are at that level and also constrained (by the fateTable)
    // to prevent features from merging.
    int[] table = makeFateTable();
    final int[] directionSequence = new int[] {7, 3, 1, 5, 0, 4, 2, 6}; // diagonal directions first
    for (int level = highestValue; level >= 1; level--) {
      int remaining =
          histogram[level]; // number of points in the level that have not been processed
      int idle = 0;
      while (remaining > 0 && idle < 8) {
        int dIndex = 0;
        do { // expand each level in 8 directions
          int n =
              processLevel(
                  directionSequence[dIndex % 8],
                  ip,
                  table,
                  levelStart[level],
                  remaining,
                  coordinates,
                  setPointList);
          // IJ.log("level="+level+" direction="+directionSequence[dIndex%8]+"
          // remain="+remaining+"-"+n);
          remaining -= n; // number of points processed
          if (n > 0) idle = 0; // nothing processed in this direction?
          dIndex++;
        } while (remaining > 0 && idle++ < 8);
      }
      if (remaining > 0 && level > 1) { // any pixels that we have not reached?
        int nextLevel = level; // find the next level to process
        do nextLevel--;
        while (nextLevel > 1 && histogram[nextLevel] == 0);
        // in principle we should add all unprocessed pixels of this level to the
        // tasklist of the next level. This would make it very slow for some images,
        // however. Thus we only add the pixels if they are at the border (of the
        // image or a thresholded area) and correct unprocessed pixels at the very
        // end by CleanupExtraLines
        if (nextLevel > 0) {
          int newNextLevelEnd = levelStart[nextLevel] + histogram[nextLevel];
          for (int i = 0, p = levelStart[level]; i < remaining; i++, p++) {
            int xy = coordinates[p];
            int x = xy & intEncodeXMask;
            int y = (xy & intEncodeYMask) >> intEncodeShift;
            int pOffset = x + y * width;
            boolean addToNext = false;
            if (x == 0 || y == 0 || x == width - 1 || y == height - 1)
              addToNext = true; // image border
            else
              for (int d = 0; d < 8; d++)
                if (isWithin(x, y, d, width, height) && pixels[pOffset + dirOffset[d]] == 0) {
                  addToNext = true; // border of area below threshold
                  break;
                }
            if (addToNext) coordinates[newNextLevelEnd++] = xy;
          }
          // tasklist for the next level to process becomes longer by this:
          histogram[nextLevel] = newNextLevelEnd - levelStart[nextLevel];
        }
      }
    }
    return true;
  }
Example #28
0
  @Override
  public void applyInPlace(FastBitmap fastBitmap) {

    if (fastBitmap.isRGB() && overlay.isRGB()) {

      int w = fastBitmap.getWidth();
      int h = fastBitmap.getHeight();

      switch (algorithm) {
        case Lighten:
          for (int i = 0; i < h; i++) {
            for (int j = 0; j < w; j++) {
              if (overlay.getRed(i, j) > fastBitmap.getRed(i, j)) {
                fastBitmap.setRed(i, j, overlay.getRed(i, j));
              }
              if (overlay.getGreen(i, j) > fastBitmap.getGreen(i, j)) {
                fastBitmap.setGreen(i, j, overlay.getGreen(i, j));
              }
              if (overlay.getBlue(i, j) > fastBitmap.getBlue(i, j)) {
                fastBitmap.setBlue(i, j, overlay.getBlue(i, j));
              }
            }
          }
          break;
        case Darken:
          for (int i = 0; i < h; i++) {
            for (int j = 0; j < w; j++) {
              if (overlay.getRed(i, j) < fastBitmap.getRed(i, j)) {
                fastBitmap.setRed(i, j, overlay.getRed(i, j));
              }
              if (overlay.getGreen(i, j) < fastBitmap.getGreen(i, j)) {
                fastBitmap.setGreen(i, j, overlay.getGreen(i, j));
              }
              if (overlay.getBlue(i, j) < fastBitmap.getBlue(i, j)) {
                fastBitmap.setBlue(i, j, overlay.getBlue(i, j));
              }
            }
          }
          break;
        case Multiply:
          for (int i = 0; i < h; i++) {
            for (int j = 0; j < w; j++) {
              int r = fastBitmap.getRed(i, j) * overlay.getRed(i, j) / 255;
              int g = fastBitmap.getGreen(i, j) * overlay.getGreen(i, j) / 255;
              int b = fastBitmap.getBlue(i, j) * overlay.getBlue(i, j) / 255;
              fastBitmap.setRGB(i, j, r, g, b);
            }
          }
          break;
        case Average:
          for (int i = 0; i < h; i++) {
            for (int j = 0; j < w; j++) {
              int r = fastBitmap.getRed(i, j) + overlay.getRed(i, j) / 2;
              int g = fastBitmap.getGreen(i, j) + overlay.getGreen(i, j) / 2;
              int b = fastBitmap.getBlue(i, j) + overlay.getBlue(i, j) / 2;
              fastBitmap.setRGB(i, j, r, g, b);
            }
          }
          break;
        case Add:
          for (int i = 0; i < h; i++) {
            for (int j = 0; j < w; j++) {
              int r = Math.min(fastBitmap.getRed(i, j) + overlay.getRed(i, j), 255);
              int g = Math.min(fastBitmap.getGreen(i, j) + overlay.getGreen(i, j), 255);
              int b = Math.min(fastBitmap.getBlue(i, j) + overlay.getBlue(i, j), 255);
              fastBitmap.setRGB(i, j, r, g, b);
            }
          }
          break;
        case Subtract:
          for (int i = 0; i < h; i++) {
            for (int j = 0; j < w; j++) {
              int temp = fastBitmap.getRed(i, j) + overlay.getRed(i, j);
              if (temp < 255) {
                fastBitmap.setRed(i, j, 0);
              } else {
                fastBitmap.setRed(i, j, temp - 255);
              }

              temp = fastBitmap.getGreen(i, j) + overlay.getGreen(i, j);
              if (temp < 255) {
                fastBitmap.setGreen(i, j, 0);
              } else {
                fastBitmap.setGreen(i, j, temp - 255);
              }

              temp = fastBitmap.getBlue(i, j) + overlay.getBlue(i, j);
              if (temp < 255) {
                fastBitmap.setBlue(i, j, 0);
              } else {
                fastBitmap.setBlue(i, j, temp - 255);
              }
            }
          }
          break;
        case Difference:
          for (int i = 0; i < h; i++) {
            for (int j = 0; j < w; j++) {
              int r = Math.abs(fastBitmap.getRed(i, j) - overlay.getRed(i, j));
              int g = Math.abs(fastBitmap.getGreen(i, j) - overlay.getGreen(i, j));
              int b = Math.abs(fastBitmap.getBlue(i, j) - overlay.getBlue(i, j));
              fastBitmap.setRGB(i, j, r, g, b);
            }
          }
          break;
        case Negation:
          for (int i = 0; i < h; i++) {
            for (int j = 0; j < w; j++) {
              int r = 255 - Math.abs(255 - fastBitmap.getRed(i, j) - overlay.getRed(i, j));
              int g = 255 - Math.abs(255 - fastBitmap.getGreen(i, j) - overlay.getGreen(i, j));
              int b = 255 - Math.abs(255 - fastBitmap.getBlue(i, j) - overlay.getBlue(i, j));
              fastBitmap.setRGB(i, j, r, g, b);
            }
          }
          break;
        case Screen:
          for (int i = 0; i < h; i++) {
            for (int j = 0; j < w; j++) {
              int r =
                  ((255 - (((255 - fastBitmap.getRed(i, j)) * (255 - overlay.getRed(i, j))) >> 8)));
              int g =
                  ((255
                      - (((255 - fastBitmap.getGreen(i, j)) * (255 - overlay.getGreen(i, j)))
                          >> 8)));
              int b =
                  ((255
                      - (((255 - fastBitmap.getBlue(i, j)) * (255 - overlay.getBlue(i, j))) >> 8)));
              fastBitmap.setRGB(i, j, r, g, b);
            }
          }
          break;
        case Exclusion:
          for (int i = 0; i < h; i++) {
            for (int j = 0; j < w; j++) {
              int r =
                  ((fastBitmap.getRed(i, j)
                      + overlay.getRed(i, j)
                      - 2 * fastBitmap.getRed(i, j) * overlay.getRed(i, j) / 255));
              int g =
                  ((fastBitmap.getGreen(i, j)
                      + overlay.getGreen(i, j)
                      - 2 * fastBitmap.getGreen(i, j) * overlay.getGreen(i, j) / 255));
              int b =
                  ((fastBitmap.getBlue(i, j)
                      + overlay.getBlue(i, j)
                      - 2 * fastBitmap.getBlue(i, j) * overlay.getBlue(i, j) / 255));
              fastBitmap.setRGB(i, j, r, g, b);
            }
          }
          break;
        case Overlay:
          for (int i = 0; i < h; i++) {
            for (int j = 0; j < w; j++) {

              int temp;
              if (overlay.getRed(i, j) < 128) {
                temp = (2 * fastBitmap.getRed(i, j) * overlay.getRed(i, j) / 255);
                temp = Math.min(255, temp);
                fastBitmap.setRed(i, j, temp);
              } else {
                temp =
                    (255
                        - 2 * (255 - fastBitmap.getRed(i, j)) * (255 - overlay.getRed(i, j)) / 255);
                temp = Math.min(255, temp);
                fastBitmap.setRed(i, j, temp);
              }

              if (overlay.getGreen(i, j) < 128) {
                temp = (2 * fastBitmap.getGreen(i, j) * overlay.getGreen(i, j) / 255);
                temp = Math.min(255, temp);
                fastBitmap.setGreen(i, j, temp);
              } else {
                temp =
                    (255
                        - 2
                            * (255 - fastBitmap.getGreen(i, j))
                            * (255 - overlay.getGreen(i, j))
                            / 255);
                temp = Math.min(255, temp);
                fastBitmap.setGreen(i, j, temp);
              }

              if (overlay.getBlue(i, j) < 128) {
                temp = (2 * fastBitmap.getBlue(i, j) * overlay.getBlue(i, j) / 255);
                temp = Math.min(255, temp);
                fastBitmap.setBlue(i, j, temp);
              } else {
                temp =
                    (255
                        - 2
                            * (255 - fastBitmap.getBlue(i, j))
                            * (255 - overlay.getBlue(i, j))
                            / 255);
                temp = Math.min(255, temp);
                fastBitmap.setBlue(i, j, temp);
              }
            }
          }
          break;
        case SoftLight:
          for (int i = 0; i < h; i++) {
            for (int j = 0; j < w; j++) {

              int temp;
              if (fastBitmap.getRed(i, j) < 128) {
                temp = (2 * overlay.getRed(i, j) * fastBitmap.getRed(i, j) / 255);
                temp = Math.min(255, temp);
                fastBitmap.setRed(i, j, temp);
              } else {
                temp =
                    (255
                        - 2 * (255 - overlay.getRed(i, j)) * (255 - fastBitmap.getRed(i, j)) / 255);
                temp = Math.min(255, temp);
                overlay.setRed(i, j, temp);
              }

              if (fastBitmap.getGreen(i, j) < 128) {
                temp = (2 * overlay.getGreen(i, j) * fastBitmap.getGreen(i, j) / 255);
                temp = Math.min(255, temp);
                fastBitmap.setGreen(i, j, temp);
              } else {
                temp =
                    (255
                        - 2
                            * (255 - overlay.getGreen(i, j))
                            * (255 - fastBitmap.getGreen(i, j))
                            / 255);
                temp = Math.min(255, temp);
                fastBitmap.setGreen(i, j, temp);
              }

              if (fastBitmap.getBlue(i, j) < 128) {
                temp = (2 * overlay.getBlue(i, j) * fastBitmap.getBlue(i, j) / 255);
                temp = Math.min(255, temp);
                fastBitmap.setBlue(i, j, temp);
              } else {
                temp =
                    (255
                        - 2
                            * (255 - overlay.getBlue(i, j))
                            * (255 - fastBitmap.getBlue(i, j))
                            / 255);
                temp = Math.min(255, temp);
                fastBitmap.setBlue(i, j, temp);
              }
            }
          }
          break;
        case HardLight:
          for (int i = 0; i < h; i++) {
            for (int j = 0; j < w; j++) {

              float temp;
              if (overlay.getRed(i, j) < 128) {
                temp =
                    (2 * ((fastBitmap.getRed(i, j) >> 1) + 64))
                        * ((float) overlay.getRed(i, j) / 255);
                fastBitmap.setRed(i, j, (int) temp);
              } else {
                temp =
                    (255
                        - (2
                            * (255 - ((fastBitmap.getRed(i, j) >> 1) + 64))
                            * (float) (255 - overlay.getRed(i, j))
                            / 255));
                fastBitmap.setRed(i, j, (int) temp);
              }

              if (overlay.getGreen(i, j) < 128) {
                temp =
                    (2 * ((fastBitmap.getGreen(i, j) >> 1) + 64))
                        * ((float) overlay.getGreen(i, j) / 255);
                fastBitmap.setGreen(i, j, (int) temp);
              } else {
                temp =
                    (255
                        - (2
                            * (255 - ((fastBitmap.getGreen(i, j) >> 1) + 64))
                            * (float) (255 - overlay.getGreen(i, j))
                            / 255));
                fastBitmap.setGreen(i, j, (int) temp);
              }

              if (overlay.getBlue(i, j) < 128) {
                temp =
                    (2 * ((fastBitmap.getBlue(i, j) >> 1) + 64))
                        * ((float) overlay.getBlue(i, j) / 255);
                fastBitmap.setBlue(i, j, (int) temp);
              } else {
                temp =
                    (255
                        - (2
                            * (255 - ((fastBitmap.getBlue(i, j) >> 1) + 64))
                            * (float) (255 - overlay.getBlue(i, j))
                            / 255));
                fastBitmap.setBlue(i, j, (int) temp);
              }
            }
          }
          break;
        case ColorDodge:
          for (int i = 0; i < h; i++) {
            for (int j = 0; j < w; j++) {

              if (overlay.getRed(i, j) == 255) {
                fastBitmap.setRed(i, j, 255);
              } else {
                int x =
                    Math.min(255, ((fastBitmap.getRed(i, j) << 8) / (255 - overlay.getRed(i, j))));
                fastBitmap.setRed(i, j, x);
              }

              if (overlay.getGreen(i, j) == 255) {
                fastBitmap.setGreen(i, j, 255);
              } else {
                int x =
                    Math.min(
                        255, ((fastBitmap.getGreen(i, j) << 8) / (255 - overlay.getGreen(i, j))));
                fastBitmap.setGreen(i, j, x);
              }

              if (overlay.getBlue(i, j) == 255) {
                fastBitmap.setBlue(i, j, 255);
              } else {
                int x =
                    Math.min(
                        255, ((fastBitmap.getBlue(i, j) << 8) / (255 - overlay.getBlue(i, j))));
                fastBitmap.setBlue(i, j, x);
              }
            }
          }
          break;
        case ColorBurn:
          for (int i = 0; i < h; i++) {
            for (int j = 0; j < w; j++) {

              if (overlay.getRed(i, j) == 0) {
                fastBitmap.setRed(i, j, overlay.getRed(i, j));
              } else {
                int x =
                    Math.max(
                        0, (255 - ((255 - fastBitmap.getRed(i, j)) << 8) / overlay.getRed(i, j)));
                fastBitmap.setRed(i, j, x);
              }

              if (overlay.getGreen(i, j) == 0) {
                fastBitmap.setGreen(i, j, overlay.getGreen(i, j));
              } else {
                int x =
                    Math.max(
                        0,
                        (255 - ((255 - fastBitmap.getGreen(i, j)) << 8) / overlay.getGreen(i, j)));
                fastBitmap.setGreen(i, j, x);
              }

              if (overlay.getBlue(i, j) == 0) {
                fastBitmap.setBlue(i, j, overlay.getBlue(i, j));
              } else {
                int x =
                    Math.max(
                        0, (255 - ((255 - fastBitmap.getBlue(i, j)) << 8) / overlay.getBlue(i, j)));
                fastBitmap.setBlue(i, j, x);
              }
            }
          }
          break;
        case LinearLight:
          for (int i = 0; i < h; i++) {
            for (int j = 0; j < w; j++) {
              int temp;

              if (overlay.getRed(i, j) < 128) {
                temp = fastBitmap.getRed(i, j) + (2 * overlay.getRed(i, j));
                if (temp < 255) {
                  fastBitmap.setRed(i, j, 0);
                } else {
                  fastBitmap.setRed(i, j, (temp - 255));
                }
              } else {
                int x = Math.min(fastBitmap.getRed(i, j) + (2 * (overlay.getRed(i, j) - 128)), 255);
                fastBitmap.setRed(i, j, x);
              }

              if (overlay.getGreen(i, j) < 128) {
                temp = fastBitmap.getGreen(i, j) + (2 * overlay.getGreen(i, j));
                if (temp < 255) {
                  fastBitmap.setGreen(i, j, 0);
                } else {
                  fastBitmap.setGreen(i, j, (temp - 255));
                }
              } else {
                int x =
                    Math.min(fastBitmap.getGreen(i, j) + (2 * (overlay.getGreen(i, j) - 128)), 255);
                fastBitmap.setGreen(i, j, x);
              }

              if (overlay.getBlue(i, j) < 128) {
                temp = fastBitmap.getBlue(i, j) + (2 * overlay.getBlue(i, j));
                if (temp < 255) {
                  fastBitmap.setBlue(i, j, 0);
                } else {
                  fastBitmap.setBlue(i, j, (temp - 255));
                }
              } else {
                int x =
                    Math.min(fastBitmap.getBlue(i, j) + (2 * (overlay.getBlue(i, j) - 128)), 255);
                fastBitmap.setBlue(i, j, x);
              }
            }
          }
          break;
        case VividLight:
          for (int i = 0; i < h; i++) {
            for (int j = 0; j < w; j++) {

              if (overlay.getRed(i, j) < 128) {
                // Color Burn
                int o = overlay.getRed(i, j) * 2;
                if (o == 0) {
                  fastBitmap.setRed(i, j, o);
                } else {
                  int x = Math.max(0, (255 - ((255 - fastBitmap.getRed(i, j)) << 8) / o));
                  fastBitmap.setRed(i, j, x);
                }
              } else {
                // Color Dodge
                int o = 2 * (overlay.getRed(i, j) - 128);
                if (o == 255) {
                  fastBitmap.setRed(i, j, 255);
                } else {
                  int x = Math.min(255, ((fastBitmap.getRed(i, j) << 8) / (255 - o)));
                  fastBitmap.setRed(i, j, x);
                }
              }

              if (overlay.getGreen(i, j) < 128) {
                // Color Burn
                int o = overlay.getGreen(i, j) * 2;
                if (o == 0) {
                  fastBitmap.setGreen(i, j, o);
                } else {
                  int x = Math.max(0, (255 - ((255 - fastBitmap.getGreen(i, j)) << 8) / o));
                  fastBitmap.setGreen(i, j, x);
                }
              } else {
                // Color Dodge
                int o = 2 * (overlay.getGreen(i, j) - 128);
                if (o == 255) {
                  fastBitmap.setGreen(i, j, 255);
                } else {
                  int x = Math.min(255, ((fastBitmap.getGreen(i, j) << 8) / (255 - o)));
                  fastBitmap.setGreen(i, j, x);
                }
              }

              if (overlay.getBlue(i, j) < 128) {
                // Color Burn
                int o = overlay.getBlue(i, j) * 2;
                if (o == 0) {
                  fastBitmap.setBlue(i, j, o);
                } else {
                  int x = Math.max(0, (255 - ((255 - fastBitmap.getBlue(i, j)) << 8) / o));
                  fastBitmap.setBlue(i, j, x);
                }
              } else {
                // Color Dodge
                int o = 2 * (overlay.getBlue(i, j) - 128);
                if (o == 255) {
                  fastBitmap.setGreen(i, j, 255);
                } else {
                  int x = Math.min(255, ((fastBitmap.getBlue(i, j) << 8) / (255 - o)));
                  fastBitmap.setBlue(i, j, x);
                }
              }
            }
          }

          break;
        case PinLight:
          for (int i = 0; i < h; i++) {
            for (int j = 0; j < w; j++) {

              int o = overlay.getRed(i, j) * 2;
              if (overlay.getRed(i, j) < 128) {
                // Darken
                if (o < fastBitmap.getRed(i, j)) {
                  fastBitmap.setRed(i, j, o);
                }
              } else {
                // Lighten
                if (o > fastBitmap.getRed(i, j)) {
                  fastBitmap.setRed(i, j, o);
                }
              }

              o = overlay.getGreen(i, j) * 2;
              if (overlay.getGreen(i, j) < 128) {
                // Darken
                if (o < fastBitmap.getGreen(i, j)) {
                  fastBitmap.setGreen(i, j, o);
                }
              } else {
                // Lighten
                if (o > fastBitmap.getGreen(i, j)) {
                  fastBitmap.setGreen(i, j, o);
                }
              }

              o = overlay.getBlue(i, j) * 2;
              if (overlay.getBlue(i, j) < 128) {
                // Darken
                if (o < fastBitmap.getBlue(i, j)) {
                  fastBitmap.setBlue(i, j, o);
                }
              } else {
                // Lighten
                if (o > fastBitmap.getBlue(i, j)) {
                  fastBitmap.setBlue(i, j, o);
                }
              }
            }
          }
          break;
        case Reflect:
          for (int i = 0; i < h; i++) {
            for (int j = 0; j < w; j++) {

              if (overlay.getRed(i, j) == 255) {
                fastBitmap.setRed(i, j, 255);
              } else {
                int x =
                    Math.min(
                        255,
                        (fastBitmap.getRed(i, j)
                            * fastBitmap.getRed(i, j)
                            / (255 - overlay.getRed(i, j))));
                fastBitmap.setRed(i, j, x);
              }

              if (overlay.getGreen(i, j) == 255) {
                fastBitmap.setGreen(i, j, 255);
              } else {
                int x =
                    Math.min(
                        255,
                        (fastBitmap.getGreen(i, j)
                            * fastBitmap.getGreen(i, j)
                            / (255 - overlay.getGreen(i, j))));
                fastBitmap.setGreen(i, j, x);
              }

              if (overlay.getBlue(i, j) == 255) {
                fastBitmap.setBlue(i, j, 255);
              } else {
                int x =
                    Math.min(
                        255,
                        (fastBitmap.getBlue(i, j)
                            * fastBitmap.getBlue(i, j)
                            / (255 - overlay.getBlue(i, j))));
                fastBitmap.setBlue(i, j, x);
              }
            }
          }
          break;
        case Phoenix:
          for (int i = 0; i < h; i++) {
            for (int j = 0; j < w; j++) {
              int r =
                  ((Math.min(fastBitmap.getRed(i, j), overlay.getRed(i, j))
                      - Math.max(fastBitmap.getRed(i, j), overlay.getRed(i, j))
                      + 255));
              int g =
                  ((Math.min(fastBitmap.getGreen(i, j), overlay.getGreen(i, j))
                      - Math.max(fastBitmap.getGreen(i, j), overlay.getGreen(i, j))
                      + 255));
              int b =
                  ((Math.min(fastBitmap.getBlue(i, j), overlay.getBlue(i, j))
                      - Math.max(fastBitmap.getBlue(i, j), overlay.getBlue(i, j))
                      + 255));

              fastBitmap.setRGB(i, j, r, g, b);
            }
          }
          break;
      }

    } else {
      throw new IllegalArgumentException("Blend only works in RGB images.");
    }
  }
  /**
   * Extract blob.
   *
   * @param id ID.
   * @param fastBitmap Image to be processed.
   * @return Image with the extracted blob.
   */
  public FastBitmap Extract(int id, FastBitmap fastBitmap) {

    // Check if blobs list is null.
    if (this.blobs == null) this.blobs = new BlobDetection().ProcessImage(fastBitmap);

    FastBitmap image;

    if (fastBitmap.isGrayscale()) {
      image =
          new FastBitmap(
              fastBitmap.getWidth(), fastBitmap.getHeight(), FastBitmap.ColorSpace.Grayscale);
      for (IntPoint p : blobs.get(id).getPoints()) {
        image.setGray(p.x, p.y, fastBitmap.getGray(p.x, p.y));
      }
    } else {
      image =
          new FastBitmap(fastBitmap.getWidth(), fastBitmap.getHeight(), FastBitmap.ColorSpace.RGB);
      for (IntPoint p : blobs.get(id).getPoints()) {
        image.setRed(p.x, p.y, fastBitmap.getRed(p.x, p.y));
        image.setGreen(p.x, p.y, fastBitmap.getGreen(p.x, p.y));
        image.setBlue(p.x, p.y, fastBitmap.getBlue(p.x, p.y));
      }
    }
    return image;
  }
  /**
   * Compute GLRLM.
   *
   * @param fastBitmap Image to be processed.
   * @return GLRLM.
   */
  public double[][] Compute(FastBitmap fastBitmap) {

    int maxGray = 255;
    if (autoGray) maxGray = ImageStatistics.Maximum(fastBitmap);

    int height = fastBitmap.getHeight();
    int width = fastBitmap.getWidth();

    double[][] runMatrix = new double[maxGray + 1][width + 1];

    switch (degree) {
      case Degree_0:
        for (int i = 0; i < height; i++) {
          int runs = 1;
          for (int j = 1; j < width; j++) {
            int g1 = fastBitmap.getGray(i, j - 1);
            int g2 = fastBitmap.getGray(i, j);
            if (g1 == g2) {
              runs++;
            } else {
              runMatrix[g1][runs]++;
              numPrimitives++;
              runs = 1;
            }
            if ((g1 == g2) && (j == width - 1)) {
              runMatrix[g1][runs]++;
            }
            if ((g1 != g2) && (j == width - 1)) {
              runMatrix[g2][1]++;
            }
          }
        }
        break;

      case Degree_45:

        // Compute I(0,0) and I(height,width)
        runMatrix[0][1]++;
        runMatrix[height - 1][width - 1]++;

        // Compute height
        for (int i = 1; i < height; i++) {
          int runs = 1;
          int steps = i;
          for (int j = 0; j < steps; j++) {
            int g1 = fastBitmap.getGray(i - j, j);
            int g2 = fastBitmap.getGray(i - j - 1, j + 1);
            if (g1 == g2) {
              runs++;
            } else {
              runMatrix[g1][runs]++;
              numPrimitives++;
              runs = 1;
            }
            if ((g1 == g2) && (j == steps - 1)) {
              runMatrix[g1][runs]++;
            }
            if ((g1 != g2) && (j == steps - 1)) {
              runMatrix[g2][1]++;
            }
          }
        }

        // Compute width
        for (int j = 1; j < width - 1; j++) {
          int runs = 1;
          int steps = height - j;
          for (int i = 1; i < steps; i++) {
            int g1 = fastBitmap.getGray(height - i, j + i - 1);
            int g2 = fastBitmap.getGray(height - i - 1, j + i);
            if (g1 == g2) {
              runs++;
            } else {
              runMatrix[g1][runs]++;
              numPrimitives++;
              runs = 1;
            }
            if ((g1 == g2) && (i == steps - 1)) {
              runMatrix[g1][runs]++;
            }
            if ((g1 != g2) && (i == steps - 1)) {
              runMatrix[g2][1]++;
            }
          }
        }
        break;

      case Degree_90:
        for (int j = 0; j < width; j++) {
          int runs = 1;
          for (int i = 0; i < height - 1; i++) {
            int g1 = fastBitmap.getGray(height - i - 1, j);
            int g2 = fastBitmap.getGray(height - i - 2, j);
            if (g1 == g2) {
              runs++;
            } else {
              runMatrix[g1][runs]++;
              numPrimitives++;
              runs = 1;
            }
            if ((g1 == g2) && (i == height - 2)) {
              runMatrix[g1][runs]++;
            }
            if ((g1 != g2) && (i == height - 2)) {
              runMatrix[g2][1]++;
            }
          }
        }
        break;

      case Degree_135:

        // Compute I(0,width) and I(height,0)
        runMatrix[0][width - 1]++;
        runMatrix[height - 1][0]++;

        // Compute height
        for (int i = 1; i < width; i++) {
          int runs = 1;
          int steps = i;
          int w = width - 1;
          for (int j = 0; j < steps; j++) {
            int g1 = fastBitmap.getGray(i - j, w);
            int g2 = fastBitmap.getGray(i - j - 1, --w);
            if (g1 == g2) {
              runs++;
            } else {
              runMatrix[g1][runs]++;
              numPrimitives++;
              runs = 1;
            }
            if ((g1 == g2) && (j == steps - 1)) {
              runMatrix[g1][runs]++;
            }
            if ((g1 != g2) && (j == steps - 1)) {
              runMatrix[g2][1]++;
            }
          }
        }
        // Compute width
        for (int j = 1; j < width - 1; j++) {
          int runs = 1;
          int steps = height - j;
          int w = width - 1 - j;
          for (int i = 1; i < steps; i++) {
            int g1 = fastBitmap.getGray(height - i, w);
            int g2 = fastBitmap.getGray(height - i - 1, --w);
            if (g1 == g2) {
              runs++;
            } else {
              runMatrix[g1][runs]++;
              numPrimitives++;
              runs = 1;
            }
            if ((g1 == g2) && (i == steps - 1)) {
              runMatrix[g1][runs]++;
            }
            if ((g1 != g2) && (i == steps - 1)) {
              runMatrix[g2][1]++;
            }
          }
        }
        break;
    }
    return runMatrix;
  }