/**
   * Computes the hessian from the original input image. Only Sobel and Three supported.
   *
   * @param type Type of gradient to compute
   * @param input Input image
   * @param derivXX Output. Derivative XX
   * @param derivYY Output. Derivative YY
   * @param derivXY Output. Derivative XY
   * @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 hessian(
      DerivativeType type, I input, D derivXX, D derivYY, D derivXY, BorderType borderType) {
    ImageBorder<I> border =
        BorderType.SKIP == borderType ? null : FactoryImageBorder.general(input, borderType);

    switch (type) {
      case SOBEL:
        if (input instanceof ImageFloat32) {
          HessianSobel.process(
              (ImageFloat32) input,
              (ImageFloat32) derivXX,
              (ImageFloat32) derivYY,
              (ImageFloat32) derivXY,
              (ImageBorder_F32) border);
        } else if (input instanceof ImageUInt8) {
          HessianSobel.process(
              (ImageUInt8) input,
              (ImageSInt16) derivXX,
              (ImageSInt16) derivYY,
              (ImageSInt16) derivXY,
              (ImageBorder_I32) border);
        } else {
          throw new IllegalArgumentException(
              "Unknown input image type: " + input.getClass().getSimpleName());
        }
        break;

      case THREE:
        if (input instanceof ImageFloat32) {
          HessianThree.process(
              (ImageFloat32) input,
              (ImageFloat32) derivXX,
              (ImageFloat32) derivYY,
              (ImageFloat32) derivXY,
              (ImageBorder_F32) border);
        } else if (input instanceof ImageUInt8) {
          HessianThree.process(
              (ImageUInt8) input,
              (ImageSInt16) derivXX,
              (ImageSInt16) derivYY,
              (ImageSInt16) derivXY,
              (ImageBorder_I32) border);
        } else {
          throw new IllegalArgumentException(
              "Unknown input image type: " + input.getClass().getSimpleName());
        }
        break;

      default:
        throw new IllegalArgumentException("Unsupported derivative type " + type);
    }
  }
 public static <I extends ImageSingleBand, D extends ImageSingleBand> void laplace(
     I input, D output) {
   if (input instanceof ImageFloat32) {
     LaplacianEdge.process((ImageFloat32) input, (ImageFloat32) output);
   } else if (input instanceof ImageUInt8) {
     LaplacianEdge.process((ImageUInt8) input, (ImageSInt16) output);
   } else {
     throw new IllegalArgumentException(
         "Unknown input image type: " + input.getClass().getSimpleName());
   }
 }
  /**
   * 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);
    }
  }