diff --git a/.gitignore b/.gitignore index aef1e72..b44d8e1 100644 --- a/.gitignore +++ b/.gitignore @@ -38,3 +38,4 @@ out/ ### Debug stuff ### .debug +.DS_Store diff --git a/build.gradle b/build.gradle index b39a365..6c7e8d0 100644 --- a/build.gradle +++ b/build.gradle @@ -1,5 +1,5 @@ plugins { - id 'org.springframework.boot' version '2.5.0' + id 'org.springframework.boot' version '2.5.2' id 'io.spring.dependency-management' version '1.0.11.RELEASE' id 'org.sonarqube' version '3.3' id 'java' @@ -7,7 +7,7 @@ plugins { group = 'io.skyshard' version = '0.0.1-SNAPSHOT' -sourceCompatibility = 16 +sourceCompatibility = 15 configurations { compileOnly { diff --git a/src/main/java/io/skyshard/Application.java b/src/main/java/io/skyshard/Application.java index b2b6540..4d9d64a 100644 --- a/src/main/java/io/skyshard/Application.java +++ b/src/main/java/io/skyshard/Application.java @@ -14,14 +14,15 @@ @EnableConfigurationProperties(AppProperties.class) public class Application { - public static void main(final String[] args) { + public static void main(final String[] args) { - Loader.load(opencv_java.class); + Loader.load(opencv_java.class); - avutil.av_log_set_level(avutil.AV_LOG_QUIET); + avutil.av_log_set_level(avutil.AV_LOG_QUIET); + + final var builder = new SpringApplicationBuilder(Application.class); + builder.headless(false); + builder.run(args); + } - final SpringApplicationBuilder builder = new SpringApplicationBuilder(Application.class); - builder.headless(false); - builder.run(args); - } } diff --git a/src/main/java/io/skyshard/configuration/GrabConfig.java b/src/main/java/io/skyshard/configuration/GrabConfig.java index 506d994..743fa1b 100644 --- a/src/main/java/io/skyshard/configuration/GrabConfig.java +++ b/src/main/java/io/skyshard/configuration/GrabConfig.java @@ -1,5 +1,6 @@ package io.skyshard.configuration; +import io.skyshard.exceptions.UnsupportedSystemException; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.SystemUtils; import org.bytedeco.javacv.FFmpegFrameGrabber; @@ -19,9 +20,8 @@ public class GrabConfig { @Bean public FFmpegFrameGrabber grabber() { - final Dimension dimension = getDimension(); - - final FFmpegFrameGrabber grabber = getSystemSpecificGrabber(); + final var dimension = getDimension(); + final var grabber = getSystemSpecificGrabber(); grabber.setFrameRate(30); grabber.setImageWidth(dimension.width); @@ -42,12 +42,13 @@ private FFmpegFrameGrabber getSystemSpecificGrabber() { } if (SystemUtils.IS_OS_MAC) { - - // 2:0 represents the screen you want to grab, if primary display set as 1:0, if external monitor 2:0. + // On a Mac, the filename represents the screen you want to capture. In this case 2:0 is my second screen + // with no audio input, and 1:0 would be your primary screen with no audio input. There is an FFMPEG command + // you can execute to identify what screen is which (google it). return buildGrabber("2:0", "avfoundation"); } - throw new RuntimeException("Unsupported System. Exiting..."); + throw new UnsupportedSystemException(); } /** @@ -55,7 +56,7 @@ private FFmpegFrameGrabber getSystemSpecificGrabber() { */ private FFmpegFrameGrabber buildGrabber(final String filename, final String format) { - FFmpegFrameGrabber grabber = new FFmpegFrameGrabber(filename); + final var grabber = new FFmpegFrameGrabber(filename); grabber.setFormat(format); return grabber; @@ -68,8 +69,7 @@ private Dimension getDimension() { final var dimension = Toolkit.getDefaultToolkit().getScreenSize(); - log.info( - "Using screen dimensions {}x{} as reference points", dimension.width, dimension.height); + log.info("Screen dimensions {}x{}", dimension.width, dimension.height); return dimension; } diff --git a/src/main/java/io/skyshard/configuration/RobotConfig.java b/src/main/java/io/skyshard/configuration/RobotConfig.java index 801b1bf..20f68f0 100644 --- a/src/main/java/io/skyshard/configuration/RobotConfig.java +++ b/src/main/java/io/skyshard/configuration/RobotConfig.java @@ -1,26 +1,28 @@ package io.skyshard.configuration; -import java.awt.*; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import java.awt.*; + @Configuration public class RobotConfig { - /** - * Create a singleton robot, the robot is used for moving the mouse pointer as well as keyboard - * inputs. - */ - @Bean - public Robot robot() { + /** + * Create a singleton robot, the robot is used for moving the mouse pointer as well as keyboard + * inputs. + */ + @Bean + public Robot robot() { - try { - final var robot = new Robot(); - robot.setAutoWaitForIdle(true); + try { + final var robot = new Robot(); + robot.setAutoWaitForIdle(true); - return robot; - } catch (final Exception exception) { - throw new RuntimeException(exception.getMessage()); + return robot; + } catch (final AWTException exception) { + throw new RuntimeException(exception.getMessage()); + } } - } + } diff --git a/src/main/java/io/skyshard/configuration/TemplateConfig.java b/src/main/java/io/skyshard/configuration/TemplateConfig.java index cb117c1..ab343f6 100644 --- a/src/main/java/io/skyshard/configuration/TemplateConfig.java +++ b/src/main/java/io/skyshard/configuration/TemplateConfig.java @@ -1,7 +1,7 @@ package io.skyshard.configuration; +import io.skyshard.exceptions.MissingTemplateException; import io.skyshard.properties.AppProperties; -import java.io.FileNotFoundException; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.opencv.core.Mat; @@ -10,31 +10,34 @@ import org.springframework.context.annotation.Configuration; import org.springframework.util.ResourceUtils; +import java.io.FileNotFoundException; + @Slf4j @Configuration @RequiredArgsConstructor public class TemplateConfig { - private final AppProperties appProperties; + private final AppProperties appProperties; - /** - * Read in the matching template file from the resource directory. If this can't be found or its - * not set in the application properties file, exit the application. - */ - @Bean - public Mat loadTemplate() { + /** + * Read in the matching template file from the resource directory. If this can't be found or its + * not set in the application properties file, exit the application. + */ + @Bean + public Mat loadTemplate() { - try { - final var templateSource = - ResourceUtils.getFile(String.format("classpath:%s", appProperties.getTemplate())); + try { + final var templateSource = + ResourceUtils.getFile(String.format("classpath:%s", appProperties.getTemplate())); - log.info("Using template file {}", templateSource.getAbsolutePath()); + log.info("Using template file {}", templateSource.getAbsolutePath()); - return Imgcodecs.imread(templateSource.getAbsolutePath()); + return Imgcodecs.imread(templateSource.getAbsolutePath()); - } catch (final FileNotFoundException fileNotFoundException) { - log.error("Cannot find template file {}, exiting...", appProperties.getTemplate()); - throw new RuntimeException(fileNotFoundException.getMessage()); + } catch (final FileNotFoundException fileNotFoundException) { + log.error("Cannot find template file {}, exiting...", appProperties.getTemplate()); + throw new MissingTemplateException(); + } } - } + } diff --git a/src/main/java/io/skyshard/domain/Target.java b/src/main/java/io/skyshard/domain/Target.java index 58872fb..d231a3d 100644 --- a/src/main/java/io/skyshard/domain/Target.java +++ b/src/main/java/io/skyshard/domain/Target.java @@ -4,28 +4,31 @@ import lombok.Data; import org.opencv.core.Point; -/** Represents where a target is found on the screen. */ +/** + * Represents where a target is found on the screen. + */ @Data @Builder public class Target { - private Point point; + private Point point; - /** - * Get the X location of the point. Since we're talking about pixels here we don't care about the - * decimal point. - */ - public int x() { + /** + * Get the X location of the point. Since we're talking about pixels here we don't care about the + * decimal point. + */ + public int x() { - return (int) Math.round(this.point.x); - } + return (int) Math.round(this.point.x); + } - /** - * Get the Y location of the point. Since we're talking about pixels here we don't care about the - * decimal point. - */ - public int y() { + /** + * Get the Y location of the point. Since we're talking about pixels here we don't care about the + * decimal point. + */ + public int y() { + + return (int) Math.round(this.point.y); + } - return (int) Math.round(this.point.y); - } } diff --git a/src/main/java/io/skyshard/exceptions/MissingTemplateException.java b/src/main/java/io/skyshard/exceptions/MissingTemplateException.java new file mode 100644 index 0000000..8481182 --- /dev/null +++ b/src/main/java/io/skyshard/exceptions/MissingTemplateException.java @@ -0,0 +1,9 @@ +package io.skyshard.exceptions; + +public class MissingTemplateException extends RuntimeException { + + public MissingTemplateException() { + super("Unable to find a template to match on. Exiting..."); + } + +} diff --git a/src/main/java/io/skyshard/exceptions/UnsupportedSystemException.java b/src/main/java/io/skyshard/exceptions/UnsupportedSystemException.java new file mode 100644 index 0000000..2d13c1d --- /dev/null +++ b/src/main/java/io/skyshard/exceptions/UnsupportedSystemException.java @@ -0,0 +1,9 @@ +package io.skyshard.exceptions; + +public class UnsupportedSystemException extends RuntimeException { + + public UnsupportedSystemException() { + super("Attempting to run on an unsupported system. Exiting..."); + } + +} diff --git a/src/main/java/io/skyshard/properties/AppProperties.java b/src/main/java/io/skyshard/properties/AppProperties.java index 5c83b6d..1d5b9d1 100644 --- a/src/main/java/io/skyshard/properties/AppProperties.java +++ b/src/main/java/io/skyshard/properties/AppProperties.java @@ -7,21 +7,30 @@ @ConfigurationProperties("application") public class AppProperties { - /** Includes writing images to disk, don't run in production Default: false */ - private boolean debug = false; + /** + * Includes writing images to disk, don't run in production Default: false + */ + private boolean debug = false; - /** Template file location relative to resource root. Default: /templates/default.png */ - private String template = "templates/default-1.png"; + /** + * Template file location relative to resource root. Default: /templates/default.png + */ + private String template = "templates/default-1.png"; - /** If true, only a single target is processed before a new image is scanned. Default: false */ - private boolean singleTargetMode = true; + /** + * If true, only a single target is processed before a new image is scanned. Default: false + */ + private boolean singleTargetMode = true; - /** Threshold used when searching for a template match via OpenCV. Default: 0.90 */ - private double matchThreshold = 0.90; + /** + * Threshold used when searching for a template match via OpenCV. Default: 0.90 + */ + private double matchThreshold = 0.90; + + /** + * How many pixels on X and Y to check for the same template image. This stops the same target + * being picked up many times. Default: 30 + */ + private double duplicateThreshold = 30; - /** - * How many pixels on X and Y to check for the same template image. This stops the same target - * being picked up many times. Default: 30 - */ - private double duplicateThreshold = 30; } diff --git a/src/main/java/io/skyshard/services/AttackService.java b/src/main/java/io/skyshard/services/AttackService.java index 9e733f4..d021dd7 100644 --- a/src/main/java/io/skyshard/services/AttackService.java +++ b/src/main/java/io/skyshard/services/AttackService.java @@ -4,5 +4,6 @@ public interface AttackService { - void attack(Target target); + void attack(Target target); + } diff --git a/src/main/java/io/skyshard/services/AttackServiceImpl.java b/src/main/java/io/skyshard/services/AttackServiceImpl.java index 2783267..c187a15 100644 --- a/src/main/java/io/skyshard/services/AttackServiceImpl.java +++ b/src/main/java/io/skyshard/services/AttackServiceImpl.java @@ -14,19 +14,20 @@ @RequiredArgsConstructor public class AttackServiceImpl implements AttackService { - private final Robot robot; + private final Robot robot; - /** - * Move the mouse to the target location, then offset it slightly to counter the fact we always - * find the very bottom right/left corner of a template match. Once the mouse has been moved over - * the target, press the '1' keyboard button. - */ - @Override - public void attack(final Target target) { + /** + * Move the mouse to the target location, then offset it slightly to counter the fact we always + * find the very bottom right/left corner of a template match. Once the mouse has been moved over + * the target, press the '1' keyboard button. + */ + @Override + public void attack(final Target target) { + + robot.mouseMove(target.x() - 5, target.y() - 5); + robot.keyPress(KeyEvent.VK_1); + robot.delay(MathUtils.random(25, 50)); + robot.keyRelease(KeyEvent.VK_1); + } - robot.mouseMove(target.x() - 5, target.y() - 5); - robot.keyPress(KeyEvent.VK_1); - robot.delay(MathUtils.random(25, 50)); - robot.keyRelease(KeyEvent.VK_1); - } } diff --git a/src/main/java/io/skyshard/services/FindTargetService.java b/src/main/java/io/skyshard/services/FindTargetService.java index db24395..0fb01c8 100644 --- a/src/main/java/io/skyshard/services/FindTargetService.java +++ b/src/main/java/io/skyshard/services/FindTargetService.java @@ -1,13 +1,15 @@ package io.skyshard.services; import io.skyshard.domain.Target; +import org.opencv.core.Mat; + import java.util.List; import java.util.Optional; -import org.opencv.core.Mat; public interface FindTargetService { - List findMultipleTarget(Mat source); + List findMultipleTarget(Mat source); + + Optional findSingleTarget(Mat source); - Optional findSingleTarget(Mat source); } diff --git a/src/main/java/io/skyshard/services/FindTargetServiceImpl.java b/src/main/java/io/skyshard/services/FindTargetServiceImpl.java index a1d4bdc..1df53a0 100644 --- a/src/main/java/io/skyshard/services/FindTargetServiceImpl.java +++ b/src/main/java/io/skyshard/services/FindTargetServiceImpl.java @@ -22,131 +22,119 @@ @RequiredArgsConstructor public class FindTargetServiceImpl implements FindTargetService { - private final AppProperties appProperties; + private final AppProperties appProperties; - private final Mat template; + private final Mat template; - @Override - public List findMultipleTarget(final Mat source) { + @Override + public List findMultipleTarget(final Mat source) { - return processMultipleTargets(source); - } - - @Override - public Optional findSingleTarget(final Mat source) { + return processMultipleTargets(source); + } - return processSingleTarget(source); - } + @Override + public Optional findSingleTarget(final Mat source) { - /** - * Find the first target on the screen. This works better with fast moving targets since you're - * just hitting one before looking again to see if anything has moved. - */ - private Optional processSingleTarget(final Mat source) { + return processSingleTarget(source); + } - final var result = matchTemplate(source); - final var minMaxLocResult = Core.minMaxLoc(result); - final var maxLoc = minMaxLocResult.maxLoc; - final var point = new Point(maxLoc.x + template.cols(), maxLoc.y + template.rows()); + /** + * Find the first target on the screen. This works better with fast moving targets since you're + * just hitting one before looking again to see if anything has moved. + */ + private Optional processSingleTarget(final Mat source) { - return minMaxLocResult.maxVal >= appProperties.getMatchThreshold() - ? Optional.of(Target.builder().point(point).build()) - : Optional.empty(); - } + final var result = matchTemplate(source); + final var minMaxLocResult = Core.minMaxLoc(result); + final var maxLoc = minMaxLocResult.maxLoc; + final var point = new Point(maxLoc.x + template.cols(), maxLoc.y + template.rows()); - /** - * Find all the targets on the screen. This is probably faster if you've got a lot of targets on - * the screen which don't move. You can attack each one before taking a new screenshot. - * - *

