All Downloads are FREE. Search and download functionalities are using the official Maven repository.

boofcv.BoofTesting Maven / Gradle / Ivy

/*
 * Copyright (c) 2021, Peter Abeles. All Rights Reserved.
 *
 * This file is part of BoofCV (http://boofcv.org).
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package boofcv;

import boofcv.core.image.*;
import boofcv.struct.image.*;
import georegression.geometry.ConvertRotation3D_F64;
import georegression.struct.se.Se3_F64;
import georegression.struct.so.Rodrigues_F64;
import org.ejml.UtilEjml;
import org.ejml.data.DMatrixRMaj;
import org.ejml.dense.row.CommonOps_DDRM;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;

// lint:forbidden disable_check for_each

/**
 * Functions to aid in unit testing code for correctly handling sub-images
 *
 * @author Peter Abeles
 */
// todo remove all comwapare with border functions and use sub-images instead
@SuppressWarnings("ALL")
public class BoofTesting {

	public static final long BASE_SEED = 0xBEEF;

	/**
	 * Creates Random but using a fixed seed with an offset. The idea if we want to test to see if a test is brittle
	 * we can do that across the project by changing the base seed.
	 *
	 * @param offset Value added to base seed.
	 * @return Random
	 */
	public static Random createRandom( long offset ) {
		return new Random(0xBEEF + offset);
	}

	/**
	 * Recommended tolerance by image data type. Useful when processing images but the type isn't known at compile
	 * time.
	 */
	public static double tolerance( ImageDataType type ) {
		double tol = UtilEjml.TEST_F64;
		if (!type.isInteger() && type.getNumBits() == 32) {
			tol = UtilEjml.TEST_F32;
		}
		return tol;
	}

	public static  T convertToGenericType( Class type ) {
		if (type == GrayS8.class || type == GrayU8.class)
			return (T)GrayI8.class;
		if (type == GrayS16.class || type == GrayU16.class)
			return (T)GrayI16.class;
		if (type == InterleavedS8.class || type == InterleavedU8.class)
			return (T)InterleavedI8.class;
		if (type == InterleavedS16.class || type == InterleavedU16.class)
			return (T)InterleavedI16.class;
		return (T)type;
	}

	public static ImageDataType convertToGenericType( ImageDataType type ) {
		if (type.isInteger()) {
			if (type.getNumBits() == 8)
				return ImageDataType.I8;
			else if (type.getNumBits() == 16)
				return ImageDataType.I16;
		}

		return type;
	}

	/**
	 * Checks to see if the two transforms are equal up to a scale factor. Rotation is tested using a single angle
	 * and translation using magnitude. NOTE: The direction of the translation is NOT considered part of the
	 * sign ambiguity in this came.
	 *
	 * @param expected the expected transform
	 * @param found the found transform
	 * @param tolAngle radians
	 * @param tolT fractional error tolernace
	 */
	public static void assertEqualsToScaleS( Se3_F64 expected, Se3_F64 found, double tolAngle, double tolT ) {
		var R = new DMatrixRMaj(3, 3);
		CommonOps_DDRM.multTransA(expected.R, found.R, R);
		Rodrigues_F64 rod = ConvertRotation3D_F64.matrixToRodrigues(R, null);
		assertEquals(0.0, rod.theta, tolAngle);

		double normFound = found.T.norm();
		double normExpected = expected.T.norm();

		if (normExpected == 0.0 || normFound == 0.0) {
			assertEquals(0.0, normFound, tolT);
			return;
		}

		// Normalize so that both have a norm of 1
		double dx = expected.T.x/normExpected - found.T.x/normFound;
		double dy = expected.T.y/normExpected - found.T.y/normFound;
		double dz = expected.T.z/normExpected - found.T.z/normFound;
		double r = Math.sqrt(dx*dx + dy*dy + dz*dz);
		assertEquals(0.0, r, tolT, "E=" + expected.T + " F=" + found.T);
	}

