/** See how well it processes an image which is not an GrayS32 */
  @Test
  public void checkOtherType() {
    GrayS32 orig = new GrayS32(width, height);
    GImageMiscOps.fillUniform(orig, rand, 0, 20);
    GrayU8 orig8 = ConvertImage.convert(orig, (GrayU8) null);

    int N = 3;
    ImageDimension dimen = UtilWavelet.transformDimension(orig, N);

    GrayS32 found = new GrayS32(dimen.width, dimen.height);
    GrayS32 expected = new GrayS32(dimen.width, dimen.height);

    WaveletDescription<WlCoef_I32> desc =
        FactoryWaveletDaub.biorthogonal_I32(5, BorderType.REFLECT);

    GrayS32 storage = new GrayS32(dimen.width, dimen.height);
    WaveletTransformOps.transformN(desc, orig.clone(), expected, storage, N);

    WaveletTransformInt<GrayU8> alg = new WaveletTransformInt<>(desc, N, 0, 255, GrayU8.class);
    alg.transform(orig8, found);

    // see if the two techniques produced the same results
    BoofTesting.assertEquals(expected, found, 0);

    // see if it can convert it back
    GrayU8 reconstructed = new GrayU8(width, height);
    alg.invert(found, reconstructed);
    BoofTesting.assertEquals(orig8, reconstructed, 0);
    // make sure the input has not been modified
    BoofTesting.assertEquals(expected, found, 0);
  }
  @Test
  public void compareToWaveletTransformOps() {
    GrayS32 orig = new GrayS32(width, height);
    GImageMiscOps.fillUniform(orig, rand, 0, 20);
    GrayS32 origCopy = orig.clone();

    int N = 3;
    ImageDimension dimen = UtilWavelet.transformDimension(orig, N);

    GrayS32 found = new GrayS32(dimen.width, dimen.height);
    GrayS32 expected = new GrayS32(dimen.width, dimen.height);

    WaveletDescription<WlCoef_I32> desc =
        FactoryWaveletDaub.biorthogonal_I32(5, BorderType.REFLECT);

    GrayS32 storage = new GrayS32(dimen.width, dimen.height);
    WaveletTransformOps.transformN(desc, orig.clone(), expected, storage, N);

    WaveletTransformInt<GrayS32> alg = new WaveletTransformInt<>(desc, N, 0, 255, GrayS32.class);
    alg.transform(orig, found);

    // make sure the original input was not modified like it is in WaveletTransformOps
    BoofTesting.assertEquals(origCopy, orig, 0);
    // see if the two techniques produced the same results
    BoofTesting.assertEquals(expected, found, 0);

    // test inverse transform
    GrayS32 reconstructed = new GrayS32(width, height);
    alg.invert(found, reconstructed);
    BoofTesting.assertEquals(orig, reconstructed, 0);
    // make sure the input has not been modified
    BoofTesting.assertEquals(expected, found, 0);
  }
  /** The XY and YX second derivatives should be indential */
  private void testSecondDerivative(Method m1, Method m2) {
    Class params[] = m1.getParameterTypes();
    ImageGray input = GeneralizedImageOps.createSingleBand(params[0], width, height);
    ImageGray derivX = GeneralizedImageOps.createSingleBand(params[1], width, height);
    ImageGray derivY = GeneralizedImageOps.createSingleBand(params[2], width, height);
    ImageGray derivXX = GeneralizedImageOps.createSingleBand(params[1], width, height);
    ImageGray derivYY = GeneralizedImageOps.createSingleBand(params[2], width, height);
    ImageGray derivXY = GeneralizedImageOps.createSingleBand(params[1], width, height);
    ImageGray derivYX = GeneralizedImageOps.createSingleBand(params[1], width, height);

    GImageMiscOps.fillUniform(input, rand, 0, 40);

    Object border;
    if (params[3] == ImageBorder_F32.class) {
      border = new ImageBorder1D_F32(BorderIndex1D_Wrap.class);
    } else {
      border = new ImageBorder1D_S32(BorderIndex1D_Wrap.class);
    }

    try {
      m1.invoke(null, input, derivX, derivY, border);
      m2.invoke(null, derivX, derivXX, derivXY, border);
      m2.invoke(null, derivY, derivYX, derivYY, border);
    } catch (IllegalAccessException | InvocationTargetException e) {
      throw new RuntimeException(e);
    }

    //		BoofTesting.printDiff(derivXY,derivYX);
    BoofTesting.assertEquals(derivXY, derivYX, 1e-3f);
  }
  public static void assertEquals(ImageBase imgA, ImageBase imgB, double tol) {

    // if no specialized check exists, use a slower generalized approach
    if (imgA instanceof ImageSingleBand) {
      GImageSingleBand a = FactoryGImageSingleBand.wrap((ImageSingleBand) imgA);
      GImageSingleBand b = FactoryGImageSingleBand.wrap((ImageSingleBand) imgB);

      for (int y = 0; y < imgA.height; y++) {
        for (int x = 0; x < imgA.width; x++) {
          double valA = a.get(x, y).doubleValue();
          double valB = b.get(x, y).doubleValue();

          double difference = valA - valB;
          if (Math.abs(difference) > tol)
            throw new RuntimeException(
                "Values not equal at (" + x + "," + y + ") " + valA + "  " + valB);
        }
      }
    } else if (imgA instanceof MultiSpectral) {
      MultiSpectral a = (MultiSpectral) imgA;
      MultiSpectral b = (MultiSpectral) imgB;

      if (a.getNumBands() != b.getNumBands())
        throw new RuntimeException("Number of bands not equal");

      for (int band = 0; band < a.getNumBands(); band++) {
        assertEquals(a.getBand(band), b.getBand(band), tol);
      }
    } else if (imgA instanceof ImageInterleaved) {
      ImageInterleaved a = (ImageInterleaved) imgA;
      ImageInterleaved b = (ImageInterleaved) imgB;

      if (a.getNumBands() != b.getNumBands())
        throw new RuntimeException("Number of bands not equal");

      int numBands = a.getNumBands();

      for (int y = 0; y < imgA.height; y++) {
        for (int x = 0; x < imgA.width; x++) {
          for (int band = 0; band < numBands; band++) {
            double valA = GeneralizedImageOps.get(a, x, y, band);
            double valB = GeneralizedImageOps.get(b, x, y, band);

            double difference = valA - valB;
            if (Math.abs(difference) > tol)
              throw new RuntimeException(
                  "Values not equal at (" + x + "," + y + ") " + valA + "  " + valB);
          }
        }
      }

    } else {
      throw new RuntimeException("Unknown image type");
    }
  }
  // TODO make sure pixels outside are not modified of sub-matrix
  // todo have the submatrices be from different shaped inputs
  @SuppressWarnings({"unchecked"})
  public static void checkSubImage(
      Object testClass, String function, boolean checkEquals, Object... inputParam) {
    try {
      ImageBase[] larger = new ImageBase[inputParam.length];
      ImageBase[] subImg = new ImageBase[inputParam.length];
      Class<?> paramDesc[] = new Class<?>[inputParam.length];
      Object[] inputModified = new Object[inputParam.length];

      for (int i = 0; i < inputParam.length; i++) {
        if (ImageBase.class.isAssignableFrom(inputParam[i].getClass())) {
          ImageBase<?> img = (ImageBase<?>) inputParam[i];

          // copy the original image inside of a larger image
          larger[i] = img._createNew(img.getWidth() + 10, img.getHeight() + 12);
          // extract a sub-image and make it equivalent to the original image.
          subImg[i] = larger[i].subimage(5, 6, 5 + img.getWidth(), 6 + img.getHeight(), null);
          subImg[i].setTo(img);
        }

        // the first time it is called use the original inputs
        inputModified[i] = inputParam[i];
        paramDesc[i] = inputParam[i].getClass();
      }

      // first try it with the original image
      Method m = findMethod(testClass.getClass(), function, paramDesc);

      m.invoke(testClass, inputModified);

      // now try it with the sub-image
      for (int i = 0; i < inputModified.length; i++) {
        if (subImg[i] != null) inputModified[i] = subImg[i];
      }
      m.invoke(testClass, inputModified);

      // the result should be the identical
      if (checkEquals) {
        for (int i = 0; i < inputParam.length; i++) {
          if (subImg[i] == null) continue;
          assertEquals((ImageBase) inputModified[i], subImg[i], 0);
        }
      }

    } catch (InvocationTargetException e) {
      throw new RuntimeException(e);
    } catch (IllegalAccessException e) {
      throw new RuntimeException(e);
    }
  }
  private void compareToNaive(
      Image left, Image right, int minDisparity, int maxDisparity, int radiusX, int radiusY) {
    int w = left.width;
    int h = left.height;

    DisparityScoreSadRect<Image, Disparity> alg =
        createAlg(minDisparity, maxDisparity, radiusX, radiusY, compDisp);
    StereoDisparityWtoNaive<Image> naive =
        new StereoDisparityWtoNaive<Image>(minDisparity, maxDisparity, radiusX, radiusY);

    Disparity found = GeneralizedImageOps.createSingleBand(disparityType, w, h);
    ImageFloat32 expected = new ImageFloat32(w, h);

    alg.process(left, right, found);
    naive.process(left, right, expected);

    BoofTesting.assertEquals(found, expected, 1);
  }