/**
   * Adjust bound to ensure the entire image is contained inside, otherwise there might be single
   * pixel wide black regions
   */
  public static void roundInside(Rectangle2D_F32 bound) {
    float x0 = (float) Math.ceil(bound.tl_x);
    float y0 = (float) Math.ceil(bound.tl_y);
    float x1 = (float) Math.floor(bound.tl_x + bound.width);
    float y1 = (float) Math.floor(bound.tl_y + bound.height);

    bound.tl_x = x0;
    bound.tl_y = y0;
    bound.width = x1 - x0;
    bound.height = y1 - y0;
  }
  /**
   * Adjusts the view such that each pixel has a correspondence to the original image while
   * maximizing the view area. In other words no black regions which can cause problems for some
   * image processing algorithms.
   *
   * <p>The original image coordinate system is maintained even if the intrinsic parameter flipY is
   * true.
   *
   * @param param Intrinsic camera parameters.
   * @param paramAdj If not null, the new camera parameters are stored here.
   * @return New transform that adjusts the view and removes lens distortion..
   */
  public static PointTransform_F32 allInside(
      IntrinsicParameters param, IntrinsicParameters paramAdj) {
    RemoveRadialPtoP_F32 removeDistort = new RemoveRadialPtoP_F32();
    AddRadialPtoP_F32 addDistort = new AddRadialPtoP_F32();
    removeDistort.set(param.fx, param.fy, param.skew, param.cx, param.cy, param.radial);
    addDistort.set(param.fx, param.fy, param.skew, param.cx, param.cy, param.radial);

    Rectangle2D_F32 bound =
        LensDistortionOps.boundBoxInside(
            param.width, param.height, new PointToPixelTransform_F32(removeDistort));

    // ensure there are no strips of black
    LensDistortionOps.roundInside(bound);

    double scaleX = bound.width / param.width;
    double scaleY = bound.height / param.height;

    double scale = Math.min(scaleX, scaleY);

    // translation and shift over so that the small axis is in the middle
    double deltaX = bound.tl_x + (scaleX - scale) * param.width / 2.0;
    double deltaY = bound.tl_y + (scaleY - scale) * param.height / 2.0;

    // adjustment matrix
    DenseMatrix64F A = new DenseMatrix64F(3, 3, true, scale, 0, deltaX, 0, scale, deltaY, 0, 0, 1);

    PointTransform_F32 tranAdj =
        PerspectiveOps.adjustIntrinsic_F32(addDistort, false, param, A, paramAdj);

    if (param.flipY) {
      PointTransform_F32 flip = new FlipVertical_F32(param.height);
      return new SequencePointTransform_F32(flip, tranAdj, flip);
    } else return tranAdj;
  }
  /**
   * Transforms the view such that the entire original image is visible after lens distortion has
   * been removed. The appropriate {@link PointTransform_F32} is returned and a new set of intrinsic
   * camera parameters for the "virtual" camera that is associated with the returned transformed.
   *
   * <p>The original image coordinate system is maintained even if the intrinsic parameter flipY is
   * true.
   *
   * @param param Intrinsic camera parameters.
   * @param paramAdj If not null, the new camera parameters are stored here.
   * @return New transform that adjusts the view and removes lens distortion..
   */
  public static PointTransform_F32 fullView(
      IntrinsicParameters param, IntrinsicParameters paramAdj) {

    RemoveRadialPtoP_F32 removeDistort = new RemoveRadialPtoP_F32();
    AddRadialPtoP_F32 addDistort = new AddRadialPtoP_F32();
    removeDistort.set(param.fx, param.fy, param.skew, param.cx, param.cy, param.radial);
    addDistort.set(param.fx, param.fy, param.skew, param.cx, param.cy, param.radial);

    Rectangle2D_F32 bound =
        DistortImageOps.boundBox_F32(
            param.width, param.height, new PointToPixelTransform_F32(removeDistort));

    double scaleX = bound.width / param.width;
    double scaleY = bound.height / param.height;

    double scale = Math.max(scaleX, scaleY);

    // translation
    double deltaX = bound.tl_x;
    double deltaY = bound.tl_y;

    // adjustment matrix
    DenseMatrix64F A = new DenseMatrix64F(3, 3, true, scale, 0, deltaX, 0, scale, deltaY, 0, 0, 1);

    PointTransform_F32 tranAdj =
        PerspectiveOps.adjustIntrinsic_F32(addDistort, false, param, A, paramAdj);

    if (param.flipY) {
      PointTransform_F32 flip = new FlipVertical_F32(param.height);
      return new SequencePointTransform_F32(flip, tranAdj, flip);
    } else return tranAdj;
  }