Skip to content

Commit

Permalink
#328 Added mask filter and alpha channel in image comparison
Browse files Browse the repository at this point in the history
  • Loading branch information
ishubin committed Nov 12, 2015
1 parent 0843c62 commit 612b5f9
Show file tree
Hide file tree
Showing 20 changed files with 233 additions and 88 deletions.
45 changes: 33 additions & 12 deletions galen-core/pom.xml
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@
<version>2.1.4-SNAPSHOT</version> <version>2.1.4-SNAPSHOT</version>
</parent> </parent>


<properties>
<selenium.version>2.48.2</selenium.version>
</properties>

<dependencies> <dependencies>
<dependency> <dependency>
<groupId>com.galenframework</groupId> <groupId>com.galenframework</groupId>
Expand Down Expand Up @@ -53,6 +57,7 @@
<dependency> <dependency>
<groupId>org.seleniumhq.selenium</groupId> <groupId>org.seleniumhq.selenium</groupId>
<artifactId>selenium-firefox-driver</artifactId> <artifactId>selenium-firefox-driver</artifactId>
<version>${selenium.version}</version>
<exclusions> <exclusions>
<exclusion> <exclusion>
<groupId>com.google.code.gson</groupId> <groupId>com.google.code.gson</groupId>
Expand All @@ -63,30 +68,46 @@
<dependency> <dependency>
<groupId>org.seleniumhq.selenium</groupId> <groupId>org.seleniumhq.selenium</groupId>
<artifactId>selenium-chrome-driver</artifactId> <artifactId>selenium-chrome-driver</artifactId>
<version>${selenium.version}</version>
<exclusions>
<exclusion>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
</exclusion>
</exclusions>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.seleniumhq.selenium</groupId> <groupId>org.seleniumhq.selenium</groupId>
<artifactId>selenium-edge-driver</artifactId> <artifactId>selenium-edge-driver</artifactId>
<version>${selenium.version}</version>
<exclusions>
<exclusion>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
</exclusion>
</exclusions>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.seleniumhq.selenium</groupId> <groupId>org.seleniumhq.selenium</groupId>
<artifactId>selenium-java</artifactId> <artifactId>selenium-java</artifactId>
<exclusions> <version>${selenium.version}</version>
<exclusion> <exclusions>
<groupId>com.google.code.gson</groupId> <exclusion>
<artifactId>gson</artifactId> <groupId>com.google.code.gson</groupId>
</exclusion> <artifactId>gson</artifactId>
</exclusions> </exclusion>
</exclusions>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.seleniumhq.selenium</groupId> <groupId>org.seleniumhq.selenium</groupId>
<artifactId>selenium-support</artifactId> <artifactId>selenium-support</artifactId>
<exclusions> <version>${selenium.version}</version>
<exclusion> <exclusions>
<groupId>com.google.code.gson</groupId> <exclusion>
<artifactId>gson</artifactId> <groupId>com.google.code.gson</groupId>
</exclusion> <artifactId>gson</artifactId>
</exclusions> </exclusion>
</exclusions>
</dependency> </dependency>
<dependency> <dependency>
<groupId>commons-cli</groupId> <groupId>commons-cli</groupId>
Expand Down
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@ public SyntaxException(Line line, String paramString, Throwable paramThrowable)
this.line = line; this.line = line;
} }


public SyntaxException(String paramString, Throwable paramThrowable) {
super(paramString, paramThrowable);
}