	/**
	 * Checks to see if the two transforms are equal up to a scale factor. Rotation is tested using a single angle
	 * and translation using magnitude. NOTE: Scale ambiguity also includes sign ambiguity for translation.
	 *
	 * @param expected the expected transform
	 * @param found the found transform
	 * @param tolAngle radians
	 * @param tolT fractional error tolernace
	 */
	public static void assertEqualsToScale( Se3_F64 expected, Se3_F64 found, double tolAngle, double tolT ) {
		var R = new DMatrixRMaj(3, 3);
		CommonOps_DDRM.multTransA(expected.R, found.R, R);
		Rodrigues_F64 rod = ConvertRotation3D_F64.matrixToRodrigues(R, null);
		assertEquals(0.0, rod.theta, tolAngle);

		double normFound = found.T.norm();
		double normExpected = expected.T.norm();

		if (normFound == 0.0 || normExpected == 0.0) {
			assertEquals(0.0, normFound, tolT);
			return;
		}

		int largestIdx = -1;
		double largestMag = 0;
		for (int i = 0; i < 3; i++) {
			double mag = Math.abs(expected.T.getIdx(i));
			if (mag > largestMag) {
				largestMag = mag;
				largestIdx = i;
			}
		}
		if (Math.signum(expected.T.getIdx(largestIdx)) != Math.signum(found.T.getIdx(largestIdx)))
			normFound *= -1;

		// they will have the same scale and a norm of 1
		double dx = expected.T.x/normExpected - found.T.x/normFound;
		double dy = expected.T.y/normExpected - found.T.y/normFound;
		double dz = expected.T.z/normExpected - found.T.z/normFound;
		double r = Math.sqrt(dx*dx + dy*dy + dz*dz);
		assertEquals(0.0, r, tolT, "E=" + expected.T + " F=" + found.T);
	}

	public static void assertEquals( Se3_F64 expected, Se3_F64 found, double tolAngle, double tolT ) {
		var R = new DMatrixRMaj(3, 3);
		CommonOps_DDRM.multTransA(expected.R, found.R, R);
		Rodrigues_F64 rod = ConvertRotation3D_F64.matrixToRodrigues(R, null);
		assertEquals(0.0, rod.theta, tolAngle);

		assertEquals(0.0, found.T.distance(expected.T), tolT);
	}

	private static void assertEquals( double expected, double found, double tol ) {
		if (Math.abs(expected - found) > tol)
			throw new RuntimeException("Error too large. expected=" + expected + " found=" + found + " tol=" + tol);
	}

	private static void assertEquals( double expected, double found, double tol, String message ) {
		if (Math.abs(expected - found) > tol)
			throw new RuntimeException("Error too large. expected=" + expected + " found=" + found + " tol=" + tol + "\n" + message);
	}

