public void Calc_5Fr(ImagePlus imp1, ImagePlus imp2) {
    if (imp1.getType() != imp2.getType()) {
      error();
      return;
    }
    if (imp1.getType() == 0) { // getType returns 0 for 8-bit, 1 for 16-bit
      bitDepth = "8-bit";
      Prefs.set("ps.bitDepth", bitDepth);
    } else {
      bitDepth = "16-bit";
      Prefs.set("ps.bitDepth", bitDepth);
    }
    int width = imp1.getWidth();
    int height = imp1.getHeight();
    if (width != imp2.getWidth() || height != imp2.getHeight()) {
      error();
      return;
    }

    ImageStack stack1 = imp1.getStack();
    //		if (bgStackTitle != "NoBg") ImageStack stack2 = imp2.getStack();
    ImageStack stack2 = imp2.getStack();

    ImageProcessor ip = imp1.getProcessor();
    int dimension = width * height;
    byte[] pixB;
    short[] pixS;
    float[][] pixF = new float[5][dimension];
    float[][] pixFBg = new float[5][dimension];

    float a;
    float b;
    float den;
    float aSmp;
    float bSmp;
    float denSmp;
    float aBg;
    float bBg;
    float denBg;
    float retF;
    float azimF;

    byte[] retB = new byte[dimension];
    short[] retS = new short[dimension];
    byte[] azimB = new byte[dimension];
    short[] azimS = new short[dimension];
    // Derived Variables:
    float swingAngle = 2f * (float) Math.PI * swing;
    float tanSwingAngleDiv2 = (float) Math.tan(swingAngle / 2.f);
    float tanSwingAngleDiv2DivSqrt2 = (float) (Math.tan(swingAngle / 2.f) / Math.sqrt(2));
    float wavelengthDiv2Pi = wavelength / (2f * (float) Math.PI);

    // get the pixels of each slice in the stack and convert to float
    for (int i = 0; i < 5; i++) {
      if (bitDepth == "8-bit") {
        pixB = (byte[]) stack1.getPixels(i + 3);
        for (int j = 0; j < dimension; j++) pixF[i][j] = 0xff & pixB[j];
        if (bgStackTitle != "NoBg") {
          pixB = (byte[]) stack2.getPixels(i + 3);
          for (int j = 0; j < dimension; j++) pixFBg[i][j] = 0xff & pixB[j];
        }
      } else {
        pixS = (short[]) stack1.getPixels(i + 3);
        for (int j = 0; j < dimension; j++) pixF[i][j] = (float) pixS[j];
        if (bgStackTitle != "NoBg") {
          pixS = (short[]) stack2.getPixels(i + 3);
          for (int j = 0; j < dimension; j++) pixFBg[i][j] = (float) pixS[j];
        }
      }
    }

    // Algorithm
    // terms a and b
    for (int j = 0; j < dimension; j++) {
      denSmp = (pixF[1][j] + pixF[2][j] + pixF[3][j] + pixF[4][j] - 4 * pixF[0][j]) / 2;
      denBg = denSmp;
      a = (pixF[4][j] - pixF[1][j]);
      aSmp = a;
      aBg = a;
      b = (pixF[2][j] - pixF[3][j]);
      bSmp = b;
      bBg = b;
      if (bgStackTitle != "NoBg") {
        denBg = (pixFBg[1][j] + pixFBg[2][j] + pixFBg[3][j] + pixFBg[4][j] - 4 * pixFBg[0][j]) / 2;
        aBg = pixFBg[4][j] - pixFBg[1][j];
        bBg = pixFBg[2][j] - pixFBg[3][j];
      }
      // Special case of sample retardance half wave, denSmp = 0
      if (denSmp == 0) {
        retF = (float) wavelength / 4;
        azimF =
            (float) (a == 0 & b == 0 ? 0 : (azimRef + 90 + 90 * Math.atan2(a, b) / Math.PI) % 180);
      } else {
        // Retardance, the background correction can be improved by separately considering sample
        // retardance values larger than a quarter wave
        if (bgStackTitle != "NoBg") {
          a = aSmp / denSmp - aBg / denBg;
          b = bSmp / denSmp - bBg / denBg;
        } else {
          a = aSmp / denSmp;
          b = bSmp / denSmp;
        }
        retF = (float) Math.atan(tanSwingAngleDiv2 * Math.sqrt(a * a + b * b));
        if (denSmp < 0) retF = (float) Math.PI - retF;
        retF = retF * wavelengthDiv2Pi; // convert to nm
        if (retF > retCeiling) retF = retCeiling;

        // Orientation
        if ((bgStackTitle == "NoBg") || ((bgStackTitle != "NoBg") && (Math.abs(denSmp) < 1))) {
          a = aSmp;
          b = bSmp;
        }
        azimF =
            (float) (a == 0 & b == 0 ? 0 : (azimRef + 90 + 90 * Math.atan2(a, b) / Math.PI) % 180);
      }
      if (bitDepth == "8-bit") retB[j] = (byte) (((int) (255 * retF / retCeiling)) & 0xff);
      else retS[j] = (short) (4095 * retF / retCeiling);
      if (mirror == "Yes") azimF = 180 - azimF;
      if (bitDepth == "8-bit") azimB[j] = (byte) (((int) azimF) & 0xff);
      else azimS[j] = (short) (azimF * 10f);
    }
    // show the resulting images in slice 1 and 2
    imp1.setSlice(3);
    if (bitDepth == "8-bit") {
      stack1.setPixels(retB, 1);
      stack1.setPixels(azimB, 2);
    } else {
      stack1.setPixels(retS, 1);
      stack1.setPixels(azimS, 2);
    }
    imp1.setSlice(1);
    IJ.selectWindow(imp1.getTitle());

    Prefs.set("ps.sampleStackTitle", sampleStackTitle);
    Prefs.set("ps.bgStackTitle", bgStackTitle);
    Prefs.set("ps.mirror", mirror);
    Prefs.set("ps.wavelength", wavelength);
    Prefs.set("ps.swing", swing);
    Prefs.set("ps.retCeiling", retCeiling);
    Prefs.set("ps.azimRef", azimRef);
    Prefs.savePreferences();
  }
  /*------------------------------------------------------------------*/
  void doIt(ImageProcessor ip) {
    int width = ip.getWidth();
    int height = ip.getHeight();
    double hLine[] = new double[width];
    double vLine[] = new double[height];

    if (!(ip.getPixels() instanceof float[])) {
      throw new IllegalArgumentException("Float image required");
    }
    switch (operation) {
      case GRADIENT_MAGNITUDE:
        {
          ImageProcessor h = ip.duplicate();
          ImageProcessor v = ip.duplicate();
          float[] floatPixels = (float[]) ip.getPixels();
          float[] floatPixelsH = (float[]) h.getPixels();
          float[] floatPixelsV = (float[]) v.getPixels();

          getHorizontalGradient(h, FLT_EPSILON);
          getVerticalGradient(v, FLT_EPSILON);
          for (int y = 0, k = 0; (y < height); y++) {
            for (int x = 0; (x < width); x++, k++) {
              floatPixels[k] =
                  (float)
                      Math.sqrt(
                          floatPixelsH[k] * floatPixelsH[k] + floatPixelsV[k] * floatPixelsV[k]);
            }
            stepProgressBar();
          }
        }
        break;
      case GRADIENT_DIRECTION:
        {
          ImageProcessor h = ip.duplicate();
          ImageProcessor v = ip.duplicate();
          float[] floatPixels = (float[]) ip.getPixels();
          float[] floatPixelsH = (float[]) h.getPixels();
          float[] floatPixelsV = (float[]) v.getPixels();

          getHorizontalGradient(h, FLT_EPSILON);
          getVerticalGradient(v, FLT_EPSILON);
          for (int y = 0, k = 0; (y < height); y++) {
            for (int x = 0; (x < width); x++, k++) {
              floatPixels[k] = (float) Math.atan2(floatPixelsH[k], floatPixelsV[k]);
            }
            stepProgressBar();
          }
        }
        break;
      case LAPLACIAN:
        {
          ImageProcessor hh = ip.duplicate();
          ImageProcessor vv = ip.duplicate();
          float[] floatPixels = (float[]) ip.getPixels();
          float[] floatPixelsHH = (float[]) hh.getPixels();
          float[] floatPixelsVV = (float[]) vv.getPixels();

          getHorizontalHessian(hh, FLT_EPSILON);
          getVerticalHessian(vv, FLT_EPSILON);
          for (int y = 0, k = 0; (y < height); y++) {
            for (int x = 0; (x < width); x++, k++) {
              floatPixels[k] = (float) (floatPixelsHH[k] + floatPixelsVV[k]);
            }
            stepProgressBar();
          }
        }
        break;
      case LARGEST_HESSIAN:
        {
          ImageProcessor hh = ip.duplicate();
          ImageProcessor vv = ip.duplicate();
          ImageProcessor hv = ip.duplicate();
          float[] floatPixels = (float[]) ip.getPixels();
          float[] floatPixelsHH = (float[]) hh.getPixels();
          float[] floatPixelsVV = (float[]) vv.getPixels();
          float[] floatPixelsHV = (float[]) hv.getPixels();

          getHorizontalHessian(hh, FLT_EPSILON);
          getVerticalHessian(vv, FLT_EPSILON);
          getCrossHessian(hv, FLT_EPSILON);
          for (int y = 0, k = 0; (y < height); y++) {
            for (int x = 0; (x < width); x++, k++) {
              floatPixels[k] =
                  (float)
                      (0.5
                          * (floatPixelsHH[k]
                              + floatPixelsVV[k]
                              + Math.sqrt(
                                  4.0 * floatPixelsHV[k] * floatPixelsHV[k]
                                      + (floatPixelsHH[k] - floatPixelsVV[k])
                                          * (floatPixelsHH[k] - floatPixelsVV[k]))));
            }
            stepProgressBar();
          }
        }
        break;
      case SMALLEST_HESSIAN:
        {
          ImageProcessor hh = ip.duplicate();
          ImageProcessor vv = ip.duplicate();
          ImageProcessor hv = ip.duplicate();
          float[] floatPixels = (float[]) ip.getPixels();
          float[] floatPixelsHH = (float[]) hh.getPixels();
          float[] floatPixelsVV = (float[]) vv.getPixels();
          float[] floatPixelsHV = (float[]) hv.getPixels();

          getHorizontalHessian(hh, FLT_EPSILON);
          getVerticalHessian(vv, FLT_EPSILON);
          getCrossHessian(hv, FLT_EPSILON);
          for (int y = 0, k = 0; (y < height); y++) {
            for (int x = 0; (x < width); x++, k++) {
              floatPixels[k] =
                  (float)
                      (0.5
                          * (floatPixelsHH[k]
                              + floatPixelsVV[k]
                              - Math.sqrt(
                                  4.0 * floatPixelsHV[k] * floatPixelsHV[k]
                                      + (floatPixelsHH[k] - floatPixelsVV[k])
                                          * (floatPixelsHH[k] - floatPixelsVV[k]))));
            }
            stepProgressBar();
          }
        }
        break;
      case HESSIAN_ORIENTATION:
        {
          ImageProcessor hh = ip.duplicate();
          ImageProcessor vv = ip.duplicate();
          ImageProcessor hv = ip.duplicate();
          float[] floatPixels = (float[]) ip.getPixels();
          float[] floatPixelsHH = (float[]) hh.getPixels();
          float[] floatPixelsVV = (float[]) vv.getPixels();
          float[] floatPixelsHV = (float[]) hv.getPixels();

          getHorizontalHessian(hh, FLT_EPSILON);
          getVerticalHessian(vv, FLT_EPSILON);
          getCrossHessian(hv, FLT_EPSILON);
          for (int y = 0, k = 0; (y < height); y++) {
            for (int x = 0; (x < width); x++, k++) {
              if (floatPixelsHV[k] < 0.0) {
                floatPixels[k] =
                    (float)
                        (-0.5
                            * Math.acos(
                                (floatPixelsHH[k] - floatPixelsVV[k])
                                    / Math.sqrt(
                                        4.0 * floatPixelsHV[k] * floatPixelsHV[k]
                                            + (floatPixelsHH[k] - floatPixelsVV[k])
                                                * (floatPixelsHH[k] - floatPixelsVV[k]))));
              } else {
                floatPixels[k] =
                    (float)
                        (0.5
                            * Math.acos(
                                (floatPixelsHH[k] - floatPixelsVV[k])
                                    / Math.sqrt(
                                        4.0 * floatPixelsHV[k] * floatPixelsHV[k]
                                            + (floatPixelsHH[k] - floatPixelsVV[k])
                                                * (floatPixelsHH[k] - floatPixelsVV[k]))));
              }
            }
            stepProgressBar();
          }
        }
        break;
      default:
        throw new IllegalArgumentException("Invalid operation");
    }
    ip.resetMinAndMax();
    imp.updateAndDraw();
  } /* end doIt */