@Override
  public Image process(Image original) {
    int width = original.getWidth();
    int height = original.getHeight();

    Image regionsMask = new Image(original.getName(), width, height);
    Image result = new Image(original);

    PixelPoint[] seedPoints = prepareSeedPoints(original);

    runRegionGrowing(original, regionsMask, seedPoints);
    applyMaskToResultImage(regionsMask, result);

    return result;
  }
  private PixelPoint[] prepareSeedPoints(Image image) {
    int width = image.getWidth();
    int height = image.getHeight();

    PixelPoint[] seedPoints = new PixelPoint[SEED_NUM];
    for (int x = 0; x < SEED_NUM_ROWS; x++) {
      for (int y = 0; y < SEED_NUM_COLS; y++) {
        seedPoints[x * SEED_NUM_ROWS + y] =
            new PixelPoint(
                image.getPixel(x * (width / SEED_NUM_ROWS), y * (height / SEED_NUM_COLS)),
                x * (width / SEED_NUM_ROWS),
                y * (height / SEED_NUM_COLS));
      }
    }

    return seedPoints;
  }
 private int[] calculateA(Image img, int x, int y, RgbColor c) {
   return new int[] {
     img.getPixel(x - 1, y - 1).getColor(c),
     img.getPixel(x, y - 1).getColor(c),
     img.getPixel(x + 1, y - 1).getColor(c),
     img.getPixel(x + 1, y).getColor(c),
     img.getPixel(x + 1, y + 1).getColor(c),
     img.getPixel(x, y + 1).getColor(c),
     img.getPixel(x - 1, y + 1).getColor(c),
     img.getPixel(x - 1, y).getColor(c)
   };
 }
  private void applyMaskToResultImage(Image imageWithRegions, Image result) {
    int width = result.getWidth();
    int height = result.getHeight();

    for (int x = 0; x < width; x++) {
      for (int y = 0; y < height; y++) {
        if (imageWithRegions.getPixel(x, y).getGrayScale() >= 0) {
          result.getPixel(x, y).setRed(0);
          result.getPixel(x, y).setGreen(imageWithRegions.getPixel(x, y).getGreen());
          result.getPixel(x, y).setBlue(imageWithRegions.getPixel(x, y).getBlue());
        }
      }
    }
  }
 @Override
 protected boolean checkIfNotBorderValue(Image img, int x, int y) {
   return x == 0 || y == 0 || x == img.getWidth() - 1 || y == img.getHeight() - 1;
 }
  private void runRegionGrowing(Image original, Image regionsMask, PixelPoint[] seeds) {
    int width = original.getWidth();
    int height = original.getHeight();
    int regions = 0;
    for (PixelPoint seed : seeds) {
      int min =
          ((seed.pixel.getGrayScale() - threshold) > 0) ? seed.pixel.getGrayScale() - threshold : 0;
      int max =
          ((seed.pixel.getGrayScale() + threshold) < 255)
              ? seed.pixel.getGrayScale() + threshold
              : 255;

      Stack<PixelPoint> stack = new Stack<>();
      List<PixelPoint> region = new ArrayList<>();

      if (regionsMask.getPixel(seed.x, seed.y).getGrayScale()
          < 0) { // meaning it was not examined yet
        stack.push(seed);

        while (!stack.isEmpty()) {
          PixelPoint curFromStack = stack.pop();
          for (int x = -1; x < 2; x++) {
            for (int y = -1; y < 2; y++) {
              if (curFromStack.x + x >= width
                  || curFromStack.x + x <= 0
                  || curFromStack.y + y >= height
                  || curFromStack.y + y <= 0) {
                continue;
              }
              if (regionsMask.getPixel(curFromStack.x + x, curFromStack.y + y).getGrayScale()
                  >= 0) {
                continue;
              }

              PixelPoint curFromNeighbourhood =
                  new PixelPoint(
                      regionsMask.getPixel(curFromStack.x + x, curFromStack.y + y),
                      curFromStack.x + x,
                      curFromStack.y + y);

              if (original.getPixel(curFromStack.x + x, curFromStack.y + y).getGrayScale() <= max
                  && original.getPixel(curFromStack.x + x, curFromStack.y + y).getGrayScale()
                      >= min) {
                for (RgbColor color : RgbColor.values()) {
                  regionsMask
                      .getPixel(curFromStack.x + x, curFromStack.y + y)
                      .setColor(color, Math.abs(255 - 15 * regions));
                }
                region.add(curFromNeighbourhood);
                stack.push(curFromNeighbourhood);
              }
            }
          }
        }

        if (region.size() < minimumPixelsForRegion) {
          for (PixelPoint point : region) {
            for (Pixel.RgbColor color : Pixel.RgbColor.values()) {
              regionsMask.getPixel(point.x, point.y).setColor(color, -10);
            }
          }
        } else {
          regions++;
        }

        region.clear();
      }
    }
  }