Search in sources :

Example 1 with Screen

use of net.hearthstats.game.Screen in project HearthStats.net-Uploader by HearthStats.

the class ScreenAnalyser method identifyScreen.

/**
   * <p>
   * Identifies the screen in the given image. If possible it will perform an
   * 'exact' match, meaning that it all the primary pixels specified in
   * {@link Screen} were within range. An exact match is very accurate, however
   * sometimes an exact match is not possible due to effects (eg partial
   * effects) or obstructing objects (eg a card being dragged over certain
   * pixels) so a partial match is performed. If a partial match doesn't
   * identify a screen with high enough confidence, no screen is returned.
   * </p>
   * <p>
   * It is considered normal that some screens can't be identified. Only the
   * screens that indicate important events have been defined in Screen so far;
   * some Hearthstone screens can't be identified but this is silently ignored
   * by the HearthstoneAnalyser which expects some unknown screens.
   * </p>
   * 
   * @param image
   *          The image to identify a Hearthstone screen from.
   * @param previousScreen
   *          The last screen that was identified; optional, but specify this to
   *          narrow down the search, reduce the risk of false positives (eg
   *          jumping out of a game unexpectedly) and generally make the
   *          analysis faster.
   * @return The Screen that was identified, or null if no screen could be
   *         identified with reasonable confidence.
   */
public Screen identifyScreen(BufferedImage image, Screen previousScreen) {
    log.trace("Identifying screen");
    // ProgramHelpers may return a null image if the window is minimised or still loading, so ignore those
    if (image == null) {
        return null;
    }
    if (expectedWidth != image.getWidth() || expectedHeight != image.getHeight()) {
        pixelMap = calculatePixelPositions(image.getWidth(), image.getHeight());
        expectedWidth = image.getWidth();
        expectedHeight = image.getHeight();
    }
    // If we have a previous screen, check only those screens which follow from
    // this one
    EnumSet<Screen> possibleScreens;
    if (previousScreen == null) {
        possibleScreens = EnumSet.allOf(Screen.class);
    } else {
        possibleScreens = previousScreen.nextScreens;
        if (possibleScreens.size() == 0) {
            throw new IllegalStateException("Unable to identify screen because previous screen " + previousScreen + " has no nextScreens parameter");
        }
    }
    Screen match = null;
    // most likely one to match, of course!
    if (previousScreen != null) {
        if (checkForExactMatch(image, previousScreen)) {
            // This screen matches
            log.trace("Exact match on previous screen {}", previousScreen);
            match = previousScreen;
        }
    }
    // only
    if (match == null) {
        for (Screen screen : possibleScreens) {
            if (checkForExactMatch(image, screen)) {
                // This screen matches
                if (log.isDebugEnabled()) {
                    if (match == null) {
                        log.trace("Exact match on new screen {}", screen);
                    } else {
                        log.warn("More that one screen matched! Matched screen {}, but have already matched {}", screen, match);
                    }
                }
                match = screen;
                // efficiency
                if (!log.isDebugEnabled())
                    break;
            }
        }
    }
    if (match == null) {
        // A check of the primary pixels did not find an exact match, so try for a
        // partial match
        log.debug("Did not find exact screen match, attempting partial match");
        Map<Screen, PartialResult> screenMatchesMap = new HashMap<>();
        int maxMatchedCount = 0;
        int maxUnmatchedCount = 0;
        Screen bestMatch = null;
        EnumSet<Screen> possibleScreensIncludingPrevious = EnumSet.copyOf(possibleScreens);
        if (previousScreen != null) {
            possibleScreensIncludingPrevious.add(previousScreen);
        }
        for (Screen screen : possibleScreensIncludingPrevious) {
            PartialResult partialResult = checkForPartialMatch(image, screen);
            if (partialResult.matchedCount >= maxMatchedCount) {
                maxMatchedCount = partialResult.matchedCount;
                bestMatch = screen;
            }
            if (partialResult.unmatchedCount > maxUnmatchedCount) {
                maxUnmatchedCount = partialResult.unmatchedCount;
            }
            log.debug("Test of screen {} matched={} unmatched={}", screen, partialResult.matchedCount, partialResult.unmatchedCount);
            screenMatchesMap.put(screen, partialResult);
        }
        // - has fewer unmatched pixels than any other screen
        assert (bestMatch != null);
        PartialResult bestMatchResult = screenMatchesMap.get(bestMatch);
        boolean acceptBestMatch = true;
        if (bestMatchResult.unmatchedCount > 2) {
            log.debug("Partial match failed because best match {} has {} unmatched pixels", bestMatch, bestMatchResult.unmatchedCount);
            acceptBestMatch = false;
        } else {
            // Check whether other screens are too close to the best-matched screen,
            // but ignore any screens considered to be equivalent (ie the playing
            // screen for each board is considered equivalent)
            ScreenGroup ignoreGroup;
            if (bestMatch.group == ScreenGroup.MATCH_PLAYING || bestMatch.group == ScreenGroup.MATCH_END) {
                ignoreGroup = bestMatch.group;
            } else {
                ignoreGroup = null;
            }
            for (Screen screen : possibleScreens) {
                if (screen != bestMatch && (ignoreGroup == null || screen.group != ignoreGroup)) {
                    // This screen is not the best match, and it's not from the same
                    // group (for those groups considered equivalent) so we need to
                    // ensure it's not too close to the best match
                    PartialResult currentResult = screenMatchesMap.get(screen);
                    if (bestMatchResult.matchedCount <= currentResult.matchedCount) {
                        log.debug("Partial match failed because best match {} has {} matched pixels whereas {} has {}", bestMatch, bestMatchResult.matchedCount, screen, currentResult.matchedCount);
                        acceptBestMatch = false;
                        break;
                    } else if (bestMatchResult.unmatchedCount >= currentResult.unmatchedCount) {
                        log.debug("Partial match failed because best match {} has {} unmatched pixels whereas {} has {}", bestMatch, bestMatchResult.unmatchedCount, screen, currentResult.unmatchedCount);
                        acceptBestMatch = false;
                        break;
                    }
                }
            }
        }
        if (acceptBestMatch) {
            log.trace("Partial match on screen {}", bestMatch);
            match = bestMatch;
        }
    }
    return match;
}
Also used : HashMap(java.util.HashMap) Screen(net.hearthstats.game.Screen) ScreenGroup(net.hearthstats.game.ScreenGroup)