	/**
	 * 

* Returns an image which is a sub-image but contains the same values of the input image. Use for * testing compliance with sub-images. The subimage is created by creating a larger image, * copying over the input image into the inner portion, then creating a subimage of the copied part. *

*/ @SuppressWarnings({"unchecked"}) public static > T createSubImageOf( T input ) { if (input instanceof ImageGray) { return (T)createSubImageOf_S((ImageGray)input); } else if (input instanceof Planar) { return (T)createSubImageOf_PL((Planar)input); } else if (input instanceof ImageInterleaved) { return (T)createSubImageOf_I((ImageInterleaved)input); } else { throw new IllegalArgumentException("Add support for this image type"); } } public static > T createSubImageOf_S( T input ) { // create the larger image T ret = (T)input.createNew(input.width + 10, input.height + 12); // create a sub-image of the inner portion ret = (T)ret.subimage(5, 7, input.width + 5, input.height + 7, null); // copy input image into the subimage ret.setTo(input); return ret; } public static > T createSubImageOf_I( T input ) { // create the larger image T ret = (T)input.createNew(input.width + 10, input.height + 12); // create a sub-image of the inner portion ret = (T)ret.subimage(5, 7, input.width + 5, input.height + 7, null); // copy input image into the subimage ret.setTo(input); return ret; } public static T createSubImageOf_PL( T input ) { T ret = (T)new Planar(input.type, input.width, input.height, input.getNumBands()); for (int i = 0; i < input.getNumBands(); i++) { ret.bands[i] = createSubImageOf_S(input.getBand(i)); } ret.stride = ret.bands[0].stride; ret.startIndex = ret.bands[0].startIndex; return ret; } /** * Searches for functions that accept only images and makes sure they only accept * images which have he same width and height. * * @param testClass Instance of the class being tested */ public static void checkImageDimensionValidation( Object testClass, int numFunctions ) { int count = 0; Method methods[] = testClass.getClass().getMethods(); for (Method m : methods) { // see if the inputs are all images if (!areAllInputsImages(m)) continue; // test a positive case Class params[] = m.getParameterTypes(); Object[] inputs = new Object[params.length]; for (int i = 0; i < params.length; i++) { inputs[i] = GeneralizedImageOps.createSingleBand(params[i], 10, 20); } try { m.invoke(testClass, inputs); } catch (IllegalAccessException | InvocationTargetException e) { throw new RuntimeException(e); } // test negative cases for (int target = 0; target < params.length; target++) { for (int i = 0; i < params.length; i++) { if (i != target) inputs[i] = GeneralizedImageOps.createSingleBand(params[i], 10, 20); else inputs[i] = GeneralizedImageOps.createSingleBand(params[i], 11, 22); } try { m.invoke(testClass, inputs); throw new RuntimeException("Expected an exception here"); } catch (InvocationTargetException e) { if (e.getTargetException().getClass() != IllegalArgumentException.class) throw new RuntimeException(e); } catch (IllegalAccessException e) { throw new RuntimeException(e); } } count++; } if (count != numFunctions) throw new RuntimeException("Unexpected number of functions"); } /** * Searches for functions that accept only images and makes sure they only accept * images which have he same width and height. * * @param testClass Instance of the class being tested */ public static void checkImageDimensionReshape( Object testClass, int numFunctions ) { int count = 0; Method methods[] = testClass.getClass().getMethods(); for (Method m : methods) { // see if the inputs are all images if (!areAllInputsImages(m)) continue; // test a positive case Class params[] = m.getParameterTypes(); Object[] inputs = new Object[params.length]; for (int i = 0; i < params.length; i++) { inputs[i] = GeneralizedImageOps.createSingleBand(params[i], 10, 20); } try { m.invoke(testClass, inputs); } catch (IllegalAccessException | InvocationTargetException e) { throw new RuntimeException(e); } // test negative cases // skip input which will be fixed size for (int target = 1; target < params.length; target++) { for (int i = 0; i < params.length; i++) { if (i != target) inputs[i] = GeneralizedImageOps.createSingleBand(params[i], 10, 20); else inputs[i] = GeneralizedImageOps.createSingleBand(params[i], 11, 22); } try { m.invoke(testClass, inputs); } catch (IllegalAccessException | InvocationTargetException e) { throw new RuntimeException(e); } for (int i = 1; i < params.length; i++) { if (10 != ((ImageBase)inputs[i]).width || 20 != ((ImageBase)inputs[i]).height) throw new RuntimeException("Wasn't reshaped"); } } count++; } if (count != numFunctions) throw new RuntimeException("Unexpected number of functions. cnt=" + count + " funcs=" + numFunctions); } private static boolean areAllInputsImages( Method m ) { Class params[] = m.getParameterTypes(); if (params.length == 0) return false; for (Class p : params) { if (!ImageGray.class.isAssignableFrom(p)) { return false; } } return true; } /** * Tests the specified function with the original image provided and with an equivalent * sub-image. The two results are then compared. The function being tested must only * have one input parameter of type {@link GrayU8}. * * @param testClass Instance of the class that contains the function being tested. * @param function The name of the function being tested. * @param checkEquals Checks to see if the two images have been modified the same way on output * @param inputParam The original input parameters */ // TODO make sure pixels outside are not modified of sub-matrix // todo have the submatrices be from different shaped inputs 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 | IllegalAccessException e) { throw new RuntimeException(e); } } /** * Searches for a function which is a perfect match. if none it exists it checks * to see if any matches that could accept an input of the specified type. If there * is only one such match that is returned. */ public static Method findMethod( Class type, String name, Class... params ) { Method methods[] = type.getMethods(); List found = new ArrayList<>(); for (Method m : methods) { if (m.getName().compareTo(name) != 0) continue; Class a[] = m.getParameterTypes(); if (a.length != params.length) continue; boolean match = true; for (int i = 0; i < a.length; i++) { if (a[i] != params[i]) { match = false; break; } } if (match) { // its a perfect match return m; } // see if it could be called with these parameters match = true; for (int i = 0; i < a.length; i++) { if (params[i] == a[i]) continue; if (a[i].isPrimitive()) { if (a[i] == Boolean.TYPE && params[i] == Boolean.class) continue; if (a[i] == Byte.TYPE && params[i] == Byte.class) continue; if (a[i] == Short.TYPE && params[i] == Short.class) continue; if (a[i] == Integer.TYPE && params[i] == Integer.class) continue; if (a[i] == Long.TYPE && params[i] == Long.class) continue; if (a[i] == Float.TYPE && params[i] == Float.class) continue; if (a[i] == Double.TYPE && params[i] == Double.class) continue; } if (!a[i].isAssignableFrom(params[i])) { match = false; break; } } if (match) { found.add(m); } } if (found.size() == 1) { return found.get(0); } throw new RuntimeException("Couldn't find matching *public* function to " + name); } /** * Looks up the static method then passes in the specified inputs. */ public static void callStaticMethod( Class classType, String name, Object... inputs ) { Class params[] = new Class[inputs.length]; for (int i = 0; i < inputs.length; i++) { params[i] = inputs[i].getClass(); } Method m = findMethod(classType, name, params); if (m == null) { for (int i = 0; i < inputs.length; i++) { if (params[i] == Integer.class) { params[i] = int.class; } else if (params[i] == Float.class) { params[i] = float.class; } else if (params[i] == Double.class) { params[i] = double.class; } } m = findMethod(classType, name, params); } if (m == null) throw new IllegalArgumentException("Method not found"); try { m.invoke(null, inputs); } catch (IllegalAccessException | InvocationTargetException e) { throw new RuntimeException(e); } } /** * Searches for all functions with the specified name in the target class. Once it finds * that function it invokes the specified function in the owner class. That function must * take in a Method as its one and only parameter. The method will be one of the matching * ones in the target class. * * @return The number of times 'targetMethod' was found and called. */ public static int findMethodThenCall( Object owner, String ownerMethod, Class target, String targetMethod ) { int total = 0; Method[] list = target.getMethods(); try { Method om = owner.getClass().getMethod(ownerMethod, Method.class); for (Method m : list) { if (!m.getName().equals(targetMethod)) continue; om.invoke(owner, m); total++; } } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { throw new RuntimeException(e); } return total; } public static void assertEquals( double a[], double b[], double tol ) { for (int i = 0; i < a.length; i++) { double diff = Math.abs(a[i] - b[i]); if (diff > tol) throw new RuntimeException("Element " + i + " not equals. " + a[i] + " " + b[i]); } } public static void assertEquals( double a[], float b[], double tol ) { for (int i = 0; i < a.length; i++) { double diff = Math.abs(a[i] - b[i]); if (diff > tol) throw new RuntimeException("Element " + i + " not equals. " + a[i] + " " + b[i]); } } public static void assertEquals( double a[], int b[] ) { for (int i = 0; i < a.length; i++) { double diff = Math.abs((int)a[i] - b[i]); if (diff != 0) throw new RuntimeException("Element " + i + " not equals. " + a[i] + " " + b[i]); } } public static void assertEquals( byte a[], byte b[] ) { for (int i = 0; i < a.length; i++) { int diff = Math.abs(a[i] - b[i]); if (diff != 0) throw new RuntimeException("Element " + i + " not equals. " + a[i] + " " + b[i]); } } public static void assertEquals( short a[], short b[] ) { for (int i = 0; i < a.length; i++) { int diff = Math.abs(a[i] - b[i]); if (diff != 0) throw new RuntimeException("Element " + i + " not equals. " + a[i] + " " + b[i]); } } public static void assertEquals( int a[], int b[] ) { for (int i = 0; i < a.length; i++) { int diff = Math.abs(a[i] - b[i]); if (diff != 0) throw new RuntimeException("Element " + i + " not equals. " + a[i] + " " + b[i]); } } public static void assertEquals( long a[], long b[] ) { for (int i = 0; i < a.length; i++) { long diff = Math.abs(a[i] - b[i]); if (diff != 0) throw new RuntimeException("Element " + i + " not equals. " + a[i] + " " + b[i]); } } public static void assertEquals( float a[], float b[], float tol ) { for (int i = 0; i < a.length; i++) { double diff = Math.abs(a[i] - b[i]); if (diff > tol) throw new RuntimeException("Element " + i + " not equals. " + a[i] + " " + b[i]); } } public static void assertEquals( ImageBase imgA, ImageBase imgB, double tol ) { // if no specialized check exists, use a slower generalized approach if (imgA instanceof ImageGray) { GImageGray a = FactoryGImageGray.wrap((ImageGray)imgA); GImageGray b = FactoryGImageGray.wrap((ImageGray)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 Planar && imgB instanceof Planar) { Planar a = (Planar)imgA; Planar b = (Planar)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 ImageMultiBand && imgB instanceof ImageMultiBand) { ImageMultiBand a = (ImageMultiBand)imgA; ImageMultiBand b = (ImageMultiBand)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"); } } public static void assertEqualsInner( ImageBase imgA, ImageBase imgB, double tol, int borderX, int borderY, boolean relative ) { // if no specialized check exists, use a slower generalized approach if (imgA instanceof ImageGray) { GImageGray a = FactoryGImageGray.wrap((ImageGray)imgA); GImageGray b = FactoryGImageGray.wrap((ImageGray)imgB); for (int y = borderY; y < imgA.height - borderY; y++) { for (int x = borderX; x < imgA.width - borderX; x++) { double valA = a.get(x, y).doubleValue(); double valB = b.get(x, y).doubleValue(); double error = Math.abs(valA - valB); if (relative) { double denominator = Math.abs(valA) + Math.abs(valB); if (denominator == 0) denominator = 1; error /= denominator; } if (error > tol) throw new RuntimeException("Values not equal at (" + x + "," + y + ") " + valA + " " + valB); } } } else if (imgA instanceof Planar) { Planar a = (Planar)imgA; Planar b = (Planar)imgB; if (a.getNumBands() != b.getNumBands()) throw new RuntimeException("Number of bands not equal"); for (int band = 0; band < a.getNumBands(); band++) { assertEqualsInner(a.getBand(band), b.getBand(band), tol, borderX, borderY, relative); } } else { throw new RuntimeException("Unknown image type"); } } public static void assertEqualsInner( ImageBase imgA, ImageBase imgB, double tol, int borderX0, int borderY0, int borderX1, int borderY1, boolean relative ) { // if no specialized check exists, use a slower generalized approach if (imgA instanceof ImageGray) { GImageGray a = FactoryGImageGray.wrap((ImageGray)imgA); GImageGray b = FactoryGImageGray.wrap((ImageGray)imgB); for (int y = borderY0; y < imgA.height - borderY1; y++) { for (int x = borderX0; x < imgA.width - borderX1; x++) { double valA = a.get(x, y).doubleValue(); double valB = b.get(x, y).doubleValue(); double error = Math.abs(valA - valB); if (relative) { double denominator = Math.abs(valA) + Math.abs(valB); if (denominator == 0) denominator = 1; error /= denominator; } if (error > tol) throw new RuntimeException("Values not equal at (" + x + "," + y + ") " + valA + " " + valB); } } } else if (imgA instanceof ImageInterleaved) { GImageMultiBand a = FactoryGImageMultiBand.wrap((ImageInterleaved)imgA); GImageMultiBand b = FactoryGImageMultiBand.wrap((ImageInterleaved)imgB); int numBands = a.getNumberOfBands(); for (int y = borderY0; y < imgA.height - borderY1; y++) { for (int x = borderX0; x < imgA.width - borderX1; x++) { for (int band = 0; band < numBands; band++) { double valA = a.get(x, y, band).doubleValue(); double valB = b.get(x, y, band).doubleValue(); double error = Math.abs(valA - valB); if (relative) { double denominator = Math.abs(valA) + Math.abs(valB); if (denominator == 0) denominator = 1; error /= denominator; } if (error > tol) throw new RuntimeException("Values not equal at (" + x + "," + y + "," + band + ") " + valA + " " + valB); } } } } else if (imgA instanceof Planar) { Planar a = (Planar)imgA; Planar b = (Planar)imgB; if (a.getNumBands() != b.getNumBands()) throw new RuntimeException("Number of bands not equal"); for (int band = 0; band < a.getNumBands(); band++) { assertEqualsInner(a.getBand(band), b.getBand(band), tol, borderX0, borderY0, borderX1, borderY1, relative); } } else { throw new RuntimeException("Unknown image type"); } } public static void assertEqualsRelative( ImageBase imgA, ImageBase imgB, double tolFrac ) { // if no specialized check exists, use a slower generalized approach if (imgA instanceof ImageGray) { GImageGray a = FactoryGImageGray.wrap((ImageGray)imgA); GImageGray b = FactoryGImageGray.wrap((ImageGray)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; double max = Math.max(Math.abs(valA), Math.abs(valB)); if (max == 0) max = 1; if (Math.abs(difference)/max > tolFrac) throw new RuntimeException("Values not equal at (" + x + "," + y + ") " + valA + " " + valB); } } } else if (imgA instanceof ImageInterleaved) { GImageMultiBand a = FactoryGImageMultiBand.wrap(imgA); GImageMultiBand b = FactoryGImageMultiBand.wrap(imgB); float valueA[] = new float[a.getNumberOfBands()]; float valueB[] = new float[b.getNumberOfBands()]; for (int y = 0; y < imgA.height; y++) { for (int x = 0; x < imgA.width; x++) { a.get(x, y, valueA); b.get(x, y, valueB); for (int i = 0; i < a.getNumberOfBands(); i++) { double valA = valueA[i]; double valB = valueB[i]; double difference = valA - valB; double max = Math.max(Math.abs(valA), Math.abs(valB)); if (max == 0) max = 1; if (Math.abs(difference)/max > tolFrac) throw new RuntimeException("Values not equal at (" + x + "," + y + ") " + valA + " " + valB); } } } } else if (imgA instanceof Planar) { Planar a = (Planar)imgA; Planar b = (Planar)imgB; if (a.getNumBands() != b.getNumBands()) throw new RuntimeException("Number of bands not equal"); for (int band = 0; band < a.getNumBands(); band++) { assertEqualsRelative(a.getBand(band), b.getBand(band), tolFrac); } } else { throw new RuntimeException("Unknown image type"); } } public static void assertEqualsBorder( ImageBase imgA, ImageBase imgB, double tol, int borderX, int borderY ) { if (imgA instanceof ImageGray) { assertEqualsBorder((ImageGray)imgA, (ImageGray)imgB, tol, borderX, borderY); } else if (imgA instanceof ImageInterleaved) { assertEqualsBorder((ImageInterleaved)imgA, (ImageInterleaved)imgB, tol, borderX, borderY); } else if (imgA instanceof Planar) { Planar _imgA_ = (Planar)imgA; Planar _imgB_ = (Planar)imgB; final int numBands = _imgA_.getNumBands(); for (int band = 0; band < numBands; band++) { assertEqualsBorder(_imgA_.getBand(band), _imgB_.getBand(band), tol, borderX, borderY); } } else { throw new RuntimeException("Unsupported image type"); } } /** * Checks to see if only the image borders are equal to each other within tolerance */ public static void assertEqualsBorder( ImageGray imgA, ImageGray imgB, double tol, int borderX, int borderY ) { if (imgA.getWidth() != imgB.getWidth()) throw new RuntimeException("Widths are not equals"); if (imgA.getHeight() != imgB.getHeight()) throw new RuntimeException("Heights are not equals"); GImageGray a = FactoryGImageGray.wrap(imgA); GImageGray b = FactoryGImageGray.wrap(imgB); for (int y = 0; y < imgA.getHeight(); y++) { for (int x = 0; x < borderX; x++) { compareValues(tol, a, b, x, y); } for (int x = imgA.getWidth() - borderX; x < imgA.getWidth(); x++) { compareValues(tol, a, b, x, y); } } for (int x = borderX; x < imgA.getWidth() - borderX; x++) { for (int y = 0; y < borderY; y++) { compareValues(tol, a, b, x, y); } for (int y = imgA.getHeight() - borderY; y < imgA.getHeight(); y++) { compareValues(tol, a, b, x, y); } } } /** * Checks to see if only the image borders are equal to each other within tolerance */ public static void assertEqualsBorder( ImageInterleaved imgA, ImageInterleaved imgB, double tol, int borderX, int borderY ) { if (imgA.getWidth() != imgB.getWidth()) throw new RuntimeException("Widths are not equals"); if (imgA.getHeight() != imgB.getHeight()) throw new RuntimeException("Heights are not equals"); int numBands = imgA.numBands; GImageMultiBand a = FactoryGImageMultiBand.wrap(imgA); GImageMultiBand b = FactoryGImageMultiBand.wrap(imgB); for (int y = 0; y < imgA.getHeight(); y++) { for (int x = 0; x < borderX; x++) { for (int band = 0; band < numBands; band++) { compareValues(tol, a, b, x, y, band); } } for (int x = imgA.getWidth() - borderX; x < imgA.getWidth(); x++) { for (int band = 0; band < numBands; band++) { compareValues(tol, a, b, x, y, band); } } } for (int x = borderX; x < imgA.getWidth() - borderX; x++) { for (int y = 0; y < borderY; y++) { for (int band = 0; band < numBands; band++) { compareValues(tol, a, b, x, y, band); } } for (int y = imgA.getHeight() - borderY; y < imgA.getHeight(); y++) { for (int band = 0; band < numBands; band++) { compareValues(tol, a, b, x, y, band); } } } } private static void compareValues( double tol, GImageGray a, GImageGray b, int x, int y ) { double normalizer = Math.abs(a.get(x, y).doubleValue()) + Math.abs(b.get(x, y).doubleValue()); if (normalizer < 1.0) normalizer = 1.0; if (Math.abs(a.get(x, y).doubleValue() - b.get(x, y).doubleValue())/normalizer > tol) throw new RuntimeException("values not equal at (" + x + " " + y + ") " + a.get(x, y) + " " + b.get(x, y)); } private static void compareValues( double tol, GImageMultiBand a, GImageMultiBand b, int x, int y, int band ) { double normalizer = Math.abs(a.get(x, y, band).doubleValue()) + Math.abs(b.get(x, y, band).doubleValue()); if (normalizer < 1.0) normalizer = 1.0; if (Math.abs(a.get(x, y, band).doubleValue() - b.get(x, y, band).doubleValue())/normalizer > tol) throw new RuntimeException("values not equal at (" + x + " " + y + " " + band + ") " + a.get(x, y, band) + " " + b.get(x, y, band)); } public static void checkBorderZero( ImageGray outputImage, int border ) { GImageGray img = FactoryGImageGray.wrap(outputImage); for (int y = 0; y < img.getHeight(); y++) { if (y >= border && y < img.getHeight() - border) continue; for (int x = 0; x < img.getWidth(); x++) { if (x >= border && x < img.getWidth() - border) continue; if (img.get(x, y).intValue() != 0) throw new RuntimeException("The border is not zero: " + x + " " + y); } } } public static void checkBorderZero( ImageGray outputImage, int borderX0, int borderY0, int borderX1, int borderY1 ) { GImageGray img = FactoryGImageGray.wrap(outputImage); for (int y = 0; y < img.getHeight(); y++) { if (y >= borderY0 && y < img.getHeight() - borderY1) continue; for (int x = 0; x < img.getWidth(); x++) { if (x >= borderX0 && x < img.getWidth() - borderX1) continue; if (img.get(x, y).intValue() != 0) throw new RuntimeException("The border is not zero: " + x + " " + y); } } } public static void printDiff( ImageGray imgA, ImageGray imgB ) { GImageGray a = FactoryGImageGray.wrap(imgA); GImageGray b = FactoryGImageGray.wrap(imgB); System.out.println("------- Difference -----------"); for (int y = 0; y < imgA.getHeight(); y++) { for (int x = 0; x < imgA.getWidth(); x++) { double diff = Math.abs(a.get(x, y).doubleValue() - b.get(x, y).doubleValue()); System.out.printf("%2d ", (int)diff); } System.out.println(); } } public static void printDiffBinary( GrayU8 imgA, GrayU8 imgB ) { System.out.println("------- Difference -----------"); for (int y = 0; y < imgA.getHeight(); y++) { for (int x = 0; x < imgA.getWidth(); x++) { if (imgA.unsafe_get(x, y) != imgB.unsafe_get(x, y)) System.out.print(" x"); else System.out.print(" ."); } System.out.println(); } } public static Object randomArray( Class type, int length, Random rand ) { Object ret; if (type == byte[].class) { byte[] data = new byte[length]; for (int i = 0; i < length; i++) { data[i] = (byte)(rand.nextInt(Byte.MAX_VALUE - Byte.MIN_VALUE) + Byte.MIN_VALUE); } ret = data; } else if (type == short[].class) { short[] data = new short[length]; for (int i = 0; i < length; i++) { data[i] = (short)(rand.nextInt(Short.MAX_VALUE - Short.MIN_VALUE) + Short.MIN_VALUE); } ret = data; } else if (type == int[].class) { int[] data = new int[length]; for (int i = 0; i < length; i++) { data[i] = rand.nextInt(1000) - 500; } ret = data; } else if (type == long[].class) { long[] data = new long[length]; for (int i = 0; i < length; i++) { data[i] = rand.nextLong(); } ret = data; } else if (type == float[].class) { float[] data = new float[length]; for (int i = 0; i < length; i++) { data[i] = rand.nextFloat() - 0.5f; } ret = data; } else if (type == double[].class) { double[] data = new double[length]; for (int i = 0; i < length; i++) { data[i] = rand.nextDouble() - 0.5; } ret = data; } else { throw new RuntimeException("Unknown. " + type.getSimpleName()); } return ret; } public static ImageDataType pritiveToImageDataType( Class type ) { if (type == byte[].class || type == byte.class) { return ImageDataType.I8; } else if (type == short[].class || type == short.class) { return ImageDataType.I16; } else if (type == int[].class || type == int.class) { return ImageDataType.S32; } else if (type == long[].class || type == long.class) { return ImageDataType.S64; } else if (type == float[].class || type == float.class) { return ImageDataType.F32; } else if (type == double[].class || type == double.class) { return ImageDataType.F64; } else { throw new RuntimeException("UNknown type"); } } public static Object primitive( Object v, Class type ) { Number value = (Number)v; if (type == byte.class) { return value.byteValue(); } else if (type == short.class) { return value.shortValue(); } else if (type == int.class) { return value.intValue(); } else if (type == long.class) { return value.longValue(); } else if (type == float.class) { return value.floatValue(); } else if (type == double.class) { return value.doubleValue(); } else { throw new RuntimeException("Unknown. " + type.getSimpleName()); } } public static Object createInstance( Class type ) { try { return type.getConstructor().newInstance(); } catch (InstantiationException | IllegalAccessException | NoSuchMethodException | InvocationTargetException e) { throw new RuntimeException(e); } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy