Skip to content

Commit 3e2210a

Browse files
authored
Use CS Core on supported platforms for USB cameras (#956)
* Update jpackage to release 64 Existing version was deleted * Use CS Core on supported platforms for USB cameras Can be more reliable then OpenCV, and handles disconnects properly * Fix unused imports
1 parent 04de723 commit 3e2210a

File tree

3 files changed

+212
-108
lines changed

3 files changed

+212
-108
lines changed
Original file line numberDiff line numberDiff line change
@@ -1,97 +1,101 @@
1-
package edu.wpi.grip.core.sources;
2-
3-
import edu.wpi.cscore.CameraServerJNI;
4-
import edu.wpi.cscore.HttpCamera;
5-
import org.bytedeco.javacpp.Loader;
6-
import org.bytedeco.javacpp.opencv_core.Mat;
7-
import org.bytedeco.javacv.Frame;
8-
import org.bytedeco.javacv.FrameConverter;
9-
import org.bytedeco.javacv.FrameGrabber;
10-
import org.bytedeco.javacv.OpenCVFrameConverter;
11-
12-
import java.io.IOException;
13-
import java.util.concurrent.TimeUnit;
14-
15-
import static com.google.common.base.Preconditions.checkNotNull;
16-
17-
// This is here because FrameGrabber has an exception called Exception which triggers PMD
18-
@SuppressWarnings({"PMD.AvoidThrowingRawExceptionTypes", "all"})
19-
public class CSCameraFrameGrabber extends FrameGrabber {
20-
21-
private static Exception loadingException = null;
22-
23-
private final String url;
24-
private final double readTimeout;
25-
26-
private Mat decoded = null;
27-
private FrameConverter<Mat> converter = new OpenCVFrameConverter.ToMat();
28-
29-
private JavaCvSink javaCvSink;
30-
private HttpCamera httpCamera;
31-
32-
public CSCameraFrameGrabber(String urlstr, int readTimeout, TimeUnit unit) {
33-
super();
34-
this.url = checkNotNull(urlstr, "urlstr");
35-
this.readTimeout = TimeUnit.MILLISECONDS.convert(readTimeout, unit) / 1000.0;
36-
}
37-
38-
public static void tryLoad() throws Exception {
39-
if (loadingException != null) {
40-
throw loadingException;
41-
} else {
42-
try {
43-
Loader.load(org.bytedeco.javacpp.opencv_highgui.class);
44-
CameraServerJNI.Helper.setExtractOnStaticLoad(false);
45-
CameraServerJNI.forceLoad();
46-
} catch (Throwable t) {
47-
throw loadingException = new Exception("Failed to load " + CSCameraFrameGrabber.class, t);
48-
}
49-
}
50-
}
51-
52-
@Override
53-
public void start() throws Exception {
54-
javaCvSink = new JavaCvSink("InputCamera");
55-
httpCamera = new HttpCamera("InputHttp", url);
56-
javaCvSink.setSource(httpCamera);
57-
}
58-
59-
public void stop() throws Exception {
60-
if (javaCvSink != null) {
61-
javaCvSink.close();
62-
}
63-
64-
if (httpCamera != null) {
65-
httpCamera.close();
66-
}
67-
68-
if (decoded != null) {
69-
decoded.close();
70-
}
71-
}
72-
73-
@Override
74-
public void trigger() throws Exception {
75-
}
76-
77-
@Override
78-
public Frame grab() throws Exception {
79-
try {
80-
if (decoded == null) {
81-
decoded = new Mat();
82-
}
83-
long frameTime = javaCvSink.grabFrame(decoded, readTimeout);
84-
if (frameTime > 0) {
85-
return converter.convert(decoded);
86-
} else {
87-
throw new IOException("Frame not read: " + frameTime);
88-
}
89-
} catch (IOException e) {
90-
throw new Exception(e.getMessage(), e);
91-
}
92-
}
93-
94-
@Override
95-
public void release() throws Exception {
96-
}
97-
}
1+
package edu.wpi.grip.core.sources;
2+
3+
import edu.wpi.cscore.CameraServerJNI;
4+
import edu.wpi.cscore.HttpCamera;
5+
import org.bytedeco.javacpp.Loader;
6+
import org.bytedeco.javacpp.opencv_core.Mat;
7+
import org.bytedeco.javacv.Frame;
8+
import org.bytedeco.javacv.FrameConverter;
9+
import org.bytedeco.javacv.FrameGrabber;
10+
import org.bytedeco.javacv.OpenCVFrameConverter;
11+
12+
import java.io.IOException;
13+
import java.util.concurrent.TimeUnit;
14+
15+
import static com.google.common.base.Preconditions.checkNotNull;
16+
17+
// This is here because FrameGrabber has an exception called Exception which triggers PMD
18+
@SuppressWarnings({"PMD.AvoidThrowingRawExceptionTypes", "all"})
19+
public class CSHttpCameraFrameGrabber extends FrameGrabber {
20+
21+
private static Exception loadingException = null;
22+
23+
private final String url;
24+
private final double readTimeout;
25+
26+
private Mat decoded = null;
27+
private FrameConverter<Mat> converter = new OpenCVFrameConverter.ToMat();
28+
29+
private JavaCvSink javaCvSink;
30+
private HttpCamera httpCamera;
31+
32+
public CSHttpCameraFrameGrabber(String urlstr, int readTimeout, TimeUnit unit) {
33+
super();
34+
this.url = checkNotNull(urlstr, "urlstr");
35+
this.readTimeout = TimeUnit.MILLISECONDS.convert(readTimeout, unit) / 1000.0;
36+
}
37+
38+
public static void tryLoad() throws Exception {
39+
if (loadingException != null) {
40+
throw loadingException;
41+
} else {
42+
try {
43+
Loader.load(org.bytedeco.javacpp.opencv_highgui.class);
44+
CameraServerJNI.Helper.setExtractOnStaticLoad(false);
45+
CameraServerJNI.forceLoad();
46+
} catch (Throwable t) {
47+
throw loadingException = new Exception("Failed to load " + CSHttpCameraFrameGrabber.class, t);
48+
}
49+
}
50+
}
51+
52+
public HttpCamera getCamera() {
53+
return this.httpCamera;
54+
}
55+
56+
@Override
57+
public void start() throws Exception {
58+
javaCvSink = new JavaCvSink("InputCamera");
59+
httpCamera = new HttpCamera("InputHttp", url);
60+
javaCvSink.setSource(httpCamera);
61+
}
62+
63+
public void stop() throws Exception {
64+
if (javaCvSink != null) {
65+
javaCvSink.close();
66+
}
67+
68+
if (httpCamera != null) {
69+
httpCamera.close();
70+
}
71+
72+
if (decoded != null) {
73+
decoded.close();
74+
}
75+
}
76+
77+
@Override
78+
public void trigger() throws Exception {
79+
}
80+
81+
@Override
82+
public Frame grab() throws Exception {
83+
try {
84+
if (decoded == null) {
85+
decoded = new Mat();
86+
}
87+
long frameTime = javaCvSink.grabFrame(decoded, readTimeout);
88+
if (frameTime > 0) {
89+
return converter.convert(decoded);
90+
} else {
91+
throw new IOException("Frame not read: " + frameTime);
92+
}
93+
} catch (IOException e) {
94+
throw new Exception(e.getMessage(), e);
95+
}
96+
}
97+
98+
@Override
99+
public void release() throws Exception {
100+
}
101+
}
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
package edu.wpi.grip.core.sources;
2+
3+
import edu.wpi.cscore.CameraServerJNI;
4+
import edu.wpi.cscore.UsbCamera;
5+
import org.bytedeco.javacpp.Loader;
6+
import org.bytedeco.javacpp.opencv_core.Mat;
7+
import org.bytedeco.javacv.Frame;
8+
import org.bytedeco.javacv.FrameConverter;
9+
import org.bytedeco.javacv.FrameGrabber;
10+
import org.bytedeco.javacv.OpenCVFrameConverter;
11+
12+
import java.io.IOException;
13+
import java.util.concurrent.TimeUnit;
14+
15+
// This is here because FrameGrabber has an exception called Exception which triggers PMD
16+
@SuppressWarnings({"PMD.AvoidThrowingRawExceptionTypes", "all"})
17+
public class CSUsbCameraFrameGrabber extends FrameGrabber {
18+
19+
private static Exception loadingException = null;
20+
21+
private final int deviceId;
22+
private final double readTimeout;
23+
24+
private Mat decoded = null;
25+
private FrameConverter<Mat> converter = new OpenCVFrameConverter.ToMat();
26+
27+
private JavaCvSink javaCvSink;
28+
private UsbCamera usbCamera;
29+
30+
public CSUsbCameraFrameGrabber(int deviceId, int readTimeout, TimeUnit unit) {
31+
super();
32+
this.deviceId = deviceId;
33+
this.readTimeout = TimeUnit.MILLISECONDS.convert(readTimeout, unit) / 1000.0;
34+
}
35+
36+
public static void tryLoad() throws Exception {
37+
if (loadingException != null) {
38+
throw loadingException;
39+
} else {
40+
try {
41+
Loader.load(org.bytedeco.javacpp.opencv_highgui.class);
42+
CameraServerJNI.Helper.setExtractOnStaticLoad(false);
43+
CameraServerJNI.forceLoad();
44+
} catch (Throwable t) {
45+
throw loadingException = new Exception("Failed to load " + CSUsbCameraFrameGrabber.class, t);
46+
}
47+
}
48+
}
49+
50+
public UsbCamera getCamera() {
51+
return this.usbCamera;
52+
}
53+
54+
@Override
55+
public void start() throws Exception {
56+
javaCvSink = new JavaCvSink("InputCamera");
57+
usbCamera = new UsbCamera("InputUsb", deviceId);
58+
javaCvSink.setSource(usbCamera);
59+
}
60+
61+
public void stop() throws Exception {
62+
if (javaCvSink != null) {
63+
javaCvSink.close();
64+
}
65+
66+
if (usbCamera != null) {
67+
usbCamera.close();
68+
}
69+
70+
if (decoded != null) {
71+
decoded.close();
72+
}
73+
}
74+
75+
@Override
76+
public void trigger() throws Exception {
77+
}
78+
79+
@Override
80+
public Frame grab() throws Exception {
81+
try {
82+
if (decoded == null) {
83+
decoded = new Mat();
84+
}
85+
long frameTime = javaCvSink.grabFrame(decoded, readTimeout);
86+
if (frameTime > 0) {
87+
return converter.convert(decoded);
88+
} else {
89+
throw new IOException("Frame not read: " + frameTime);
90+
}
91+
} catch (IOException e) {
92+
throw new Exception(e.getMessage(), e);
93+
}
94+
}
95+
96+
@Override
97+
public void release() throws Exception {
98+
}
99+
}

core/src/main/java/edu/wpi/grip/core/sources/CameraSource.java

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@
2626
import org.bytedeco.javacpp.opencv_core.Mat;
2727
import org.bytedeco.javacv.FrameGrabber;
2828
import org.bytedeco.javacv.OpenCVFrameGrabber;
29-
import org.bytedeco.javacv.VideoInputFrameGrabber;
3029

3130
import java.io.IOException;
3231
import java.net.MalformedURLException;
@@ -60,8 +59,8 @@ public class CameraSource extends Source implements RestartableService {
6059
* Reading from an existing connection shouldn't take that long. If it does we should really give
6160
* up and try to reconnect.
6261
*/
63-
private static final int IP_CAMERA_READ_TIMEOUT = 5;
64-
private static final TimeUnit IP_CAMERA_TIMEOUT_UNIT = TimeUnit.SECONDS;
62+
private static final int CAMERA_READ_TIMEOUT = 5;
63+
private static final TimeUnit CAMERA_TIMEOUT_UNIT = TimeUnit.SECONDS;
6564

6665
private static final String DEVICE_NUMBER_PROPERTY = "deviceNumber";
6766
private static final String ADDRESS_PROPERTY = "address";
@@ -391,11 +390,13 @@ public static class FrameGrabberFactoryImpl implements FrameGrabberFactory {
391390

392391
@Override
393392
public FrameGrabber create(int deviceNumber) {
394-
// On Windows, videoInput is much more reliable for webcam capture. On other platforms,
395-
// OpenCV's frame
396-
// grabber class works fine.
397-
if (StandardSystemProperty.OS_NAME.value().contains("Windows")) {
398-
return new VideoInputFrameGrabber(deviceNumber);
393+
// On Windows and Linux, use CS Core USB Cameras.
394+
// Mac support does not exist, so must use OpenCV's Grabber
395+
if (StandardSystemProperty.OS_NAME.value().contains("Windows")
396+
|| StandardSystemProperty.OS_NAME.value().contains("Linux")) {
397+
return new CSUsbCameraFrameGrabber(deviceNumber,
398+
CAMERA_READ_TIMEOUT,
399+
CAMERA_TIMEOUT_UNIT);
399400
} else {
400401
return new OpenCVFrameGrabber(deviceNumber);
401402
}
@@ -409,10 +410,10 @@ public FrameGrabber create(String addressProperty) throws MalformedURLException
409410
if (new URL(addressProperty).getPath().length() <= 1) {
410411
addressProperty += DEFAULT_IP_CAMERA_PATH;
411412
}
412-
return new CSCameraFrameGrabber(
413+
return new CSHttpCameraFrameGrabber(
413414
addressProperty,
414-
IP_CAMERA_READ_TIMEOUT,
415-
IP_CAMERA_TIMEOUT_UNIT);
415+
CAMERA_READ_TIMEOUT,
416+
CAMERA_TIMEOUT_UNIT);
416417
}
417418
}
418419
}

0 commit comments

Comments
 (0)