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

com.github.loyada.jdollarx.visual.SimilarityComparator Maven / Gradle / Ivy

There is a newer version: 1.5.5
Show newest version
package com.github.loyada.jdollarx.visual;

import java.awt.*;
import java.awt.image.BufferedImage;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.BiConsumer;

import static com.github.loyada.jdollarx.visual.ImageUtils.pixelValueIsSignificantlyDifferent;
import static java.lang.String.format;

public class SimilarityComparator  implements BiConsumer {
    private final int maxBadPixelsRatio;

    public SimilarityComparator(int maxBadPixelsRatio) {
        this.maxBadPixelsRatio = maxBadPixelsRatio;
    }

    private static boolean noTransitionFound(
            int actualPixOneSide, int actual, int actualPixOtherSide,
            int refPixOneSide, int ref, int refPixOtherSize
    ) {
        boolean foundTransition  = (pixelValueIsSignificantlyDifferent(actualPixOneSide, actualPixOtherSide) &&
                pixelValueIsSignificantlyDifferent(refPixOneSide, refPixOtherSize)) ||
                (pixelValueIsSignificantlyDifferent(actualPixOneSide, actual) &&
                pixelValueIsSignificantlyDifferent(refPixOneSide, ref)) ||
                (pixelValueIsSignificantlyDifferent(actualPixOtherSide, actual) &&
                        pixelValueIsSignificantlyDifferent(refPixOtherSize, ref));

        return !foundTransition || ImageUtils.avgIsSignificantlyDifferent(actualPixOneSide, actual, actualPixOtherSide,
                refPixOneSide, ref, refPixOtherSize);
    }

    static boolean pixelMismatch(BufferedImage refImage, BufferedImage actualImage, int x, int y) {
        int actual = actualImage.getRGB(x, y);
        int ref = refImage.getRGB(x, y);
        if  (!pixelValueIsSignificantlyDifferent(actual, ref)) {
            return false;
        }

        return (noTransitionFound(
                actualImage.getRGB(x - 1, y - 1),
                actual,
                actualImage.getRGB(x + 1, y + 1),
                refImage.getRGB(x - 1, y - 1),
                ref,
                refImage.getRGB(x + 1, y + 1)
        ) && noTransitionFound(
                actualImage.getRGB(x - 1, y),
                actual,
                actualImage.getRGB(x + 1, y),
                refImage.getRGB(x - 1, y),
                ref,
                refImage.getRGB(x + 1, y)
        ) && noTransitionFound(
                actualImage.getRGB(x, y - 1),
                actual,
                actualImage.getRGB(x, y + 1),
                refImage.getRGB(x, y - 1),
                ref,
                refImage.getRGB(x, y + 1)
        ) && noTransitionFound(
                actualImage.getRGB(x - 1, y + 1),
                actual,
                actualImage.getRGB(x + 1, y - 1),
                refImage.getRGB(x - 1, y + 1),
                ref,
                refImage.getRGB(x + 1, y - 1)
        ));
    }

    @Override
    public void accept(BufferedImage refImage, BufferedImage actualImage) {
        int threshold = actualImage.getWidth() * actualImage.getHeight() / maxBadPixelsRatio;
        int countOfErrors = 0;
        for (int y = 1; y < actualImage.getHeight()-1; y++) {
            for (int x = 1; x < actualImage.getWidth() - 1; x++) {
                if (pixelMismatch(actualImage, refImage, x, y)) {
                    countOfErrors++;
                }
                if (countOfErrors > threshold) {
                    String errMessage = format("images have significant differences. \n" +
                                    "found %d significant differences in %d pixels",
                            threshold, y * actualImage.getWidth() + x);
                    Images.logger.info(errMessage);
                    throw new AssertionError(errMessage);
                }
            }
        }
        Images.logger.info(format(
                        "found %d significant differences. This is under the %d threshold",
                countOfErrors, threshold));
    }

    public static Optional getErrorImage(BufferedImage refImage, BufferedImage actualImage) {
        AtomicReference foundDiff = new AtomicReference<>(false);
        BufferedImage errImage = new BufferedImage(refImage.getWidth(), refImage.getHeight(),BufferedImage.TYPE_INT_RGB);
        for (int y = 1; y < actualImage.getHeight()-1; y++) {
            for (int x = 1; x < actualImage.getWidth() - 1; x++) {
                if (pixelMismatch(actualImage, refImage, x, y)) {
                    foundDiff.set(true);
                    errImage.setRGB(x, y, 0xff0000);
                } else {
                    Color oldColor = new Color(refImage.getRGB(x, y), refImage.isAlphaPremultiplied());
                    Color newColor = new Color(oldColor.getRed() / 4, oldColor.getGreen() / 4, oldColor.getBlue() / 4);
                    errImage.setRGB(x, y, newColor.getRGB());
                }
            }
        }

        return foundDiff.get() ? Optional.of(errImage) : Optional.empty();
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy