001/* 002 * Copyright (c) 2020-2021 REV Robotics 003 * 004 * Redistribution and use in source and binary forms, with or without 005 * modification, are permitted provided that the following conditions are met: 006 * 007 * 1. Redistributions of source code must retain the above copyright notice, 008 * this list of conditions and the following disclaimer. 009 * 2. Redistributions in binary form must reproduce the above copyright 010 * notice, this list of conditions and the following disclaimer in the 011 * documentation and/or other materials provided with the distribution. 012 * 3. Neither the name of REV Robotics nor the names of its 013 * contributors may be used to endorse or promote products derived from 014 * this software without specific prior written permission. 015 * 016 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 017 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 018 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 019 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 020 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 021 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 022 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 023 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 024 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 025 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 026 * POSSIBILITY OF SUCH DAMAGE. 027 */ 028 029package com.revrobotics; 030 031import edu.wpi.first.wpilibj.util.Color; 032import java.util.ArrayList; 033 034public class ColorMatch { 035 private static double CalculateDistance(Color color1, Color color2) { 036 double redDiff = color1.red - color2.red; 037 double greenDiff = color1.green - color2.green; 038 double blueDiff = color1.blue - color2.blue; 039 040 return Math.sqrt((redDiff * redDiff + greenDiff * greenDiff + blueDiff * blueDiff) / 2); 041 } 042 043 private static final double kDefaultConfidence = 0.95; 044 private double m_confidenceLevel; 045 private ArrayList<Color> m_colorsToMatch = new ArrayList<Color>(); 046 047 public ColorMatch() { 048 m_confidenceLevel = kDefaultConfidence; 049 } 050 051 /** 052 * Add color to match object 053 * 054 * @param color color to add to matching 055 */ 056 public void addColorMatch(Color color) { 057 m_colorsToMatch.add(color); 058 } 059 060 /** 061 * Set the confidence interval for determining color. Defaults to 0.95 062 * 063 * @param confidence A value between 0 and 1 064 */ 065 public void setConfidenceThreshold(double confidence) { 066 if (confidence < 0) { 067 confidence = 0; 068 } else if (confidence > 1) { 069 confidence = 1; 070 } 071 m_confidenceLevel = confidence; 072 } 073 074 /** 075 * MatchColor uses euclidean distance to compare a given normalized RGB vector against stored 076 * values 077 * 078 * @param colorToMatch color to compare against stored colors 079 * @return Matched color if detected, returns null if no color detected confidence level 080 */ 081 public ColorMatchResult matchColor(Color colorToMatch) { 082 ColorMatchResult match = matchClosestColor(colorToMatch); 083 if (match.confidence > m_confidenceLevel) { 084 return match; 085 } 086 return null; 087 } 088 089 /** 090 * MatchColor uses euclidean distance to compare a given normalized RGB vector against stored 091 * values 092 * 093 * @param color color to compare against stored colors 094 * @return Closest color to match 095 */ 096 public ColorMatchResult matchClosestColor(Color color) { 097 double magnitude = color.red + color.blue + color.green; 098 if (magnitude > 0 && m_colorsToMatch.size() > 0) { 099 double minDistance = 1.0; 100 int idx = 0; 101 102 for (int i = 0; i < m_colorsToMatch.size(); i++) { 103 double targetDistance = CalculateDistance(m_colorsToMatch.get(i), color); 104 105 if (targetDistance < minDistance) { 106 minDistance = targetDistance; 107 idx = i; 108 } 109 } 110 ColorMatchResult match = 111 new ColorMatchResult(m_colorsToMatch.get(idx), 1.0 - (minDistance / magnitude)); 112 return match; 113 } else { 114 // return frc::Color::kBlack; 115 return new ColorMatchResult(Color.kBlack, 0.0); 116 } 117 } 118}