package com.shootoff.camera; import static org.junit.Assert.*; import java.awt.Dimension; import java.awt.image.BufferedImage; import java.io.File; import java.io.IOException; import java.util.Optional; import javafx.geometry.Bounds; import javax.imageio.ImageIO; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ErrorCollector; import org.opencv.core.Mat; import org.opencv.core.MatOfPoint2f; import com.shootoff.camera.autocalibration.AutoCalibrationManager; import com.shootoff.camera.shotdetection.JavaShotDetector; import com.shootoff.config.Configuration; import com.shootoff.config.ConfigurationException; import com.shootoff.gui.CalibrationConfigurator; import com.shootoff.gui.CalibrationManager; import com.shootoff.gui.CalibrationOption; import com.shootoff.gui.ExerciseListener; import com.shootoff.gui.MockCanvasManager; import com.shootoff.gui.pane.ProjectorArenaPane; import com.shootoff.plugins.TrainingExercise; import com.shootoff.plugins.engine.PluginEngine; public class TestAutoCalibration implements VideoFinishedListener { private AutoCalibrationManager acm; private Configuration config; private MockCanvasManager mockCanvasManager; private boolean[][] sectorStatuses; private MockCamera mockCamera = new MockCamera(); @Rule public ErrorCollector collector = new ErrorCollector(); @Before public void setUp() throws ConfigurationException { nu.pattern.OpenCV.loadShared(); acm = new AutoCalibrationManager(new MockCameraManager(), mockCamera, false); config = new Configuration(new String[0]); // Minimize logging attempts because Travis-CI will kill us // due to verbose output. config.setDebugMode(false); mockCanvasManager = new MockCanvasManager(config, true); sectorStatuses = new boolean[JavaShotDetector.SECTOR_ROWS][JavaShotDetector.SECTOR_COLUMNS]; for (int x = 0; x < JavaShotDetector.SECTOR_COLUMNS; x++) { for (int y = 0; y < JavaShotDetector.SECTOR_ROWS; y++) { sectorStatuses[y][x] = true; } } } Object processingLock = new Object(); private MockCameraManager autoCalibrationVideo(String videoPath) { File videoFile = new File(TestAutoCalibration.class.getResource(videoPath).getFile()); MockCameraManager cameraManager; cameraManager = new MockCameraManager(new MockCamera(videoFile), mockCanvasManager, sectorStatuses, Optional.empty(), this); mockCanvasManager.setCameraManager(cameraManager); ProjectorArenaPane pac = new ProjectorArenaPane(config, mockCanvasManager); cameraManager.setCalibrationManager(new CalibrationManager(new CalibrationConfigurator() { @Override public void toggleCalibrating(boolean isCalibrating) {} @Override public CalibrationOption getCalibratedFeedBehavior() { return CalibrationOption.ONLY_IN_BOUNDS; } @Override public void calibratedFeedBehaviorsChanged() {} }, cameraManager, pac, null, null, new ExerciseListener() { @Override public void setProjectorExercise(TrainingExercise exercise) {} @Override public void setExercise(TrainingExercise exercise) {} @Override public PluginEngine getPluginEngine() { return null; } })); cameraManager.enableAutoCalibration(false); cameraManager.setDetecting(false); cameraManager.setDetectionLockState(true); cameraManager.start(); try { synchronized (processingLock) { processingLock.wait(); } } catch (InterruptedException e) { e.printStackTrace(); } return cameraManager; } @Test public void testCalibrateProjection() throws IOException { acm.reset(); BufferedImage testFrame = ImageIO .read(TestAutoCalibration.class.getResourceAsStream("/autocalibration/calibrate-projection.png")); mockCamera.setViewSize(new Dimension(testFrame.getWidth(), testFrame.getHeight())); final Mat mat = acm.prepTestFrame(testFrame); // Step 1: Find the chessboard corners final Optional<MatOfPoint2f> boardCorners = acm.findChessboard(mat); assertTrue(boardCorners.isPresent()); Optional<Bounds> calibrationBounds = acm.calibrateFrame(boardCorners.get(), mat); assertTrue(calibrationBounds.isPresent()); assertEquals(113, calibrationBounds.get().getMinX(), 1.0); assertEquals(32, calibrationBounds.get().getMinY(), 1.0); assertEquals(422, calibrationBounds.get().getWidth(), 1.0); assertEquals(316, calibrationBounds.get().getHeight(), 1.0); BufferedImage resultFrame = acm.undistortFrame(testFrame); // File outputfile = new File("calibrate-projection-result.png"); // ImageIO.write(resultFrame, "png", outputfile); BufferedImage compareFrame = ImageIO.read( TestAutoCalibration.class.getResourceAsStream("/autocalibration/calibrate-projection-result.png")); assertEquals(true, compareImages(compareFrame, resultFrame)); } @Test public void testCalibrateProjection2() throws IOException { acm.reset(); BufferedImage testFrame = ImageIO .read(TestAutoCalibration.class.getResourceAsStream("/autocalibration/calibrate-projection-2.png")); mockCamera.setViewSize(new Dimension(testFrame.getWidth(), testFrame.getHeight())); final Mat mat = acm.prepTestFrame(testFrame); // Step 1: Find the chessboard corners final Optional<MatOfPoint2f> boardCorners = acm.findChessboard(mat); assertTrue(boardCorners.isPresent()); Optional<Bounds> calibrationBounds = acm.calibrateFrame(boardCorners.get(), mat); assertTrue(calibrationBounds.isPresent()); assertEquals(113, calibrationBounds.get().getMinX(), 1.0); assertEquals(34, calibrationBounds.get().getMinY(), 1.0); assertEquals(420, calibrationBounds.get().getWidth(), 1.0); assertEquals(316, calibrationBounds.get().getHeight(), 1.0); BufferedImage resultFrame = acm.undistortFrame(testFrame); // File outputfile = new File("calibrate-projection-2-result.png"); // ImageIO.write(resultFrame, "png", outputfile); BufferedImage compareFrame = ImageIO.read( TestAutoCalibration.class.getResourceAsStream("/autocalibration/calibrate-projection-2-result.png")); assertEquals(true, compareImages(compareFrame, resultFrame)); } @Test public void testCalibrateProjectionCutoff() throws IOException { acm.reset(); BufferedImage testFrame = ImageIO.read( TestAutoCalibration.class.getResourceAsStream("/autocalibration/calibrate-projection-cutoff.png")); mockCamera.setViewSize(new Dimension(testFrame.getWidth(), testFrame.getHeight())); final Mat mat = acm.prepTestFrame(testFrame); // Step 1: Find the chessboard corners final Optional<MatOfPoint2f> boardCorners = acm.findChessboard(mat); assertTrue(boardCorners.isPresent()); Optional<Bounds> calibrationBounds = acm.calibrateFrame(boardCorners.get(), mat); assertEquals(false, calibrationBounds.isPresent()); } @Test public void testCalibrateTightPatternUpsidedown() throws IOException { acm.reset(); BufferedImage testFrame = ImageIO.read(TestAutoCalibration.class .getResourceAsStream("/autocalibration/tight-calibration-pattern-upsidedown.png")); mockCamera.setViewSize(new Dimension(testFrame.getWidth(), testFrame.getHeight())); final Mat mat = acm.prepTestFrame(testFrame); final Optional<MatOfPoint2f> boardCorners = acm.findChessboard(mat); assertTrue(boardCorners.isPresent()); Optional<Bounds> calibrationBounds = acm.calibrateFrame(boardCorners.get(), mat); assertTrue(calibrationBounds.isPresent()); } @Test public void testCalibrateTightPatternCutOff() throws IOException { acm.reset(); BufferedImage testFrame = ImageIO.read( TestAutoCalibration.class.getResourceAsStream("/autocalibration/tight-calibration-pattern-cutoff.png")); mockCamera.setViewSize(new Dimension(testFrame.getWidth(), testFrame.getHeight())); final Mat mat = acm.prepTestFrame(testFrame); // Step 1: Find the chessboard corners final Optional<MatOfPoint2f> boardCorners = acm.findChessboard(mat); assertTrue(boardCorners.isPresent()); Optional<Bounds> calibrationBounds = acm.calibrateFrame(boardCorners.get(), mat); assertEquals(false, calibrationBounds.isPresent()); } @Test public void testCalibrateTightPattern() throws IOException { acm.reset(); BufferedImage testFrame = ImageIO .read(TestAutoCalibration.class.getResourceAsStream("/autocalibration/tight-calibration-pattern.png")); mockCamera.setViewSize(new Dimension(testFrame.getWidth(), testFrame.getHeight())); final Mat mat = acm.prepTestFrame(testFrame); // Step 1: Find the chessboard corners final Optional<MatOfPoint2f> boardCorners = acm.findChessboard(mat); assertTrue(boardCorners.isPresent()); Optional<Bounds> calibrationBounds = acm.calibrateFrame(boardCorners.get(), mat); assertTrue(calibrationBounds.isPresent()); assertEquals(45, calibrationBounds.get().getMinX(), 1.0); assertEquals(25, calibrationBounds.get().getMinY(), 1.0); assertEquals(570, calibrationBounds.get().getWidth(), 1.0); assertEquals(431, calibrationBounds.get().getHeight(), 1.0); BufferedImage resultFrame = acm.undistortFrame(testFrame); // File outputfile = new File("tight-calibration-pattern-result.png"); // ImageIO.write(resultFrame, "png", outputfile); BufferedImage compareFrame = ImageIO.read( TestAutoCalibration.class.getResourceAsStream("/autocalibration/tight-calibration-pattern-result.png")); assertEquals(true, compareImages(compareFrame, resultFrame)); } @Test public void testCalibrateOffEdgeImage() throws IOException { acm.reset(); BufferedImage testFrame = ImageIO.read( TestAutoCalibration.class.getResourceAsStream("/autocalibration/calibration_crash_shortpatter.png")); mockCamera.setViewSize(new Dimension(testFrame.getWidth(), testFrame.getHeight())); final Mat mat = acm.prepTestFrame(testFrame); // Step 1: Find the chessboard corners final Optional<MatOfPoint2f> boardCorners = acm.findChessboard(mat); assertTrue(boardCorners.isPresent()); Optional<Bounds> calibrationBounds = acm.calibrateFrame(boardCorners.get(), mat); assertFalse(calibrationBounds.isPresent()); } @Test public void testCalibrateTightPatternTurned() throws IOException { BufferedImage testFrame = ImageIO.read( TestAutoCalibration.class.getResourceAsStream("/autocalibration/tight-calibration-pattern-turned.png")); mockCamera.setViewSize(new Dimension(testFrame.getWidth(), testFrame.getHeight())); final Mat mat = acm.prepTestFrame(testFrame); final Optional<MatOfPoint2f> boardCorners = acm.findChessboard(mat); assertTrue(boardCorners.isPresent()); Optional<Bounds> calibrationBounds = acm.calibrateFrame(boardCorners.get(), mat); assertTrue(calibrationBounds.isPresent()); assertEquals(137, calibrationBounds.get().getMinX(), 1.0); assertEquals(66, calibrationBounds.get().getMinY(), 1.0); assertEquals(402, calibrationBounds.get().getWidth(), 1.0); assertEquals(280, calibrationBounds.get().getHeight(), 1.0); BufferedImage resultFrame = acm.undistortFrame(testFrame); // File outputfile = new // File("tight-calibration-pattern-turned-result.png"); // ImageIO.write(resultFrame, "png", outputfile); BufferedImage compareFrame = ImageIO.read(TestAutoCalibration.class .getResourceAsStream("/autocalibration/tight-calibration-pattern-turned-result.png")); assertEquals(true, compareImages(compareFrame, resultFrame)); } @Test public void testCalibrateHighRes() throws IOException { CameraManager result = autoCalibrationVideo("/autocalibration/highres-autocalibration-1280x720.mp4"); assertEquals(true, result.cameraAutoCalibrated); } @Test public void testCalibrateWithPaperPattern() throws IOException { MockCameraManager result = autoCalibrationVideo("/autocalibration/calibrate-projection-paper-ifly53e.mp4"); assertEquals(true, result.cameraAutoCalibrated); assertEquals(75.84, result.getACM().getPaperDimensions().get().getWidth(), 1); assertEquals(56.00, result.getACM().getPaperDimensions().get().getHeight(), 1); } @Test public void testCalibrateWithPaperPattern2() throws IOException { MockCameraManager result = autoCalibrationVideo("/autocalibration/calibrate-projection-paper-ifly53e-2.mp4"); assertEquals(true, result.cameraAutoCalibrated); assertEquals(75.80, result.getACM().getPaperDimensions().get().getWidth(), 1); assertEquals(57.15, result.getACM().getPaperDimensions().get().getHeight(), 1); } @Test public void testCalibrateProjectionPaper() throws IOException { acm.reset(); BufferedImage testFrame = ImageIO .read(TestAutoCalibration.class.getResourceAsStream("/autocalibration/calibrate-projection-paper.png")); mockCamera.setViewSize(new Dimension(testFrame.getWidth(), testFrame.getHeight())); acm.processFrame(new Frame(testFrame, 2000)); Optional<Bounds> calibrationBounds = Optional.of(acm.getBoundsResult()); assertTrue(calibrationBounds.isPresent()); assertEquals(113, calibrationBounds.get().getMinX(), 1.0); assertEquals(37, calibrationBounds.get().getMinY(), 1.0); assertEquals(418, calibrationBounds.get().getWidth(), 1.0); assertEquals(316, calibrationBounds.get().getHeight(), 1.0); } /* * http://stackoverflow.com/questions/11006394/is-there-a-simple-way-to- * compare -bufferedimage-instances */ public static boolean compareImages(BufferedImage imgA, BufferedImage imgB) { // The images must be the same size. if (imgA.getWidth() == imgB.getWidth() && imgA.getHeight() == imgB.getHeight()) { int width = imgA.getWidth(); int height = imgA.getHeight(); // Loop over every pixel. for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { // Compare the pixels for equality. if (imgA.getRGB(x, y) != imgB.getRGB(x, y)) { return false; } } } } else { return false; } return true; } @Override public void videoFinished() { synchronized (processingLock) { processingLock.notifyAll(); } } }