From 9a9cbfd3451a107d21fe6f98aff700fe31ac44c9 Mon Sep 17 00:00:00 2001 From: Paul Schaller Date: Tue, 14 Nov 2023 15:51:36 +0100 Subject: [PATCH] Added two helper Classes and partially implemented --- .../ChristmasTreeScanning.java | 143 +++++++++++++++++- .../christmastreescanning/Point3D.java | 13 ++ .../PointInterpolator.java | 17 +++ 3 files changed, 169 insertions(+), 4 deletions(-) create mode 100644 src/main/java/com/cringe_studios/christmastreescanning/Point3D.java create mode 100644 src/main/java/com/cringe_studios/christmastreescanning/PointInterpolator.java diff --git a/src/main/java/com/cringe_studios/christmastreescanning/ChristmasTreeScanning.java b/src/main/java/com/cringe_studios/christmastreescanning/ChristmasTreeScanning.java index a7a256d..80abf62 100644 --- a/src/main/java/com/cringe_studios/christmastreescanning/ChristmasTreeScanning.java +++ b/src/main/java/com/cringe_studios/christmastreescanning/ChristmasTreeScanning.java @@ -1,18 +1,153 @@ package com.cringe_studios.christmastreescanning; +import java.awt.Color; import java.awt.Point; +import java.awt.geom.Point2D; import java.awt.image.BufferedImage; -public class ChristmasTreeScanning { - Point getBrightestSpot(BufferedImage image) { - int[] imageColors = image.getRGB(0, 0, image.getWidth(), image.getHeight(), null, 0, 0); +public class ChristmasTreeScanning { + private Point getBrightestSpotGrayscale(BufferedImage grayScaleImage, byte gammaDelta) { + int[] imageColors = grayScaleImage.getRGB(0, 0, grayScaleImage.getWidth(), grayScaleImage.getHeight(), null, 0, 0); for(int i = 0; i < imageColors.length; i++) { - + imageColors[i] &= 0x0000_00FF; // because the image is grayscale, only one component is needed } + //TODO + Point brightestSpot = new Point(0, 0); return brightestSpot; } + + //gammaDelta is the difference between the brightest pixel and the darkest pixel included in the search set for the brightest spot + //The brightest point Operation is done per color dimension, so you could provide different images for red, green and blue brightest points + public Point getBrightestSpot(BufferedImage redImage, BufferedImage greenImage, BufferedImage blueImage, byte gammaDelta) { + Point brightestRedSpot = getBrightestSpotGrayscale(redImage, gammaDelta); + Point brightestGreenSpot = getBrightestSpotGrayscale(greenImage, gammaDelta); + Point brightestBlueSpot = getBrightestSpotGrayscale(blueImage, gammaDelta); + + double RGdistance = brightestRedSpot.distanceSq(brightestGreenSpot); + double GBdistance = brightestGreenSpot.distanceSq(brightestBlueSpot); + double BRdistance = brightestBlueSpot.distanceSq(brightestRedSpot); + + PointInterpolator brightestSpot; + + //Get the center between the two closest points; a better implementation would work with a confidentnes-value + if(RGdistance < GBdistance && RGdistance < BRdistance) { + brightestSpot = new PointInterpolator(brightestRedSpot, brightestGreenSpot); + }else if(GBdistance < RGdistance && GBdistance < BRdistance) { + brightestSpot = new PointInterpolator(brightestGreenSpot, brightestBlueSpot); + }else { + brightestSpot = new PointInterpolator(brightestBlueSpot, brightestRedSpot); + } + + + return brightestSpot.getCenter(); + } + + public Point getBrightestSpot(BufferedImage image, byte gammaDelta) { + BufferedImage redImage = new BufferedImage(image.getWidth(), image.getHeight(), BufferedImage.TYPE_BYTE_GRAY); + BufferedImage greenImage = new BufferedImage(image.getWidth(), image.getHeight(), BufferedImage.TYPE_BYTE_GRAY); + BufferedImage blueImage = new BufferedImage(image.getWidth(), image.getHeight(), BufferedImage.TYPE_BYTE_GRAY); + + for(int i = 0; i < image.getWidth(); i++) { + for(int j = 0; j < image.getHeight(); j++) { + int currentRGB = image.getRGB(i, j); + int currentBlue = currentRGB & 0xFF; + int currentGreen = (currentRGB >> 8) & 0xFF; + int currentRed = (currentRGB >> 16) & 0xFF; + + redImage.setRGB(i, j, (currentRed << 16) | (currentRed << 8) | currentRed); + greenImage.setRGB(i, j, (currentGreen << 16) | (currentGreen << 8) | currentGreen); + blueImage.setRGB(i, j, (currentBlue << 16) | (currentBlue << 8) | currentBlue); + } + } + + return getBrightestSpot(redImage, greenImage, blueImage, gammaDelta); + } + + public Point getBrightestSpot(BufferedImage image) { + return getBrightestSpot(image, (byte)1); //TODO is it really 1? + } + + + + // This method normalizes the Point coordinates, + // Optionally flips them horizontally + // Adjusts the height (y-coordinate) to make the virtual points not squished (puts them into a rectangle) + // Returns the Points as Point2D + public Point2D[] normalizePoints(Point[] points, boolean isBacksite) { + if(points == null) return null; + + Point2D normalizedPoints[] = new Point2D.Double[points.length]; + for(int i = 0; i < normalizedPoints.length; i++) { + + } + + //TODO is this still needed? + + if(isBacksite) { + for(int i = 0; i < normalizedPoints.length; i++) { + normalizedPoints[i].setLocation(-normalizedPoints[i].getX(), normalizedPoints[i].getY()); + } + } + + return normalizedPoints; + } + + /** + * Normalizes 3D points with the given parameters and returns a NEW array with the normalized points. To see which coordinate dimension represents what direction, view Point3D class + * @param points + * @param baseline for z coordinates. Can be different to the lowest LED + * @param minX minimal X value of LED box + * @param minY minimal Y value of LED box + * @param maxX maximal X value of LED box + * @param maxY maximal Y value of LED box + * @return new Array with normalized Points + */ + public Point3D[] normalizePoints(Point3D[] points, double baseline, double minX, double minY, double maxX, double maxY) { + Point3D normalizedPoints[] = new Point3D[points.length]; + for(int i = 0; i < normalizedPoints.length; i++) { + double normalizedX = (points[i].x - minX) / (maxX - minX); + double normalizedY = (points[i].y - minY) / (maxY - minY); + double normalizedZ = ((baseline - points[i].z) - minX) / (maxX - minX); + + normalizedX = normalizedX * 2.0 - 1.0; + normalizedY = normalizedY * 2.0 - 1.0; + + normalizedPoints[i] = new Point3D(normalizedX, normalizedY, normalizedZ); + } + + return normalizedPoints; + } + + public Point3D[] normalizePoints(Point3D[] points, double baseline) { + double lowestX = Double.POSITIVE_INFINITY; + double highestX = Double.NEGATIVE_INFINITY; + + for(int i = 0; i < points.length; i++) { + if(lowestX > points[i].x) { + lowestX = points[i].x; + } + + if(highestX < points[i].x) { + highestX = points[i].x; + } + } + + return normalizePoints(points, baseline, lowestX, highestX, lowestX, highestX); + } + + public Point3D[] normalizePoints(Point3D[] points) { + double lowestZ = Double.POSITIVE_INFINITY; + + for(int i = 0; i < points.length; i++) { + if(lowestZ > points[i].z) { + lowestZ = points[i].z; + } + } + + return normalizePoints(points, lowestZ); + } } diff --git a/src/main/java/com/cringe_studios/christmastreescanning/Point3D.java b/src/main/java/com/cringe_studios/christmastreescanning/Point3D.java new file mode 100644 index 0000000..d9b4145 --- /dev/null +++ b/src/main/java/com/cringe_studios/christmastreescanning/Point3D.java @@ -0,0 +1,13 @@ +package com.cringe_studios.christmastreescanning; + +public class Point3D { + public double x; // baseline + public double y; // coming out of the image + public double z; // The height + + public Point3D(double nx, double ny, double nz) { + x = nx; + y = ny; + z = nz; + } +} diff --git a/src/main/java/com/cringe_studios/christmastreescanning/PointInterpolator.java b/src/main/java/com/cringe_studios/christmastreescanning/PointInterpolator.java new file mode 100644 index 0000000..6615a59 --- /dev/null +++ b/src/main/java/com/cringe_studios/christmastreescanning/PointInterpolator.java @@ -0,0 +1,17 @@ +package com.cringe_studios.christmastreescanning; + +import java.awt.Point; + +public class PointInterpolator { + Point a; + Point b; + + public PointInterpolator(Point na, Point nb) { + a = na; + b = nb; + } + + Point getCenter() { + return new Point( (a.x + b.x) / 2, (a.y + b.y) / 2); + } +}