If you want to debug the screenshot and matching, you'll need to use this method for - * outputs. - */ - private List processMultipleTargets(final Mat source) { + return minMaxLocResult.maxVal >= appProperties.getMatchThreshold() + ? Optional.of(Target.builder().point(point).build()) + : Optional.empty(); + } - final var result = matchTemplate(source); - final List targets = new ArrayList<>(); + /** + * Find all the targets on the screen. This is probably faster if you've got a lot of targets on + * the screen which don't move. You can attack each one before taking a new screenshot. + * + *

If you want to debug the screenshot and matching, you'll need to use this method for + * outputs. + */ + private List processMultipleTargets(final Mat source) { - double maxValue; - Mat destination; + final var result = matchTemplate(source); + final List targets = new ArrayList<>(); - while (true) { - final var minMaxLocResult = Core.minMaxLoc(result); - final var maxLoc = minMaxLocResult.maxLoc; + double maxValue; + Mat destination; - maxValue = minMaxLocResult.maxVal; - destination = source.clone(); + while (true) { + final var minMaxLocResult = Core.minMaxLoc(result); + final var maxLoc = minMaxLocResult.maxLoc; - if (maxValue >= appProperties.getMatchThreshold()) { - final Point point = new Point(maxLoc.x + template.cols(), maxLoc.y + template.rows()); + maxValue = minMaxLocResult.maxVal; + destination = source.clone(); - // Update the pointer location to the next target - Imgproc.rectangle(result, maxLoc, point, new Scalar(0, 255, 0), -1); - drawMatch(source, maxLoc, point); + if (maxValue >= appProperties.getMatchThreshold()) { + final var point = new Point(maxLoc.x + template.cols(), maxLoc.y + template.rows()); - if (isDuplicate(targets, point)) { - continue; - } + // Update the pointer location to the next target + Imgproc.rectangle(result, maxLoc, point, new Scalar(0, 255, 0), -1); - targets.add(Target.builder().point(point).build()); - } else { - break; - } - } + if(appProperties.isDebug()) { + MatUtils.drawRectangle(source, maxLoc, point); + } - if (appProperties.isDebug()) { - MatUtils.write(destination, "match"); - } + if (isDuplicate(targets, point)) { + continue; + } - return targets; - } + targets.add(Target.builder().point(point).build()); + } else { + break; + } + } - /** Preform the match between the source and the template file. */ - private Mat matchTemplate(final Mat source) { + if (appProperties.isDebug()) { + MatUtils.write(destination, "match"); + } - final Mat result = new Mat(); + return targets; + } - final Mat normalizeSource = MatUtils.normalize(source); - final Mat normalizeSample = MatUtils.normalize(template); - final Mat normalizeResult = MatUtils.normalize(result); + /** + * Preform the match between the source and the template file. + */ + private Mat matchTemplate(final Mat source) { - Imgproc.matchTemplate( - normalizeSource, normalizeSample, normalizeResult, Imgproc.TM_CCOEFF_NORMED); - Imgproc.threshold(normalizeResult, normalizeResult, 0.1, 1, Imgproc.THRESH_TOZERO); + final var result = new Mat(); + final var normalizeSource = MatUtils.normalize(source); + final var normalizeSample = MatUtils.normalize(template); + final var normalizeResult = MatUtils.normalize(result); - return normalizeResult; - } + Imgproc.matchTemplate(normalizeSource, normalizeSample, normalizeResult, Imgproc.TM_CCOEFF_NORMED); + Imgproc.threshold(normalizeResult, normalizeResult, 0.1, 1, Imgproc.THRESH_TOZERO); - /** Draw on the source image the location of the match. */ - private void drawMatch(final Mat source, final Point maxLoc, final Point point) { + return normalizeResult; + } - if (appProperties.isDebug()) { - Imgproc.rectangle(source, maxLoc, point, new Scalar(0, 255, 0), 5); - Imgproc.putText( - source, - point.x + "," + point.y, - point, - Imgproc.FONT_HERSHEY_PLAIN, - 2.0, - new Scalar(0, 255, 0), - 1); + /** + * Decide if a template match is duplicate based on it being within proximity to another match. + * Only used for {@link FindTargetService#findMultipleTarget(Mat)}. + */ + private boolean isDuplicate(final List targets, final Point point) { + + final double threshold = appProperties.getDuplicateThreshold(); + + return targets.stream() + .anyMatch( + target -> + Range.between(target.getPoint().x - threshold, target.getPoint().x + threshold) + .contains(point.x) + && Range.between( + target.getPoint().y - threshold, target.getPoint().y + threshold) + .contains(point.y)); } - } - - /** - * Decide if a template match is duplicate based on it being within proximity to another match. - * Only used for {@link FindTargetService#findMultipleTarget(Mat)}. - */ - private boolean isDuplicate(final List targets, final Point point) { - - final double threshold = appProperties.getDuplicateThreshold(); - - return targets.stream() - .anyMatch( - target -> - Range.between(target.getPoint().x - threshold, target.getPoint().x + threshold) - .contains(point.x) - && Range.between( - target.getPoint().y - threshold, target.getPoint().y + threshold) - .contains(point.y)); - } + } diff --git a/src/main/java/io/skyshard/services/ScreenshotService.java b/src/main/java/io/skyshard/services/ScreenshotService.java index 7f5fe42..0712073 100644 --- a/src/main/java/io/skyshard/services/ScreenshotService.java +++ b/src/main/java/io/skyshard/services/ScreenshotService.java @@ -4,5 +4,6 @@ public interface ScreenshotService { - Mat take(); + Mat take(); + } diff --git a/src/main/java/io/skyshard/services/ScreenshotServiceImpl.java b/src/main/java/io/skyshard/services/ScreenshotServiceImpl.java index 09c74a7..f6b3516 100644 --- a/src/main/java/io/skyshard/services/ScreenshotServiceImpl.java +++ b/src/main/java/io/skyshard/services/ScreenshotServiceImpl.java @@ -16,28 +16,29 @@ @RequiredArgsConstructor public class ScreenshotServiceImpl implements ScreenshotService { - private final FFmpegFrameGrabber grabber; + private final FFmpegFrameGrabber grabber; - private final AppProperties appProperties; + private final AppProperties appProperties; - private final ToOrgOpenCvCoreMat toCore = new ToOrgOpenCvCoreMat(); + private final ToOrgOpenCvCoreMat toCore = new ToOrgOpenCvCoreMat(); - @Override - @SneakyThrows - public Mat take() { + @Override + @SneakyThrows + public Mat take() { - if (!grabber.isCloseInputStream()) { - grabber.stop(); - } + if (!grabber.isCloseInputStream()) { + grabber.stop(); + } + + grabber.start(); - grabber.start(); + final Mat screenshot = toCore.convert(grabber.grabImage()); - final Mat screenshot = toCore.convert(grabber.grabImage()); + if (appProperties.isDebug()) { + MatUtils.write(screenshot, "screenshot"); + } - if (appProperties.isDebug()) { - MatUtils.write(screenshot, "screenshot"); + return screenshot; } - return screenshot; - } } diff --git a/src/main/java/io/skyshard/tasks/StartUp.java b/src/main/java/io/skyshard/tasks/StartUp.java index 23a9661..5a0f2a6 100644 --- a/src/main/java/io/skyshard/tasks/StartUp.java +++ b/src/main/java/io/skyshard/tasks/StartUp.java @@ -12,23 +12,24 @@ @RequiredArgsConstructor public class StartUp { - private final AppProperties appProperties; + private final AppProperties appProperties; - private final ScreenshotService screenshotService; + private final ScreenshotService screenshotService; - private final FindTargetService findTargetService; + private final FindTargetService findTargetService; - private final AttackService attackService; + private final AttackService attackService; - @Scheduled(fixedDelay = 250, initialDelay = 0) - public void process() { + @Scheduled(fixedDelay = 250, initialDelay = 0) + public void process() { - final var screenshot = screenshotService.take(); + final var screenshot = screenshotService.take(); - if (appProperties.isSingleTargetMode()) { - findTargetService.findSingleTarget(screenshot).ifPresent((attackService::attack)); - } else { - findTargetService.findMultipleTarget(screenshot).forEach(attackService::attack); + if (appProperties.isSingleTargetMode()) { + findTargetService.findSingleTarget(screenshot).ifPresent((attackService::attack)); + } else { + findTargetService.findMultipleTarget(screenshot).forEach(attackService::attack); + } } - } + } diff --git a/src/main/java/io/skyshard/utils/MatUtils.java b/src/main/java/io/skyshard/utils/MatUtils.java index f1abaa9..fa01059 100644 --- a/src/main/java/io/skyshard/utils/MatUtils.java +++ b/src/main/java/io/skyshard/utils/MatUtils.java @@ -2,13 +2,11 @@ import lombok.SneakyThrows; import lombok.experimental.UtilityClass; -import org.opencv.core.Core; -import org.opencv.core.CvType; -import org.opencv.core.Mat; +import org.opencv.core.*; import org.opencv.imgcodecs.Imgcodecs; +import org.opencv.imgproc.Imgproc; import java.nio.file.Files; -import java.nio.file.Path; import java.nio.file.Paths; import java.text.MessageFormat; import java.time.Instant; @@ -16,25 +14,39 @@ @UtilityClass public class MatUtils { - /** - * Given a Mat image, make sure its as basic as it can be and is the same as all other images. - * This is important with matchTemplating as if the number of channels are different things can - * get awkward and hard to debug. - */ - public Mat normalize(final Mat mat) { + /** + * Given a Mat image, make sure its as basic as it can be and is the same as all other images. + * This is important with matchTemplating as if the number of channels are different things can + * get awkward and hard to debug. + */ + public Mat normalize(final Mat mat) { - var result = new Mat(); - Core.normalize(mat, result, 0, 255, Core.NORM_MINMAX, CvType.CV_8UC1); + final var result = new Mat(); + Core.normalize(mat, result, 0, 255, Core.NORM_MINMAX, CvType.CV_8UC1); - return result; - } + return result; + } - @SneakyThrows - public void write(final Mat mat, final String name) { + @SneakyThrows + public void write(final Mat mat, final String name) { - final Path path = Files.createDirectories(Paths.get("./.debug")); + final var path = Files.createDirectories(Paths.get("./.debug")); + + Imgcodecs.imwrite( + MessageFormat.format("{0}/{1}-{2}.jpg", path, Instant.now().toEpochMilli(), name), mat); + } + + public void drawRectangle(final Mat source, final Point maxLoc, final Point point) { + + Imgproc.rectangle(source, maxLoc, point, new Scalar(0, 255, 0), 5); + Imgproc.putText( + source, + point.x + "," + point.y, + point, + Imgproc.FONT_HERSHEY_PLAIN, + 2.0, + new Scalar(0, 255, 0), + 1); + } - Imgcodecs.imwrite( - MessageFormat.format(path + "/{0}-{1}.jpg", Instant.now().toEpochMilli(), name), mat); - } } diff --git a/src/main/java/io/skyshard/utils/MathUtils.java b/src/main/java/io/skyshard/utils/MathUtils.java index 3011b3b..9d8c18a 100644 --- a/src/main/java/io/skyshard/utils/MathUtils.java +++ b/src/main/java/io/skyshard/utils/MathUtils.java @@ -1,16 +1,20 @@ package io.skyshard.utils; -import java.util.Random; import lombok.experimental.UtilityClass; +import java.util.Random; + @UtilityClass public class MathUtils { - private static final Random random = new Random(); + private static final Random random = new Random(); + + /** + * Return a random number between the two values. + */ + public int random(final int min, final int max) { - /** Return a random number between the two values. */ - public int random(final int min, final int max) { + return random.nextInt(max - min) + min; + } - return random.nextInt(max - min) + min; - } }