/**
   * Returns the kernel for finding the X derivative.
   *
   * @param type Type of gradient
   * @param isInteger integer or floating point kernels
   * @return The kernel. Can be 1D or 2D
   */
  public static KernelBase lookupKernelX(DerivativeType type, boolean isInteger) {
    switch (type) {
      case PREWITT:
        return GradientPrewitt.getKernelX(isInteger);

      case SOBEL:
        return GradientSobel.getKernelX(isInteger);

      case THREE:
        return GradientThree.getKernelX(isInteger);

      case TWO_0:
        return GradientTwo0.getKernelX(isInteger);

      case TWO_1:
        return GradientTwo1.getKernelX(isInteger);
    }

    throw new IllegalArgumentException("Unknown kernel type: " + type);
  }
  /**
   * Computes the gradient using the specified image type.
   *
   * @param type Type of gradient to compute
   * @param input Input image
   * @param derivX Output. Derivative X
   * @param derivY Output. Derivative Y
   * @param borderType How it should handle borders. null == skip border
   * @param <I> Input image type
   * @param <D> Output image type
   */
  public static <I extends ImageSingleBand, D extends ImageSingleBand> void gradient(
      DerivativeType type, I input, D derivX, D derivY, BorderType borderType) {

    ImageBorder<I> border =
        BorderType.SKIP == borderType ? null : FactoryImageBorder.general(input, borderType);

    switch (type) {
      case PREWITT:
        if (input instanceof ImageFloat32) {
          GradientPrewitt.process(
              (ImageFloat32) input,
              (ImageFloat32) derivX,
              (ImageFloat32) derivY,
              (ImageBorder_F32) border);
        } else if (input instanceof ImageUInt8) {
          GradientPrewitt.process(
              (ImageUInt8) input,
              (ImageSInt16) derivX,
              (ImageSInt16) derivY,
              (ImageBorder_I32) border);
        } else if (input instanceof ImageSInt16) {
          GradientPrewitt.process(
              (ImageSInt16) input,
              (ImageSInt16) derivX,
              (ImageSInt16) derivY,
              (ImageBorder_I32) border);
        } else {
          throw new IllegalArgumentException(
              "Unknown input image type: " + input.getClass().getSimpleName());
        }
        break;
      case SOBEL:
        if (input instanceof ImageFloat32) {
          GradientSobel.process(
              (ImageFloat32) input,
              (ImageFloat32) derivX,
              (ImageFloat32) derivY,
              (ImageBorder_F32) border);
        } else if (input instanceof ImageUInt8) {
          GradientSobel.process(
              (ImageUInt8) input,
              (ImageSInt16) derivX,
              (ImageSInt16) derivY,
              (ImageBorder_I32) border);
        } else if (input instanceof ImageSInt16) {
          GradientSobel.process(
              (ImageSInt16) input,
              (ImageSInt16) derivX,
              (ImageSInt16) derivY,
              (ImageBorder_I32) border);
        } else {
          throw new IllegalArgumentException(
              "Unknown input image type: " + input.getClass().getSimpleName());
        }
        break;
      case THREE:
        if (input instanceof ImageFloat32) {
          GradientThree.process(
              (ImageFloat32) input,
              (ImageFloat32) derivX,
              (ImageFloat32) derivY,
              (ImageBorder_F32) border);
        } else if (input instanceof ImageUInt8) {
          GradientThree.process(
              (ImageUInt8) input,
              (ImageSInt16) derivX,
              (ImageSInt16) derivY,
              (ImageBorder_I32) border);
        } else if (input instanceof ImageSInt16) {
          GradientThree.process(
              (ImageSInt16) input,
              (ImageSInt16) derivX,
              (ImageSInt16) derivY,
              (ImageBorder_I32) border);
        } else {
          throw new IllegalArgumentException(
              "Unknown input image type: " + input.getClass().getSimpleName());
        }
        break;
      case TWO_0:
        if (input instanceof ImageFloat32) {
          GradientTwo0.process(
              (ImageFloat32) input,
              (ImageFloat32) derivX,
              (ImageFloat32) derivY,
              (ImageBorder_F32) border);
        } else if (input instanceof ImageUInt8) {
          GradientTwo0.process(
              (ImageUInt8) input,
              (ImageSInt16) derivX,
              (ImageSInt16) derivY,
              (ImageBorder_I32) border);
        } else if (input instanceof ImageSInt16) {
          GradientTwo0.process(
              (ImageSInt16) input,
              (ImageSInt16) derivX,
              (ImageSInt16) derivY,
              (ImageBorder_I32) border);
        } else {
          throw new IllegalArgumentException(
              "Unknown input image type: " + input.getClass().getSimpleName());
        }
        break;
      case TWO_1:
        if (input instanceof ImageFloat32) {
          GradientTwo1.process(
              (ImageFloat32) input,
              (ImageFloat32) derivX,
              (ImageFloat32) derivY,
              (ImageBorder_F32) border);
        } else if (input instanceof ImageUInt8) {
          GradientTwo1.process(
              (ImageUInt8) input,
              (ImageSInt16) derivX,
              (ImageSInt16) derivY,
              (ImageBorder_I32) border);
        } else if (input instanceof ImageSInt16) {
          GradientTwo1.process(
              (ImageSInt16) input,
              (ImageSInt16) derivX,
              (ImageSInt16) derivY,
              (ImageBorder_I32) border);
        } else {
          throw new IllegalArgumentException(
              "Unknown input image type: " + input.getClass().getSimpleName());
        }
        break;

      default:
        throw new IllegalArgumentException("Unknown type: " + type);
    }
  }