Example 2 with Screen

use of net.hearthstats.game.Screen in project HearthStats.net-Uploader by HearthStats.

the class ScreenAnalyser method matchScreensForTesting.

@SuppressWarnings("unchecked")
EnumSet<Screen>[] matchScreensForTesting(BufferedImage image) {
    if (expectedWidth != image.getWidth() || expectedHeight != image.getHeight()) {
        pixelMap = calculatePixelPositions(image.getWidth(), image.getHeight());
        expectedWidth = image.getWidth();
        expectedHeight = image.getHeight();
    }
    EnumSet<Screen> primaryMatches = EnumSet.noneOf(Screen.class);
    EnumSet<Screen> secondaryMatches = EnumSet.noneOf(Screen.class);
    for (Screen screen : Screen.values()) {
        if (checkForExactMatch(image, screen)) {
            primaryMatches.add(screen);
            if (checkForMatchSecondary(image, screen)) {
                secondaryMatches.add(screen);
            }
        }
    }
    return new EnumSet[] { primaryMatches, secondaryMatches };
}
Also used : Screen(net.hearthstats.game.Screen) EnumSet(java.util.EnumSet)

Example 3 with Screen

use of net.hearthstats.game.Screen in project HearthStats.net-Uploader by HearthStats.

the class RelativePixelAnalyserMain method testFindRelativePixel.

