ImageUtils { public static Offset findDominantOffset(BufferedImage expectedImage, BufferedImage actualImage, double diffThreshold) { int STEP = 5; int maxOffset = 10; int expectedWidth = expectedImage.getWidth(), expectedHeight = expectedImage.getHeight(); int actualWidth = actualImage.getWidth(), actualHeight = actualImage.getHeight(); int subWidth = Math.min(expectedWidth, actualWidth), subHeight = Math.min(expectedHeight, actualHeight); int xMax = Math.abs(expectedWidth - actualWidth), yMax = Math.abs(expectedHeight - actualHeight); xMax = Math.min(xMax, maxOffset); yMax = Math.min(yMax, maxOffset); int sizeRelationType = getSizeRelationType(expectedWidth, expectedHeight, actualWidth, actualHeight); int expectedXOffset = 0, expectedYOffset = 0, actualXOffset = 0, actualYOffset = 0; switch (sizeRelationType) { case 1: actualXOffset = 1; actualYOffset = 1; break; case 2: expectedXOffset = 1; expectedYOffset = 1; break; case 3: expectedXOffset = 1; actualYOffset = 1; break; case 4: expectedYOffset = 1; actualXOffset = 1; break; } int[] expectedColors = new int[expectedWidth * expectedHeight]; int[] actualColors = new int[actualWidth * actualHeight]; expectedImage.getRGB(0, 0, expectedWidth, expectedHeight, expectedColors, 0, expectedWidth); actualImage.getRGB(0, 0, actualWidth, actualHeight, actualColors, 0, actualWidth); int[] expectedRed = new int[expectedColors.length]; int[] expectedGreen = new int[expectedColors.length]; int[] expectedBlue = new int[expectedColors.length]; int[] actualRed = new int[actualColors.length]; int[] actualGreen = new int[actualColors.length]; int[] actualBlue = new int[actualColors.length]; for (int i = 0; i < expectedColors.length; i++) { Color expectedColor = new Color(expectedColors[i]); expectedRed[i] = expectedColor.getRed(); expectedGreen[i] = expectedColor.getGreen(); expectedBlue[i] = expectedColor.getBlue(); } for (int i = 0; i < actualColors.length; i++) { Color actualColor = new Color(actualColors[i]); actualRed[i] = actualColor.getRed(); actualGreen[i] = actualColor.getGreen(); actualBlue[i] = actualColor.getBlue(); } int r, g, b, bestX = 0, bestY = 0; int thresDiffCount, thresDiffMin = -1; if (sizeRelationType == 1 || sizeRelationType == 2) { for (int y = 0; y <= yMax; y++) { for (int x = 0; x <= xMax; x++) { thresDiffCount = 0; for (int i = 0; i < subHeight; i = i + STEP) { for (int j = 0; j < subWidth; j = j + STEP) { r = expectedRed[expectedWidth * (i + y * expectedYOffset) + (j + x * expectedXOffset)] - actualRed[actualWidth * (i + y * actualYOffset) + (j + x * actualXOffset)]; g = expectedGreen[expectedWidth * (i + y * expectedYOffset) + (j + x * expectedXOffset)] - actualGreen[actualWidth * (i + y * actualYOffset) + (j + x * actualXOffset)]; b = expectedBlue[expectedWidth * (i + y * expectedYOffset) + (j + x * expectedXOffset)] - actualBlue[actualWidth * (i + y * actualYOffset) + (j + x * actualXOffset)]; if (r * r + g * g + b * b > 3 * 255 * 255 * diffThreshold * diffThreshold) thresDiffCount++; } } if (thresDiffCount < thresDiffMin || thresDiffMin == -1) { thresDiffMin = thresDiffCount; bestX = x; bestY = y; } } } } else { int x = 0; for (int y = 0; y <= yMax; y++) { thresDiffCount = 0; for (int i = 0; i < subHeight; i = i + STEP) { for (int j = 0; j < subWidth; j = j + STEP) { r = expectedRed[expectedWidth * (i + y * expectedYOffset) + (j + x * expectedXOffset)] - actualRed[actualWidth * (i + y * actualYOffset) + (j + x * actualXOffset)]; g = expectedGreen[expectedWidth * (i + y * expectedYOffset) + (j + x * expectedXOffset)] - actualGreen[actualWidth * (i + y * actualYOffset) + (j + x * actualXOffset)]; b = expectedBlue[expectedWidth * (i + y * expectedYOffset) + (j + x * expectedXOffset)] - actualBlue[actualWidth * (i + y * actualYOffset) + (j + x * actualXOffset)]; if (r * r + g * g + b * b > 3 * 255 * 255 * diffThreshold * diffThreshold) thresDiffCount++; } } if (thresDiffCount < thresDiffMin || thresDiffMin == -1) { thresDiffMin = thresDiffCount; bestX = x; bestY = y; } } int y = 0; for (x = 1; x <= xMax; x++) { thresDiffCount = 0; for (int i = 0; i < subHeight; i = i + STEP) { for (int j = 0; j < subWidth; j = j + STEP) { r = expectedRed[expectedWidth * (i + y * expectedYOffset) + (j + x * expectedXOffset)] - actualRed[actualWidth * (i + y * actualYOffset) + (j + x * actualXOffset)]; g = expectedGreen[expectedWidth * (i + y * expectedYOffset) + (j + x * expectedXOffset)] - actualGreen[actualWidth * (i + y * actualYOffset) + (j + x * actualXOffset)]; b = expectedBlue[expectedWidth * (i + y * expectedYOffset) + (j + x * expectedXOffset)] - actualBlue[actualWidth * (i + y * actualYOffset) + (j + x * actualXOffset)]; if (r * r + g * g + b * b > 3 * 255 * 255 * diffThreshold * diffThreshold) thresDiffCount++; } } if (thresDiffCount < thresDiffMin || thresDiffMin == -1) { thresDiffMin = thresDiffCount; bestX = x; bestY = y; } } } return new Offset(bestX, bestY); } private  ImageUtils(); static ImageComparedResult compare(BufferedImage image1, Rectangle imageArea1, BufferedImage image2,
			Rectangle imageArea2, CompareOption[] options); static boolean isContained(BufferedImage entireImage, BufferedImage partImage); static double[][] calcIntegralImage(BufferedImage source); static boolean imageEquals(BufferedImage image1, BufferedImage image2); static BufferedImage getMaskedImage(BufferedImage image, List<Rectangle> maskAreas); static BufferedImage getMarkedImage(BufferedImage image, DiffPoints diffPoints); static BufferedImage getDiffImage(BufferedImage leftImage, BufferedImage rightImage, DiffPoints diffPoints); static BufferedImage trim(BufferedImage image, int trimTop, int trimLeft, int trimBottom, int trimRight); static BufferedImage verticalMerge(List<BufferedImage> images); static BufferedImage merge(List<List<BufferedImage>> images); static int[] getRGB(BufferedImage image, int width, int height); static int[] toARGB(int pixel); static int toPixel(int[] argb); static int toPixel(int r, int g, int b); static int toPixel(int a, int r, int g, int b); static int detectBackgroundColor(BufferedImage b); static boolean getObjectRectangle(BufferedImage image, Rectangle rectangle); static double norm(int pixel1, int pixel2); static double[] countSubpixel(BufferedImage bimage); static void removeRedundantRectangles(List<Rectangle> rectangles, int xLimit, int yLimit); static void removeOverlappingRectangles(List<Rectangle> rectangles); static void reshapeRect(Rectangle rectangle, int xLimit, int yLimit); static Rectangle getTightDiffArea(Rectangle rectangle, int xLimit, int yLimit); static List<Rectangle> convertObjectGroupsToAreas(List<ObjectGroup> objectGroups); static List<ObjectGroup> convertDiffPointsToObjectGroups(DiffPoints DP, int group_distance); static BufferedImage getSubImage(BufferedImage image, Rectangle rectangle); static Offset findDominantOffset(BufferedImage expectedImage, BufferedImage actualImage, double diffThreshold); static BufferedImage getDominantImage(BufferedImage expectedImage, BufferedImage actualImage, Offset offset); static int getSizeRelationType(int expectedWidth, int expectedHeight, int actualWidth, int actualHeight);  }
@Test public void testFindDominantOffset() throws Exception { BufferedImage image1 = ImageIO.read(getClass().getResource("ImageUtilsTest_dominant_expected.png")); BufferedImage image2 = ImageIO.read(getClass().getResource("ImageUtilsTest_dominant_merge.png")); double diffThreshold = ComparisonParameterDefaults.getDiffThreshold(); Offset expected = new Offset(5, 5); Offset actual = ImageUtils.findDominantOffset(image1, image2, diffThreshold); assertThat(actual.getX(), is(expected.getX())); assertThat(actual.getY(), is(expected.getY())); }