com.shootoff.camera.shotdetection.PixelCluster.java Source code

Java tutorial

Introduction

Here is the source code for com.shootoff.camera.shotdetection.PixelCluster.java

Source

/*
 * ShootOFF - Software for Laser Dry Fire Training
 * Copyright (C) 2016 phrack
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package com.shootoff.camera.shotdetection;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Optional;

import org.opencv.core.Mat;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javafx.scene.paint.Color;

public class PixelCluster extends HashSet<Pixel> {
    private static final Logger logger = LoggerFactory.getLogger(PixelCluster.class);

    private static final long serialVersionUID = 1L;

    public double centerPixelX;
    public double centerPixelY;

    private final static double CURRENT_COLOR_BIAS_MULTIPLIER = .5;

    // We ignore fully connected pixels because they are not on the edges
    private final static int MAXIMUM_CONNECTEDNESS = 8;

    // We collect all the pixels AROUND the detected shot, not any in the shot
    // itself
    // Usually the pixels in the shot are max brightness which are biased green
    // So we look around the shot instead
    public double getColorDifference(final Mat workingFrame, final int[][] colorDistanceFromRed) {
        final Map<Pixel, byte[]> visited = new HashMap<Pixel, byte[]>();
        int avgSaturation = 0;

        for (final Pixel pixel : this) {
            if (pixel.getConnectedness() < MAXIMUM_CONNECTEDNESS) {
                for (int h = -1; h <= 1; h++) {
                    for (int w = -1; w <= 1; w++) {
                        if (h == 0 && w == 0)
                            continue;

                        final int rx = pixel.x + w;
                        final int ry = pixel.y + h;

                        if (rx < 0 || ry < 0 || rx >= workingFrame.cols() || ry >= workingFrame.rows())
                            continue;

                        final Pixel nearPoint = new Pixel(rx, ry);

                        if (!visited.containsKey(nearPoint) && !this.contains(nearPoint)) {
                            byte[] np = { 0, 0, 0 };
                            workingFrame.get(ry, rx, np);
                            final int npSaturation = np[1] & 0xFF;

                            avgSaturation += npSaturation;

                            visited.put(nearPoint, np);
                        }
                    }
                }
            }
        }

        final int pixelCount = visited.size();
        if (pixelCount == 0)
            return 0;

        avgSaturation /= pixelCount;

        int colorDistance = 0;
        int avgColorDistance = 0;
        int tempColorDistance = 0;

        for (final Entry<Pixel, byte[]> pixelEntry : visited.entrySet()) {
            byte[] np = pixelEntry.getValue();
            final int npSaturation = np[1] & 0xFF;

            if (npSaturation > avgSaturation) {
                final int npColor = np[0] & 0xFF;
                final int npLum = np[2] & 0xFF;

                final int thisDFromRed = Math.min(npColor, Math.abs(180 - npColor)) * npLum * npSaturation;
                final int thisDFromGreen = Math.abs(60 - npColor) * npLum * npSaturation;

                final int currentCol = thisDFromRed - thisDFromGreen;

                final Pixel pixel = pixelEntry.getKey();
                colorDistance += currentCol
                        - (int) (CURRENT_COLOR_BIAS_MULTIPLIER * colorDistanceFromRed[pixel.x][pixel.y]);

                if (logger.isTraceEnabled()) {
                    tempColorDistance += currentCol;
                    avgColorDistance += colorDistanceFromRed[pixel.x][pixel.y];
                }
            }
        }

        if (logger.isTraceEnabled())
            logger.trace("Pixels {} Color {} avg {} sum {}", pixelCount, colorDistance / pixelCount,
                    avgColorDistance / pixelCount, tempColorDistance / pixelCount);

        return colorDistance;
    }

    public Optional<Color> getColor(final Mat workingFrame, final int[][] colorDistanceFromRed) {
        final double colorDist = getColorDifference(workingFrame, colorDistanceFromRed);

        // Sometimes it's better to guess than to return nothing
        if (colorDist < 0)
            return Optional.of(Color.RED);
        else
            return Optional.of(Color.GREEN);
    }
}