Skip to content

Commit

Permalink
Fix Camera Index Assignment. (#1031)
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
BytingBulldogs3539 authored Dec 1, 2023
1 parent f597d11 commit 469bc0e
Show file tree
Hide file tree
Showing 6 changed files with 81 additions and 13 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -108,8 +110,20 @@ public static QuirkyCamera getQuirkyCamera(int usbVid, int usbPid, String baseNa
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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,9 @@
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;
Expand All @@ -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);
Expand Down Expand Up @@ -77,6 +79,22 @@ public USBCameraSource(CameraConfiguration config) {
}
}

/**
* 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 {
Expand All @@ -88,6 +106,10 @@ void disableAutoFocus() {
}
}

public QuirkyCamera getCameraQuirks() {
return this.cameraQuirks;
}

@Override
public FrameProvider getFrameProvider() {
return usbFrameProvider;
Expand All @@ -103,7 +125,7 @@ protected USBCameraSettables(CameraConfiguration configuration) {
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) {
Expand Down Expand Up @@ -343,11 +365,27 @@ public boolean isVendorCamera() {
}

@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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ public VisionModule(PipelineManager pipelineManager, VisionSource visionSource,

// 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 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -342,7 +342,7 @@ private static List<VisionSource> loadVisionSourcesFromCamConfigs(
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);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ public void setBlueGain(int blue) {}
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) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
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;
Expand Down Expand Up @@ -165,7 +166,16 @@ public void testMultipleStreamIndex() {
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(
Expand All @@ -176,9 +186,15 @@ public void testMultipleStreamIndex() {
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) {
Expand Down

0 comments on commit 469bc0e

Please sign in to comment.