Skip to content

Commit

Permalink
Implemented #175 (#177)
Browse files Browse the repository at this point in the history
#175: added the ability to ignore some percent of differences for comparison.
  • Loading branch information
romankh3 authored Mar 11, 2020
1 parent 8a932d6 commit 96c08dd
Show file tree
Hide file tree
Showing 12 changed files with 158 additions and 76 deletions.
10 changes: 0 additions & 10 deletions .github/ISSUE_TEMPLATE/custom.md

This file was deleted.

10 changes: 10 additions & 0 deletions .github/ISSUE_TEMPLATE/question.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
---
name: Question
about: ask any questions, which are interested you
title: "[QUESTION]"
labels: 'question'
assignees: romankh3

---


2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
Be sure to include a title and clear description, as much relevant information as possible,
and a code sample or an executable test case demonstrating the expected behavior that is not occurring.

* Provide test images, which can be used for reprodusing.
* Provide test images, which can be used for reproducing.

## Did you write a patch that fixes a bug
* Open a new GitHub pull request with the patch.
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ Published on Maven Central and jCenter Java Library that compares 2 images with
| `percentOpacityExcludedRectangles` | The desired opacity of the excluded rectangle fill. |
| `fillDifferenceRectangles` | Flag which says fill difference rectangles or not. |
| `percentOpacityDifferenceRectangles` | The desired opacity of the difference rectangle fill. |
| `allowingPercentOfDifferentPixels` | The percent of the allowing pixels to be different to stay {@link ImageComparisonState#MATCH} for comparison. E.g. percent of the pixels, which would ignore in comparison. |

## Release Notes

Expand Down
3 changes: 3 additions & 0 deletions RELEASE_NOTES.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
# Release Notes

## 4.2.0
* added the ability to ignore some percent of differences for comparison.

## 4.1.0
* added option to fill rectangles(167 issue)

Expand Down
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ plugins {
}

group 'com.github.romankh3'
version '4.1.0'
version '4.2.0-SNAPSHOT'
description 'A library and utility to compare different images.'
sourceCompatibility = 1.8

Expand Down
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

<groupId>com.github.romankh3</groupId>
<artifactId>image-comparison</artifactId>
<version>4.1.0</version>
<version>4.2.0-SNAPSHOT</version>
<packaging>jar</packaging>

<name>Image Comparison</name>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,18 @@

import com.github.romankh3.image.comparison.model.ExcludedAreas;
import com.github.romankh3.image.comparison.model.ImageComparisonResult;
import com.github.romankh3.image.comparison.model.ImageComparisonState;
import com.github.romankh3.image.comparison.model.Rectangle;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Point;

import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.*;
import java.util.stream.Collectors;

import static java.util.Collections.emptyList;

/**
* Main class for comparison images.
*/
Expand Down Expand Up @@ -112,32 +110,38 @@ public class ImageComparison {
* The difference in percent between two images.
*/
private float differencePercent;

/**
* Flag for filling comparison difference rectangles.
*/
private boolean fillDifferenceRectangles = false;

/**
* Sets the opacity percentage of the fill of comparison difference rectangles. 0.0 means completely transparent and 100.0 means completely opaque.
*/
private double percentOpacityDifferenceRectangles = 20.0;

/**
* Flag for filling excluded rectangles.
*/
private boolean fillExcludedRectangles = false;

/**
* Sets the opacity percentage of the fill of excluded rectangles. 0.0 means completely transparent and 100.0 means completely opaque.
*/
private double percentOpacityExcludedRectangles = 20.0;

/**
* The percent of the allowing pixels to be different to stay {@link ImageComparisonState#MATCH} for comparison.
* E.g. percent of the pixels, which would ignore in comparison.
*/
private double allowingPercentOfDifferentPixels = 0.0;

/**
* Create a new instance of {@link ImageComparison} that can compare the given images.
*
* @param expected expected image to be compared
* @param actual actual image to be compared
* @param actual actual image to be compared
*/
public ImageComparison(String expected, String actual) {
this(ImageComparisonUtil.readImageFromResources(expected),
Expand All @@ -148,22 +152,22 @@ public ImageComparison(String expected, String actual) {
/**
* Create a new instance of {@link ImageComparison} that can compare the given images.
*
* @param expected expected image to be compared
* @param actual actual image to be compared
* @param expected expected image to be compared
* @param actual actual image to be compared
* @param destination destination to save the result. If null, the result is shown in the UI.
*/
public ImageComparison(BufferedImage expected, BufferedImage actual, File destination) {
this.expected = expected;
this.actual = actual;
this.destination = destination;
differenceConstant = calculateDifferenceConstant();
differenceConstant = calculateDifferenceConstant();
}

/**
* Create a new instance of {@link ImageComparison} that can compare the given images.
*
* @param expected expected image to be compared
* @param actual actual image to be compared
* @param actual actual image to be compared
*/
public ImageComparison(BufferedImage expected, BufferedImage actual) {
this(expected, actual, null);
Expand Down Expand Up @@ -203,7 +207,7 @@ public ImageComparisonResult compareImages() {
* Check images for equals their widths and heights.
*
* @param expected {@link BufferedImage} object of the expected image.
* @param actual {@link BufferedImage} object of the actual image.
* @param actual {@link BufferedImage} object of the actual image.
* @return true if image size are not equal, false otherwise.
*/
private boolean isImageSizesNotEqual(BufferedImage expected, BufferedImage actual) {
Expand All @@ -212,25 +216,31 @@ private boolean isImageSizesNotEqual(BufferedImage expected, BufferedImage actua

/**
* Populate binary matrix with "0" and "1". If the pixels are different set it as "1", otherwise "0".
*
* @return the count of different pixels
*/
private void populateTheMatrixOfTheDifferences() {
private long populateTheMatrixOfTheDifferences() {
long countOfDifferentPixels = 0;
matrix = new int[expected.getHeight()][expected.getWidth()];
for (int y = 0; y < expected.getHeight(); y++) {
for (int x = 0; x < expected.getWidth(); x++) {
Point point = new Point(x, y);
if (!excludedAreas.contains(point)) {
matrix[y][x] = isDifferentPixels(expected.getRGB(x, y), actual.getRGB(x, y)) ? 1 : 0;
if (!excludedAreas.contains(new Point(x, y))) {
if (isDifferentPixels(expected.getRGB(x, y), actual.getRGB(x, y))) {
matrix[y][x] = 1;
countOfDifferentPixels++;
}
}
}
}
return countOfDifferentPixels;
}

/**
* Say if the two pixels equal or not. The rule is the difference between two pixels
* need to be more than {@link #pixelToleranceLevel}.
*
* @param expectedRgb the RGB value of the Pixel of the Expected image.
* @param actualRgb the RGB value of the Pixel of the Actual image.
* @param actualRgb the RGB value of the Pixel of the Actual image.
* @return {@code true} if they' are difference, {@code false} otherwise.
*/
private boolean isDifferentPixels(int expectedRgb, int actualRgb) {
Expand All @@ -257,7 +267,15 @@ private boolean isDifferentPixels(int expectedRgb, int actualRgb) {
* @return the collection of the populated {@link Rectangle} objects.
*/
private List<Rectangle> populateRectangles() {
populateTheMatrixOfTheDifferences();
long countOfDifferentPixels = populateTheMatrixOfTheDifferences();

if (countOfDifferentPixels == 0) {
return emptyList();
}

if (isAllowedPercentOfDifferentPixels(countOfDifferentPixels)) {
return emptyList();
}
groupRegions();
List<Rectangle> rectangles = new ArrayList<>();
while (counter <= regionCount) {
Expand All @@ -271,6 +289,19 @@ private List<Rectangle> populateRectangles() {
return mergeRectangles(rectangles);
}

/**
* Say if provided {@param countOfDifferentPixels} is allowed for {@link ImageComparisonState#MATCH} state.
*
* @param countOfDifferentPixels the count of the different pixels in comparison.
* @return true, if percent of different pixels lower or equal {@link ImageComparison#allowingPercentOfDifferentPixels},
* false - otherwise.
*/
private boolean isAllowedPercentOfDifferentPixels(long countOfDifferentPixels) {
long totalPixelCount = matrix.length * matrix[0].length;
double actualPercentOfDifferentPixels = ((double) countOfDifferentPixels / (double) totalPixelCount) * 100;
return actualPercentOfDifferentPixels <= allowingPercentOfDifferentPixels;
}

/**
* Create a {@link Rectangle} object.
*
Expand Down Expand Up @@ -358,7 +389,7 @@ private void drawExcludedRectangles(Graphics2D graphics) {
if (drawExcludedRectangles) {
graphics.setColor(Color.GREEN);
draw(graphics, excludedAreas.getExcluded());

if (fillExcludedRectangles) {
fillRectangles(graphics, excludedAreas.getExcluded(), percentOpacityExcludedRectangles);
}
Expand All @@ -369,7 +400,7 @@ private void drawExcludedRectangles(Graphics2D graphics) {
* Draw rectangles with the differences.
*
* @param rectangles the collection of the {@link Rectangle} of differences.
* @param graphics prepared {@link Graphics2D}object.
* @param graphics prepared {@link Graphics2D}object.
*/
private void drawRectanglesOfDifferences(List<Rectangle> rectangles, Graphics2D graphics) {
List<Rectangle> rectanglesForDraw;
Expand All @@ -385,7 +416,7 @@ private void drawRectanglesOfDifferences(List<Rectangle> rectangles, Graphics2D
}

draw(graphics, rectanglesForDraw);

if (fillDifferenceRectangles) {
fillRectangles(graphics, rectanglesForDraw, percentOpacityDifferenceRectangles);
}
Expand All @@ -395,7 +426,6 @@ private void drawRectanglesOfDifferences(List<Rectangle> rectangles, Graphics2D
* Prepare {@link Graphics2D} based on resultImage and rectangleLineWidth
*
* @param image image based on created {@link Graphics2D}.
*
* @return prepared {@link Graphics2D} object.
*/
private Graphics2D preparedGraphics2D(BufferedImage image) {
Expand All @@ -420,7 +450,7 @@ private void saveImageForDestination(BufferedImage image) {
* getWidth/getHeight return real width/height,
* so need to draw rectangle on one px smaller because minpoint + width/height is point on excluded pixel.
*
* @param graphics the {@link Graphics2D} object for drawing.
* @param graphics the {@link Graphics2D} object for drawing.
* @param rectangles the collection of the {@link Rectangle}.
*/
private void draw(Graphics2D graphics, List<Rectangle> rectangles) {
Expand All @@ -431,18 +461,18 @@ private void draw(Graphics2D graphics, List<Rectangle> rectangles) {
rectangle.getHeight() - 1)
);
}

/**
* Fill rectangles based on collection of the {@link Rectangle} and {@link Graphics2D}.
* getWidth/getHeight return real width/height,
* so need to draw rectangle fill two px smaller to fit inside rectangle borders.
*
* @param graphics the {@link Graphics2D} object for drawing.
* @param rectangles rectangles the collection of the {@link Rectangle}.
* @param graphics the {@link Graphics2D} object for drawing.
* @param rectangles rectangles the collection of the {@link Rectangle}.
* @param percentOpacity the opacity of the fill.
*/
private void fillRectangles(Graphics2D graphics, List<Rectangle> rectangles, double percentOpacity) {

graphics.setColor(new Color(graphics.getColor().getRed(),
graphics.getColor().getGreen(),
graphics.getColor().getBlue(),
Expand All @@ -453,10 +483,9 @@ private void fillRectangles(Graphics2D graphics, List<Rectangle> rectangles, dou
rectangle.getMinPoint().y - 1,
rectangle.getWidth() - 2,
rectangle.getHeight() - 2)
);
);
}




/**
* Group rectangle regions in matrix.
Expand Down Expand Up @@ -601,34 +630,46 @@ public ImageComparison setExcludedAreas(List<Rectangle> excludedAreas) {
this.excludedAreas = new ExcludedAreas(excludedAreas);
return this;
}

public boolean isFillDifferenceRectangles() {
return this.fillDifferenceRectangles;
}

public double getPercentOpacityDifferenceRectangles() {
return this.percentOpacityDifferenceRectangles;
}

public ImageComparison setDifferenceRectangleFilling(boolean fillRectangles, double percentOpacity) {
this.fillDifferenceRectangles = fillRectangles;
this.percentOpacityDifferenceRectangles = percentOpacity;
return this;
}

public boolean isFillExcludedRectangles() {
return this.fillExcludedRectangles;
}

public double getPercentOpacityExcludedRectangles() {
return this.percentOpacityExcludedRectangles;
}

public ImageComparison setExcludedRectangleFilling(boolean fillRectangles, double percentOpacity) {
this.fillExcludedRectangles = fillRectangles;
this.percentOpacityExcludedRectangles = percentOpacity;
return this;
}



public double getAllowingPercentOfDifferentPixels() {
return allowingPercentOfDifferentPixels;
}

public ImageComparison setAllowingPercentOfDifferentPixels(double allowingPercentOfDifferentPixels) {
if(0.0 <= allowingPercentOfDifferentPixels && allowingPercentOfDifferentPixels <= 100) {
this.allowingPercentOfDifferentPixels = allowingPercentOfDifferentPixels;
} else {
//todo add warning here
}

return this;
}
}
Loading

0 comments on commit 96c08dd

Please sign in to comment.