001/* 002 * Copyright (C) Photon Vision. 003 * 004 * This program is free software: you can redistribute it and/or modify 005 * it under the terms of the GNU General Public License as published by 006 * the Free Software Foundation, either version 3 of the License, or 007 * (at your option) any later version. 008 * 009 * This program is distributed in the hope that it will be useful, 010 * but WITHOUT ANY WARRANTY; without even the implied warranty of 011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 012 * GNU General Public License for more details. 013 * 014 * You should have received a copy of the GNU General Public License 015 * along with this program. If not, see <https://www.gnu.org/licenses/>. 016 */ 017 018package org.photonvision.vision.camera; 019 020import com.fasterxml.jackson.annotation.JsonCreator; 021import com.fasterxml.jackson.annotation.JsonProperty; 022import java.util.ArrayList; 023import java.util.Arrays; 024import java.util.HashMap; 025import java.util.List; 026import java.util.Objects; 027 028public class QuirkyCamera { 029 private static final List<QuirkyCamera> quirkyCameras = 030 List.of( 031 // SeeCam, which has an odd exposure range 032 new QuirkyCamera( 033 0x2560, 0xc128, "See3Cam_24CUG", CameraQuirk.Gain, CameraQuirk.See3Cam_24CUG), 034 // Chris's older generic "Logitech HD Webcam" 035 new QuirkyCamera(0x9331, 0x5A3, CameraQuirk.CompletelyBroken), 036 // Logitech C270 037 new QuirkyCamera(0x825, 0x46D, CameraQuirk.CompletelyBroken), 038 // A laptop internal camera someone found broken 039 new QuirkyCamera(0x0bda, 0x5510, CameraQuirk.CompletelyBroken), 040 // SnapCamera on Windows 041 new QuirkyCamera(-1, -1, "Snap Camera", CameraQuirk.CompletelyBroken), 042 // Mac Facetime Camera shared into Windows in Bootcamp 043 new QuirkyCamera(-1, -1, "FaceTime HD Camera", CameraQuirk.CompletelyBroken), 044 // Microsoft Lifecam 045 new QuirkyCamera(-1, -1, "LifeCam HD-3000", CameraQuirk.LifeCamControls), 046 // Microsoft Lifecam 047 new QuirkyCamera(-1, -1, "LifeCam Cinema (TM)", CameraQuirk.LifeCamControls), 048 // PS3Eye 049 new QuirkyCamera( 050 0x1415, 0x2000, CameraQuirk.Gain, CameraQuirk.FPSCap100, CameraQuirk.PsEyeControls), 051 // Logitech C925-e 052 new QuirkyCamera(0x85B, 0x46D, CameraQuirk.AdjustableFocus), 053 // Generic arducam. Since OV2311 can't be differentiated 054 // at first boot, apply stickyFPS to the generic case, too 055 new QuirkyCamera( 056 0x0c45, 057 0x6366, 058 "", 059 "Arducam Generic", 060 CameraQuirk.ArduCamCamera, 061 CameraQuirk.Gain, 062 CameraQuirk.StickyFPS), 063 // Arducam OV2311 064 new QuirkyCamera( 065 0x0c45, 066 0x6366, 067 "OV2311", 068 "OV2311", 069 CameraQuirk.ArduCamCamera, 070 CameraQuirk.ArduOV2311Controls, 071 CameraQuirk.StickyFPS), 072 // Arducam OV9281 073 new QuirkyCamera( 074 0x0c45, 075 0x6366, 076 "OV9281", 077 "OV9281", 078 CameraQuirk.ArduCamCamera, 079 CameraQuirk.ArduOV9281Controls), 080 // Arducam OV9782 081 new QuirkyCamera( 082 0x0c45, 083 0x6366, 084 "OV9782", 085 "OV9782", 086 CameraQuirk.ArduCamCamera, 087 CameraQuirk.Gain, 088 CameraQuirk.ArduOV9782Controls), 089 // Innomaker OV9281 090 new QuirkyCamera( 091 0x0c45, 0x636d, "USB Camera", "Innomaker OV9281", CameraQuirk.InnoOV9281Controls)); 092 093 public static final QuirkyCamera DefaultCamera = new QuirkyCamera(0, 0, ""); 094 public static final QuirkyCamera ZeroCopyPiCamera = 095 new QuirkyCamera( 096 -1, 097 -1, 098 "unicam", 099 CameraQuirk.Gain, 100 CameraQuirk.AwbRedBlueGain); // PiCam (using libcamera GPU Driver on raspberry pi) 101 102 @JsonProperty("baseName") 103 public final String baseName; 104 105 @JsonProperty("usbVid") 106 public final int usbVid; 107 108 @JsonProperty("usbPid") 109 public final int usbPid; 110 111 @JsonProperty("displayName") 112 public final String displayName; 113 114 @JsonProperty("quirks") 115 public final HashMap<CameraQuirk, Boolean> quirks; 116 117 /** 118 * Creates a QuirkyCamera that matches by USB VID/PID 119 * 120 * @param usbVid USB VID of camera 121 * @param usbPid USB PID of camera 122 * @param quirks Camera quirks 123 */ 124 private QuirkyCamera(int usbVid, int usbPid, CameraQuirk... quirks) { 125 this(usbVid, usbPid, "", quirks); 126 } 127 128 /** 129 * Creates a QuirkyCamera that matches by USB VID/PID and name 130 * 131 * @param usbVid USB VID of camera 132 * @param usbPid USB PID of camera 133 * @param baseName CSCore name of camera 134 * @param quirks Camera quirks 135 */ 136 private QuirkyCamera(int usbVid, int usbPid, String baseName, CameraQuirk... quirks) { 137 this(usbVid, usbPid, baseName, "", quirks); 138 } 139 140 /** 141 * Creates a QuirkyCamera that matches by USB VID/PID and name 142 * 143 * @param usbVid USB VID of camera 144 * @param usbPid USB PID of camera 145 * @param baseName CSCore name of camera 146 * @param displayName Human-friendly quirky camera name 147 * @param quirks Camera quirks 148 */ 149 private QuirkyCamera( 150 int usbVid, int usbPid, String baseName, String displayName, CameraQuirk... quirks) { 151 this.usbVid = usbVid; 152 this.usbPid = usbPid; 153 this.baseName = baseName; 154 this.displayName = displayName; 155 156 this.quirks = new HashMap<>(); 157 158 // (1) Fill quirk map with the supplied Quirk list 159 for (var q : quirks) { 160 this.quirks.put(q, true); 161 } 162 163 // (2) for all other quirks in CameraQuirks (in this version of Photon), default to false 164 for (var q : CameraQuirk.values()) { 165 this.quirks.putIfAbsent(q, false); 166 } 167 } 168 169 @JsonCreator 170 public QuirkyCamera( 171 @JsonProperty("baseName") String baseName, 172 @JsonProperty("usbVid") int usbVid, 173 @JsonProperty("usbPid") int usbPid, 174 @JsonProperty("displayName") String displayName, 175 @JsonProperty("quirks") HashMap<CameraQuirk, Boolean> quirks) { 176 this.baseName = baseName; 177 this.usbPid = usbPid; 178 this.usbVid = usbVid; 179 this.quirks = quirks; 180 this.displayName = displayName; 181 } 182 183 /** 184 * Check if this camera 185 * 186 * @param quirk 187 * @return 188 */ 189 public boolean hasQuirk(CameraQuirk quirk) { 190 return quirks.getOrDefault(quirk, false); 191 } 192 193 public static QuirkyCamera getQuirkyCamera(int usbVid, int usbPid) { 194 return getQuirkyCamera(usbVid, usbPid, ""); 195 } 196 197 public static QuirkyCamera getQuirkyCamera(int usbVid, int usbPid, String baseName) { 198 for (var qc : quirkyCameras) { 199 boolean useBaseNameMatch = !qc.baseName.isEmpty(); 200 boolean matchesBaseName = true; // default to matching 201 if (useBaseNameMatch) { 202 matchesBaseName = baseName.endsWith(qc.baseName); 203 } 204 205 boolean usePidVidMatch = (qc.usbVid != -1) && (qc.usbPid != -1); 206 boolean matchesPidVid = true; // default to matching 207 if (usePidVidMatch) { 208 matchesPidVid = (qc.usbVid == usbVid && qc.usbPid == usbPid); 209 } 210 211 if (matchesPidVid && matchesBaseName) { 212 // We have a quirky camera! 213 // Copy the quirks from our predefined object and create 214 // a QuirkyCamera object with the complete properties 215 List<CameraQuirk> quirks = new ArrayList<CameraQuirk>(); 216 for (var q : CameraQuirk.values()) { 217 if (qc.hasQuirk(q)) quirks.add(q); 218 } 219 QuirkyCamera c = 220 new QuirkyCamera( 221 usbVid, 222 usbPid, 223 baseName, 224 Arrays.copyOf(quirks.toArray(), quirks.size(), CameraQuirk[].class)); 225 return c; 226 } 227 } 228 return new QuirkyCamera(usbVid, usbPid, baseName); 229 } 230 231 public boolean hasQuirks() { 232 return quirks.containsValue(true); 233 } 234 235 @Override 236 public boolean equals(Object o) { 237 if (this == o) return true; 238 if (o == null || getClass() != o.getClass()) return false; 239 QuirkyCamera that = (QuirkyCamera) o; 240 return usbVid == that.usbVid 241 && usbPid == that.usbPid 242 && Objects.equals(baseName, that.baseName) 243 && Objects.equals(quirks, that.quirks); 244 } 245 246 @Override 247 public String toString() { 248 String ret = 249 "QuirkyCamera [baseName=" 250 + baseName 251 + ", displayName=" 252 + displayName 253 + ", usbVid=" 254 + usbVid 255 + ", usbPid=" 256 + usbPid 257 + ", quirks=" 258 + quirks.toString() 259 + "]"; 260 return ret; 261 } 262 263 @Override 264 public int hashCode() { 265 return Objects.hash(usbVid, usbPid, baseName, quirks); 266 } 267 268 /** 269 * Add/remove quirks from the camera we're controlling 270 * 271 * @param quirksToChange map of true/false for quirks we should change 272 */ 273 public void updateQuirks(HashMap<CameraQuirk, Boolean> quirksToChange) { 274 for (var q : quirksToChange.entrySet()) { 275 var quirk = q.getKey(); 276 var hasQuirk = q.getValue(); 277 278 this.quirks.put(quirk, hasQuirk); 279 } 280 } 281}