public static void horizontal3(
      Kernel1D_I32 kernel, ImageSInt16 image, ImageInt16 dest, int divisor, boolean includeBorder) {
    final short[] dataSrc = image.data;
    final short[] dataDst = dest.data;

    final int k1 = kernel.data[0];
    final int k2 = kernel.data[1];
    final int k3 = kernel.data[2];

    final int radius = kernel.getRadius();

    final int yBorder = includeBorder ? 0 : radius;

    final int width = image.getWidth();
    final int height = image.getHeight() - yBorder;
    final int halfDivisor = divisor / 2;

    for (int i = yBorder; i < height; i++) {
      int indexDst = dest.startIndex + i * dest.stride + radius;
      int j = image.startIndex + i * image.stride - radius;
      final int jEnd = j + width - radius;

      for (j += radius; j < jEnd; j++) {
        int indexSrc = j;
        int total = (dataSrc[indexSrc++]) * k1;
        total += (dataSrc[indexSrc++]) * k2;
        total += (dataSrc[indexSrc]) * k3;

        dataDst[indexDst++] = (short) ((total + halfDivisor) / divisor);
      }
    }
  }
  public static void convolve3(Kernel2D_I32 kernel, ImageSInt16 src, ImageInt16 dest, int divisor) {
    final short[] dataSrc = src.data;
    final short[] dataDst = dest.data;

    final int width = src.getWidth();
    final int height = src.getHeight();
    final int halfDivisor = divisor / 2;

    final int kernelRadius = kernel.getRadius();
    final int totalRow[] = new int[width];

    for (int y = kernelRadius; y < height - kernelRadius; y++) {

      // first time through the value needs to be set
      int k1 = kernel.data[0];
      int k2 = kernel.data[1];
      int k3 = kernel.data[2];

      int indexSrcRow = src.startIndex + (y - kernelRadius) * src.stride - kernelRadius;
      for (int x = kernelRadius; x < width - kernelRadius; x++) {
        int indexSrc = indexSrcRow + x;

        int total = 0;
        total += (dataSrc[indexSrc++]) * k1;
        total += (dataSrc[indexSrc++]) * k2;
        total += (dataSrc[indexSrc]) * k3;

        totalRow[x] = total;
      }

      // rest of the convolution rows are an addition
      for (int i = 1; i < 3; i++) {
        indexSrcRow = src.startIndex + (y + i - kernelRadius) * src.stride - kernelRadius;

        k1 = kernel.data[i * 3 + 0];
        k2 = kernel.data[i * 3 + 1];
        k3 = kernel.data[i * 3 + 2];

        for (int x = kernelRadius; x < width - kernelRadius; x++) {
          int indexSrc = indexSrcRow + x;

          int total = 0;
          total += (dataSrc[indexSrc++]) * k1;
          total += (dataSrc[indexSrc++]) * k2;
          total += (dataSrc[indexSrc]) * k3;

          totalRow[x] += total;
        }
      }
      int indexDst = dest.startIndex + y * dest.stride + kernelRadius;
      for (int x = kernelRadius; x < width - kernelRadius; x++) {
        dataDst[indexDst++] = (short) ((totalRow[x] + halfDivisor) / divisor);
      }
    }
  }
  /** Adds Gaussian/normal i.i.d noise to each pixel in the image. */
  public static void addGaussian(ImageSInt16 img, Random rand, double sigma, int min, int max) {
    final int h = img.getHeight();
    final int w = img.getWidth();

    short[] data = img.data;

    for (int y = 0; y < h; y++) {
      int index = img.getStartIndex() + y * img.getStride();
      for (int x = 0; x < w; x++) {
        int value = (data[index]) + (int) (rand.nextGaussian() * sigma);
        if (value < min) value = min;
        if (value > max) value = max;

        data[index++] = (short) value;
      }
    }
  }
  /** Adds uniform i.i.d noise to each pixel in the image. Noise range is min <= X < max. */
  public static void addUniform(ImageSInt16 img, Random rand, int min, int max) {
    final int h = img.getHeight();
    final int w = img.getWidth();

    int range = max - min;

    short[] data = img.data;

    for (int y = 0; y < h; y++) {
      int index = img.getStartIndex() + y * img.getStride();
      for (int x = 0; x < w; x++) {
        int value = (data[index]) + rand.nextInt(range) + min;
        if (value < -32768) value = -32768;
        if (value > 32767) value = 32767;

        data[index++] = (short) value;
      }
    }
  }