public SyntaxException(Line line, String paramString) { public SyntaxException(Line line, String paramString) {
super(paramString); super(paramString);
this.line = line; this.line = line;
Expand Down
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -19,16 +19,21 @@
import com.galenframework.parser.ExpectNumber; import com.galenframework.parser.ExpectNumber;
import com.galenframework.parser.ExpectWord; import com.galenframework.parser.ExpectWord;
import com.galenframework.parser.SyntaxException; import com.galenframework.parser.SyntaxException;
import com.galenframework.rainbow4j.ImageHandler;
import com.galenframework.rainbow4j.Rainbow4J;
import com.galenframework.rainbow4j.filters.*; import com.galenframework.rainbow4j.filters.*;
import com.galenframework.specs.SpecImage; import com.galenframework.specs.SpecImage;
import com.galenframework.specs.reader.StringCharReader; import com.galenframework.specs.reader.StringCharReader;
import com.galenframework.config.GalenConfig; import com.galenframework.config.GalenConfig;
import com.galenframework.parser.Expectations; import com.galenframework.parser.Expectations;
import com.galenframework.specs.Spec; import com.galenframework.specs.Spec;
import com.galenframework.utils.GalenUtils;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.Pair; import org.apache.commons.lang3.tuple.Pair;


import java.io.File; import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;


Expand Down Expand Up @@ -67,20 +72,20 @@ else if ("area".equals(parameter.getKey())) {
spec.setSelectedArea(parseRect(parameter.getValue())); spec.setSelectedArea(parseRect(parameter.getValue()));
} }
else if ("filter".equals(parameter.getKey())) { else if ("filter".equals(parameter.getKey())) {
ImageFilter filter = parseImageFilter(parameter.getValue()); ImageFilter filter = parseImageFilter(parameter.getValue(), contextPath);
spec.getOriginalFilters().add(filter); spec.getOriginalFilters().add(filter);
spec.getSampleFilters().add(filter); spec.getSampleFilters().add(filter);
} }
else if ("filter-a".equals(parameter.getKey())) { else if ("filter-a".equals(parameter.getKey())) {
ImageFilter filter = parseImageFilter(parameter.getValue()); ImageFilter filter = parseImageFilter(parameter.getValue(), contextPath);
spec.getOriginalFilters().add(filter); spec.getOriginalFilters().add(filter);
} }
else if ("filter-b".equals(parameter.getKey())) { else if ("filter-b".equals(parameter.getKey())) {
ImageFilter filter = parseImageFilter(parameter.getValue()); ImageFilter filter = parseImageFilter(parameter.getValue(), contextPath);
spec.getSampleFilters().add(filter); spec.getSampleFilters().add(filter);
} }
else if ("map-filter".equals(parameter.getKey())) { else if ("map-filter".equals(parameter.getKey())) {
ImageFilter filter = parseImageFilter(parameter.getValue()); ImageFilter filter = parseImageFilter(parameter.getValue(), contextPath);
spec.getMapFilters().add(filter); spec.getMapFilters().add(filter);
} }
else if ("crop-if-outside".equals(parameter.getKey())) { else if ("crop-if-outside".equals(parameter.getKey())) {
Expand All @@ -104,28 +109,51 @@ private Integer parseIntegerParameter(String name, String value) {
else throw new SyntaxException(name + " parameter should be integer: " + value); else throw new SyntaxException(name + " parameter should be integer: " + value);
} }


private ImageFilter parseImageFilter(String filterText) { private ImageFilter parseImageFilter(String filterText, String contextPath) {
StringCharReader reader = new StringCharReader(filterText); StringCharReader reader = new StringCharReader(filterText);


String filterName = new ExpectWord().read(reader); String filterName = new ExpectWord().read(reader);
Double value = new ExpectNumber().read(reader);


if ("contrast".equals(filterName)) {
return new ContrastFilter(value.intValue()); if ("mask".equals(filterName)) {
} String imagePath = reader.getTheRest().trim();
else if ("blur".equals(filterName)) {
return new BlurFilter(value.intValue()); if (imagePath.isEmpty()) {
} throw new SyntaxException("Mask filter image path is not defined");
else if ("denoise".equals(filterName)) { }
return new DenoiseFilter(value.intValue());
} String fullImagePath = imagePath;
else if ("saturation".equals(filterName)) {
return new SaturationFilter(value.intValue()); if (contextPath != null && !contextPath.isEmpty()) {
} fullImagePath = contextPath + File.separator + imagePath;
else if ("quantinize".equals(filterName)) { }
return new QuantinizeFilter(value.intValue()); try {

InputStream stream = GalenUtils.findMandatoryFileOrResourceAsStream(fullImagePath);

return new MaskFilter(new ImageHandler(Rainbow4J.loadImage(stream)));
} catch (IOException exception) {
throw new SyntaxException("Couldn't load " + fullImagePath, exception);
}
} else {
Double value = new ExpectNumber().read(reader);
if ("contrast".equals(filterName)) {
return new ContrastFilter(value.intValue());
}
else if ("blur".equals(filterName)) {
return new BlurFilter(value.intValue());
}
else if ("denoise".equals(filterName)) {
return new DenoiseFilter(value.intValue());
}
else if ("saturation".equals(filterName)) {
return new SaturationFilter(value.intValue());
}
else if ("quantinize".equals(filterName)) {
return new QuantinizeFilter(value.intValue());
}
} }
else throw new SyntaxException("Unknown image filter: " + filterName); throw new SyntaxException("Unknown image filter: " + filterName);
} }


private Rect parseRect(String text) { private Rect parseRect(String text) {
Expand Down
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -499,4 +499,13 @@ public static boolean isObjectGroup(String singleExpression) {
public static String extractGroupName(String singleExpression) { public static String extractGroupName(String singleExpression) {
return singleExpression.substring(1); return singleExpression.substring(1);
} }

public static InputStream findMandatoryFileOrResourceAsStream(String imagePath) throws FileNotFoundException {
InputStream stream = findFileOrResourceAsStream(imagePath);
if (stream == null) {
throw new FileNotFoundException(imagePath);
}

return stream;
}
} }
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;


import com.galenframework.rainbow4j.filters.MaskFilter;
import com.galenframework.specs.*; import com.galenframework.specs.*;
import com.galenframework.specs.colors.ColorRange; import com.galenframework.specs.colors.ColorRange;
import junit.framework.Assert; import junit.framework.Assert;
Expand Down Expand Up @@ -995,6 +996,18 @@ public void shouldReadSpec_image_withMaxPixelsError_tolerance5_filterBlur2_filte


} }


@Test
public void shouldReadSpec_image_withMask() throws IOException {
SpecImage spec = (SpecImage)readSpec("image file image.png, filter mask color-scheme-image-1.png");

assertThat(spec.getImagePaths(), contains("image.png"));
assertThat(spec.getOriginalFilters().size(), is(1));
assertThat(spec.getOriginalFilters().get(0), is(instanceOf(MaskFilter.class)));

assertThat(spec.getSampleFilters().size(), is(1));
assertThat(spec.getSampleFilters().get(0), is(instanceOf(MaskFilter.class)));
}

@Test @Test
public void shouldReadSpec_image_withMaxPixelsError_andArea() throws IOException { public void shouldReadSpec_image_withMaxPixelsError_andArea() throws IOException {
SpecImage spec = (SpecImage)readSpec("image file imgs/image.png, error 112 px, area 10 10 100 20"); SpecImage spec = (SpecImage)readSpec("image file imgs/image.png, error 112 px, area 10 10 100 20");
Expand Down
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -368,9 +368,8 @@ public Object[][] provideGoodSamples() {
row(specTextMatches(".* some.* multiline"), page(new HashMap<String, PageElement>(){{ row(specTextMatches(".* some.* multiline"), page(new HashMap<String, PageElement>(){{
put("object", element(10, 10, 10, 10).withText("A text with some \n more multiline")); put("object", element(10, 10, 10, 10).withText("A text with some \n more multiline"));
}})), }})),



// Above
// Above


row(specAbove("button", Range.exact(20)), page(new HashMap<String, PageElement>(){{ row(specAbove("button", Range.exact(20)), page(new HashMap<String, PageElement>(){{
put("object", element(10, 10, 10, 10)); put("object", element(10, 10, 10, 10));
Expand Down
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ public class ImageHandler {
private int width; private int width;
private int height; private int height;


private final static int BLOCK_SIZE = 3; public final static int BLOCK_SIZE = 4;


public ImageHandler(BufferedImage image) { public ImageHandler(BufferedImage image) {
this.bytes = readRgbModelFrom(image); this.bytes = readRgbModelFrom(image);
Expand All @@ -47,11 +47,9 @@ public ImageHandler(byte[] bytes, int w, int h) {




private static byte[] readRgbModelFrom(BufferedImage image) { private static byte[] readRgbModelFrom(BufferedImage image) {

int w = image.getWidth(); int w = image.getWidth();
int h = image.getHeight(); int h = image.getHeight();



int[] pixels = new int[w * h]; int[] pixels = new int[w * h];
image.getRGB(0, 0, w, h, pixels, 0, w); image.getRGB(0, 0, w, h, pixels, 0, w);


Expand All @@ -62,9 +60,10 @@ private static byte[] readRgbModelFrom(BufferedImage image) {
int index = r * w + c; int index = r * w + c;
int indexRgb = r * w * BLOCK_SIZE + c * BLOCK_SIZE; int indexRgb = r * w * BLOCK_SIZE + c * BLOCK_SIZE;


rgbBytes[indexRgb] = (byte)((pixels[index] >> 16) &0xff); rgbBytes[indexRgb] = (byte)((pixels[index] >> 16) & 0xff);
rgbBytes[indexRgb + 1] = (byte)((pixels[index] >> 8) &0xff); rgbBytes[indexRgb + 1] = (byte)((pixels[index] >> 8) & 0xff);
rgbBytes[indexRgb + 2] = (byte)(pixels[index] &0xff); rgbBytes[indexRgb + 2] = (byte)(pixels[index] & 0xff);
rgbBytes[indexRgb + 3] = (byte)((pixels[index] >> 24) & 0xff);
} }
} }


Expand All @@ -77,7 +76,8 @@ public Color pickColor(int x, int y) {


return new Color(bytes[k] & 0xff, return new Color(bytes[k] & 0xff,
bytes[k + 1] & 0xff, bytes[k + 1] & 0xff,
bytes[k + 2] & 0xff bytes[k + 2] & 0xff,
bytes[k + 3] & 0xff
); );
} }
else { else {
Expand All @@ -86,22 +86,28 @@ public Color pickColor(int x, int y) {
} }


public static long colorDiff(Color left, Color right) { public static long colorDiff(Color left, Color right) {
return Math.abs( left.getRed() - right.getRed()) if (left.getAlpha() > 128 && right.getAlpha() > 128) {
+ Math.abs( left.getGreen() - right.getGreen()) return Math.abs(left.getRed() - right.getRed())
+ Math.abs( left.getBlue() - right.getBlue()); + Math.abs(left.getGreen() - right.getGreen())
+ Math.abs(left.getBlue() - right.getBlue());
} else {
return 0L;
}
} }




public BufferedImage getImage() { public BufferedImage getImage() {
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);


for(int r=0; r< height; r++) { for(int r=0; r< height; r++) {
for (int c = 0; c < width; c++) { for (int c = 0; c < width; c++) {
int index = r * width * BLOCK_SIZE + c * BLOCK_SIZE; int index = r * width * BLOCK_SIZE + c * BLOCK_SIZE;
int red = bytes[index] & 0xFF; int red = bytes[index] & 0xFF;
int green = bytes[index + 1] & 0xFF; int green = bytes[index + 1] & 0xFF;
int blue = bytes[index + 2] & 0xFF; int blue = bytes[index + 2] & 0xFF;
int rgb = (red << 16) | (green << 8) | blue; int alpha = bytes[index + 3] & 0xFF;

int rgb = (alpha << 24) | (red << 16) | (green << 8) | blue;
image.setRGB(c, r, rgb); image.setRGB(c, r, rgb);
} }
} }
Expand All @@ -113,11 +119,12 @@ public void applyFilter(ImageFilter filter, Rectangle area) {
filter.apply(this.bytes, width, height, area); filter.apply(this.bytes, width, height, area);
} }


public void setRGB(int x, int y, int r, int g, int b) { public void setRGBA(int x, int y, int r, int g, int b, int a) {
int k = y * width * BLOCK_SIZE + x * BLOCK_SIZE; int k = y * width * BLOCK_SIZE + x * BLOCK_SIZE;
bytes[k] = (byte) (r & 0xff); bytes[k] = (byte) (r & 0xff);
bytes[k + 1] = (byte) (g & 0xff); bytes[k + 1] = (byte) (g & 0xff);
bytes[k + 2] = (byte) (b & 0xff); bytes[k + 2] = (byte) (b & 0xff);
bytes[k + 3] = (byte) (a & 0xff);
} }


public int getWidth() { public int getWidth() {
Expand Down
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -159,14 +159,14 @@ public static ImageCompareResult compare(BufferedImage imageA, BufferedImage ima
} else if (diff <= 30) { } else if (diff <= 30) {
color = Color.green; color = Color.green;
} }
mapHandler.setRGB(x, y, color.getRed(), color.getGreen(), color.getBlue()); mapHandler.setRGBA(x, y, color.getRed(), color.getGreen(), color.getBlue(), 255);


mismatchingPixels += 1; mismatchingPixels += 1;
} else { } else {
mapHandler.setRGB(x, y, 0, 0, 0); mapHandler.setRGBA(x, y, 0, 0, 0, 255);
} }
} else { } else {
mapHandler.setRGB(x, y, 0, 0, 0); mapHandler.setRGBA(x, y, 0, 0, 0, 255);
} }


x += 1; x += 1;
Expand Down Expand Up @@ -206,7 +206,7 @@ private static ImageCompareResult analyzeComparisonMap(ImageHandler mapHandler)


byte[] bytes = mapHandler.getBytes(); byte[] bytes = mapHandler.getBytes();


for (int k = 0; k < bytes.length - 3; k += 3) { for (int k = 0; k < bytes.length - ImageHandler.BLOCK_SIZE; k += ImageHandler.BLOCK_SIZE) {
if (((int)bytes[k] &0xff) > 0 || ((int)bytes[k + 1] &0xff) > 0 || ((int)bytes[k + 2] &0xff) > 0) { if (((int)bytes[k] &0xff) > 0 || ((int)bytes[k + 1] &0xff) > 0 || ((int)bytes[k + 2] &0xff) > 0) {
totalMismatchingPixels++; totalMismatchingPixels++;
} }
Expand Down
Loading

0 comments on commit 612b5f9

Please sign in to comment.