public void testFindRelativePixel() throws Exception {
    ScreenAnalyser analyser = new ScreenAnalyser();
    RelativePixelAnalyser relativePixelAnalyser = new RelativePixelAnalyser();
    File imageFolder = new File(IMAGE_PATH);
    File[] imageArray = imageFolder.listFiles();
    Assert.assertNotNull("No files found in " + IMAGE_PATH + ". Please make sure you've set the path to a folder that contains screenshots from Hearthstone", imageArray);
    List<File> images = new ArrayList<>(imageArray.length);
    for (File image : imageArray) {
        if (image.getName().endsWith(".png")) {
            // Determine if this is a match end image
            BufferedImage bufferedImage = ImageIO.read(image);
            Screen screen = analyser.identifyScreen(bufferedImage, null);
            if (screen == Screen.MATCH_NAXXRAMAS_END || screen == Screen.MATCH_ORGRIMMAR_END || screen == Screen.MATCH_PANDARIA_END || screen == Screen.MATCH_STORMWIND_END || screen == Screen.MATCH_STRANGLETHORN_END) {
                // This is a match end screen, so it is suitable for testing with the RelativePixelAnalyser
                images.add(image);
            }
            bufferedImage.flush();
        }
    }
    Assert.assertFalse("No match end images found in " + IMAGE_PATH + ". Please make sure you've set the path to a folder that contains screenshots from Hearthstone", images.size() == 0);
    int page = 0;
    int pageCount = (images.size() / PAGE_SIZE) + 1;
    while (page < pageCount) {
        page++;
        String filename = IMAGE_PATH + "/relative-test-" + page + ".html";
        try (BufferedWriter output = new BufferedWriter(new FileWriter(filename))) {
            writeHtmlHeader(output, page, pageCount);
            List<Coordinate> coordinates = new ArrayList<>();
            for (int i = (page - 1) * PAGE_SIZE; i < images.size() && i < page * PAGE_SIZE; i++) {
                File image = images.get(i);
                output.write("<tr>" + "<td colspan=\"3\" class=\"filename\"><h2>");
                output.write(image.getName());
                output.write("</h2></td>" + "</tr>" + "<tr>" + "<td><div><img src=\"");
                output.write(image.getName());
                output.write("\" id=\"img_");
                output.write(String.valueOf(i - ((page - 1) * PAGE_SIZE)));
                output.write("\" alt=\"");
                output.write(image.getName());
                output.write("\" width=\"400\"></div></td>");
                output.write("<td><canvas id=\"canvas_");
                output.write(String.valueOf(i - ((page - 1) * PAGE_SIZE)));
                output.write("\" width=\"300\" height=\"300\"></td>");
                try {
                    log.debug("***** Testing Image {} *****", image.getName());
                    BufferedImage bufferedImage = ImageIO.read(image);
                    Coordinate coordinate = relativePixelAnalyser.findRelativePixel(bufferedImage, UniquePixel.VICTORY_DEFEAT_REFBOX_TL, UniquePixel.VICTORY_DEFEAT_REFBOX_BR, 8, 11);
                    coordinates.add(coordinate);
                    output.write("<td class=\"");
                    if (coordinate == null) {
                        output.write("matchzero");
                    } else {
                        output.write("matchone");
                    }
                    output.write("\">");
                    if (coordinate != null) {
                        output.write("<div>Reference Pixel = ");
                        output.write(String.valueOf(coordinate.x()));
                        output.write(", ");
                        output.write(String.valueOf(coordinate.y()));
                        output.write("</div>");
                        int victory1Matches = relativePixelAnalyser.countMatchingRelativePixels(bufferedImage, coordinate, new UniquePixel[] { UniquePixel.VICTORY_REL_1A, UniquePixel.VICTORY_REL_1B });
                        int victory2Matches = relativePixelAnalyser.countMatchingRelativePixels(bufferedImage, coordinate, new UniquePixel[] { UniquePixel.VICTORY_REL_2A, UniquePixel.VICTORY_REL_2B, UniquePixel.VICTORY_REL_2C });
                        int defeat1Matches = relativePixelAnalyser.countMatchingRelativePixels(bufferedImage, coordinate, new UniquePixel[] { UniquePixel.DEFEAT_REL_1A, UniquePixel.DEFEAT_REL_1B, UniquePixel.DEFEAT_REL_1C, UniquePixel.DEFEAT_REL_1D, UniquePixel.DEFEAT_REL_1E });
                        int defeat2Matches = relativePixelAnalyser.countMatchingRelativePixels(bufferedImage, coordinate, new UniquePixel[] { UniquePixel.DEFEAT_REL_2A });
                        output.write("<div>Count of V1 matches: ");
                        output.write(String.valueOf(victory1Matches));
                        output.write("</div>");
                        output.write("<div>Count of V2 matches: ");
                        output.write(String.valueOf(victory2Matches));
                        output.write("</div>");
                        output.write("<div>Count of D1 matches: ");
                        output.write(String.valueOf(defeat1Matches));
                        output.write("</div>");
                        output.write("<div>Count of D2 matches: ");
                        output.write(String.valueOf(defeat2Matches));
                        output.write("</div>");
                        if (victory1Matches > 0 && victory2Matches == 3 && defeat1Matches == 0 && defeat2Matches == 0) {
                            output.write("<div><b>MATCHED VICTORY</b></div>");
                        }
                        if (victory1Matches == 0 && victory2Matches == 0 && defeat1Matches > 0 && defeat2Matches == 1) {
                            output.write("<div><b>MATCHED DEFEAT</b></div>");
                        }
                    }
                    output.write("</td>");
                } catch (IOException e) {
                    log.warn("Cannot handle image " + image.getName() + " due to exception", e);
                    output.write("<b>Exception</b></td></tr>");
                }
                output.write("</tr>");
            }
            writeCanvasJavascript(output, coordinates);
            writeHtmlFooter(output, page, pageCount);
        } catch (IOException e) {
            Assert.fail("IOException writing file " + filename);
            throw e;
        }
    }
}
Also used : Screen(net.hearthstats.game.Screen) ArrayList(java.util.ArrayList) RelativePixelAnalyser(net.hearthstats.game.imageanalysis.RelativePixelAnalyser) BufferedImage(java.awt.image.BufferedImage) Coordinate(net.hearthstats.util.Coordinate) ScreenAnalyser(net.hearthstats.game.imageanalysis.ScreenAnalyser)

Aggregations

Screen (net.hearthstats.game.Screen)3 BufferedImage (java.awt.image.BufferedImage)1 ArrayList (java.util.ArrayList)1 EnumSet (java.util.EnumSet)1 HashMap (java.util.HashMap)1 ScreenGroup (net.hearthstats.game.ScreenGroup)1 RelativePixelAnalyser (net.hearthstats.game.imageanalysis.RelativePixelAnalyser)1 ScreenAnalyser (net.hearthstats.game.imageanalysis.ScreenAnalyser)1 Coordinate (net.hearthstats.util.Coordinate)1