mirror of
https://github.com/PhotonVision/photonvision
synced 2026-06-29 02:21:41 +00:00
Fix Camera Index Assignment. (#1031)
Fixes a bug where multiple cameras would receive identical stream indexes and would cause at least one camera to not function at all and the other to not display a stream. One of the reasons the that usbcamerasource equals method used to incorrectly determine equality was because the quirkycamera object didn't have a basename. When a camera that had a quirk was found it would set equal to a predefined quirkycamera without changing the name first. Add unit test that will better test the equality of two usbcamerasources. This required a few changes to allow creating a fake usbcamerasource.
This commit is contained in:
@@ -17,6 +17,8 @@
|
||||
|
||||
package org.photonvision.vision.camera;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
@@ -108,8 +110,20 @@ public class QuirkyCamera {
|
||||
for (var qc : quirkyCameras) {
|
||||
boolean hasBaseName = !qc.baseName.isEmpty();
|
||||
boolean matchesBaseName = qc.baseName.equals(baseName) || !hasBaseName;
|
||||
// If we have a quirkycamera we need to copy the quirks from our predefined object and create
|
||||
// a quirkycamera object with the baseName.
|
||||
if (qc.usbVid == usbVid && qc.usbPid == usbPid && matchesBaseName) {
|
||||
return qc;
|
||||
List<CameraQuirk> quirks = new ArrayList<CameraQuirk>();
|
||||
for (var q : CameraQuirk.values()) {
|
||||
if (qc.hasQuirk(q)) quirks.add(q);
|
||||
}
|
||||
QuirkyCamera c =
|
||||
new QuirkyCamera(
|
||||
usbVid,
|
||||
usbPid,
|
||||
baseName,
|
||||
Arrays.copyOf(quirks.toArray(), quirks.size(), CameraQuirk[].class));
|
||||
return c;
|
||||
}
|
||||
}
|
||||
return new QuirkyCamera(usbVid, usbPid, baseName);
|
||||
|
||||
@@ -28,7 +28,9 @@ import org.photonvision.common.configuration.CameraConfiguration;
|
||||
import org.photonvision.common.configuration.ConfigManager;
|
||||
import org.photonvision.common.logging.LogGroup;
|
||||
import org.photonvision.common.logging.Logger;
|
||||
import org.photonvision.common.util.TestUtils;
|
||||
import org.photonvision.vision.frame.FrameProvider;
|
||||
import org.photonvision.vision.frame.provider.FileFrameProvider;
|
||||
import org.photonvision.vision.frame.provider.USBFrameProvider;
|
||||
import org.photonvision.vision.processes.VisionSource;
|
||||
import org.photonvision.vision.processes.VisionSourceSettables;
|
||||
@@ -37,10 +39,10 @@ public class USBCameraSource extends VisionSource {
|
||||
private final Logger logger;
|
||||
private final UsbCamera camera;
|
||||
private final USBCameraSettables usbCameraSettables;
|
||||
private final USBFrameProvider usbFrameProvider;
|
||||
private FrameProvider usbFrameProvider;
|
||||
private final CvSink cvSink;
|
||||
|
||||
public final QuirkyCamera cameraQuirks;
|
||||
private QuirkyCamera cameraQuirks;
|
||||
|
||||
public USBCameraSource(CameraConfiguration config) {
|
||||
super(config);
|
||||
@@ -77,6 +79,22 @@ public class USBCameraSource extends VisionSource {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Mostly just used for unit tests to better simulate a usb camera without a camera being present.
|
||||
*/
|
||||
public USBCameraSource(CameraConfiguration config, int pid, int vid, boolean unitTest) {
|
||||
this(config);
|
||||
|
||||
cameraQuirks = QuirkyCamera.getQuirkyCamera(pid, vid, config.baseName);
|
||||
|
||||
if (unitTest)
|
||||
usbFrameProvider =
|
||||
new FileFrameProvider(
|
||||
TestUtils.getWPIImagePath(
|
||||
TestUtils.WPI2019Image.kCargoStraightDark72in_HighRes, false),
|
||||
TestUtils.WPI2019Image.FOV);
|
||||
}
|
||||
|
||||
void disableAutoFocus() {
|
||||
if (cameraQuirks.hasQuirk(CameraQuirk.AdjustableFocus)) {
|
||||
try {
|
||||
@@ -88,6 +106,10 @@ public class USBCameraSource extends VisionSource {
|
||||
}
|
||||
}
|
||||
|
||||
public QuirkyCamera getCameraQuirks() {
|
||||
return this.cameraQuirks;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FrameProvider getFrameProvider() {
|
||||
return usbFrameProvider;
|
||||
@@ -103,7 +125,7 @@ public class USBCameraSource extends VisionSource {
|
||||
super(configuration);
|
||||
getAllVideoModes();
|
||||
if (!cameraQuirks.hasQuirk(CameraQuirk.StickyFPS))
|
||||
setVideoMode(videoModes.get(0)); // fixes double FPS set
|
||||
if (!videoModes.isEmpty()) setVideoMode(videoModes.get(0)); // fixes double FPS set
|
||||
}
|
||||
|
||||
public void setAutoExposure(boolean cameraAutoExposure) {
|
||||
@@ -343,11 +365,27 @@ public class USBCameraSource extends VisionSource {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
USBCameraSource that = (USBCameraSource) o;
|
||||
return cameraQuirks.equals(that.cameraQuirks);
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) return true;
|
||||
if (obj == null) return false;
|
||||
if (getClass() != obj.getClass()) return false;
|
||||
USBCameraSource other = (USBCameraSource) obj;
|
||||
if (camera == null) {
|
||||
if (other.camera != null) return false;
|
||||
} else if (!camera.equals(other.camera)) return false;
|
||||
if (usbCameraSettables == null) {
|
||||
if (other.usbCameraSettables != null) return false;
|
||||
} else if (!usbCameraSettables.equals(other.usbCameraSettables)) return false;
|
||||
if (usbFrameProvider == null) {
|
||||
if (other.usbFrameProvider != null) return false;
|
||||
} else if (!usbFrameProvider.equals(other.usbFrameProvider)) return false;
|
||||
if (cvSink == null) {
|
||||
if (other.cvSink != null) return false;
|
||||
} else if (!cvSink.equals(other.cvSink)) return false;
|
||||
if (cameraQuirks == null) {
|
||||
if (other.cameraQuirks != null) return false;
|
||||
} else if (!cameraQuirks.equals(other.cameraQuirks)) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -95,7 +95,7 @@ public class VisionModule {
|
||||
|
||||
// Find quirks for the current camera
|
||||
if (visionSource instanceof USBCameraSource) {
|
||||
cameraQuirks = ((USBCameraSource) visionSource).cameraQuirks;
|
||||
cameraQuirks = ((USBCameraSource) visionSource).getCameraQuirks();
|
||||
} else if (visionSource instanceof LibcameraGpuSource) {
|
||||
cameraQuirks = QuirkyCamera.ZeroCopyPiCamera;
|
||||
} else {
|
||||
|
||||
@@ -342,7 +342,7 @@ public class VisionSourceManager {
|
||||
cameraSources.add(piCamSrc);
|
||||
} else {
|
||||
var newCam = new USBCameraSource(configuration);
|
||||
if (!newCam.cameraQuirks.hasQuirk(CameraQuirk.CompletelyBroken)
|
||||
if (!newCam.getCameraQuirks().hasQuirk(CameraQuirk.CompletelyBroken)
|
||||
&& !newCam.getSettables().videoModes.isEmpty()) {
|
||||
cameraSources.add(newCam);
|
||||
}
|
||||
|
||||
@@ -59,7 +59,7 @@ public abstract class VisionSourceSettables {
|
||||
public abstract VideoMode getCurrentVideoMode();
|
||||
|
||||
public void setVideoModeInternal(int index) {
|
||||
setVideoMode(getAllVideoModes().get(index));
|
||||
if (!getAllVideoModes().isEmpty()) setVideoMode(getAllVideoModes().get(index));
|
||||
}
|
||||
|
||||
public void setVideoMode(VideoMode mode) {
|
||||
|
||||
@@ -31,6 +31,7 @@ import org.photonvision.common.configuration.CameraConfiguration;
|
||||
import org.photonvision.common.configuration.ConfigManager;
|
||||
import org.photonvision.common.dataflow.CVPipelineResultConsumer;
|
||||
import org.photonvision.common.util.TestUtils;
|
||||
import org.photonvision.vision.camera.USBCameraSource;
|
||||
import org.photonvision.vision.frame.FrameProvider;
|
||||
import org.photonvision.vision.frame.FrameStaticProperties;
|
||||
import org.photonvision.vision.frame.provider.FileFrameProvider;
|
||||
@@ -165,7 +166,16 @@ public class VisionModuleManagerTest {
|
||||
TestUtils.WPI2019Image.FOV);
|
||||
var testSource3 = new TestSource(ffp3, conf3);
|
||||
|
||||
var modules = vmm.addSources(List.of(testSource, testSource2, testSource3));
|
||||
// Arducam OV9281 UC844 raspberry pi test.
|
||||
var conf4 = new CameraConfiguration("Left", "dev/video1");
|
||||
USBCameraSource usbSimulation = new USBCameraSource(conf4, 0x6366, 0x0c45, true);
|
||||
|
||||
var conf5 = new CameraConfiguration("Right", "dev/video2");
|
||||
USBCameraSource usbSimulation2 = new USBCameraSource(conf5, 0x6366, 0x0c45, true);
|
||||
|
||||
var modules =
|
||||
vmm.addSources(
|
||||
List.of(testSource, testSource2, testSource3, usbSimulation, usbSimulation2));
|
||||
|
||||
System.out.println(
|
||||
Arrays.toString(
|
||||
@@ -176,9 +186,15 @@ public class VisionModuleManagerTest {
|
||||
modules.stream()
|
||||
.map(it -> it.visionSource.getCameraConfiguration().streamIndex)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
assertTrue(usbSimulation.equals(usbSimulation));
|
||||
assertTrue(!usbSimulation.equals(usbSimulation2));
|
||||
|
||||
assertTrue(idxs.contains(0));
|
||||
assertTrue(idxs.contains(1));
|
||||
assertTrue(idxs.contains(2));
|
||||
assertTrue(idxs.contains(3));
|
||||
assertTrue(idxs.contains(4));
|
||||
}
|
||||
|
||||
private static void printTestResults(CVPipelineResult pipelineResult) {
|
||||
|
||||
Reference in New Issue
Block a user