mirror of
https://github.com/PhotonVision/photonvision
synced 2026-06-23 01:21:40 +00:00
Delete unused classes and remove unused code
This commit is contained in:
@@ -1,31 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) Photon Vision.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.photonvision.common.dataflow.events;
|
||||
|
||||
import org.photonvision.common.dataflow.DataChangeDestination;
|
||||
import org.photonvision.common.dataflow.DataChangeSource;
|
||||
|
||||
public class HTTPRequestEvent<T> extends DataChangeEvent<T> {
|
||||
public HTTPRequestEvent(
|
||||
DataChangeSource sourceType,
|
||||
DataChangeDestination destType,
|
||||
String propertyName,
|
||||
T newValue) {
|
||||
super(sourceType, destType, propertyName, newValue);
|
||||
}
|
||||
}
|
||||
@@ -1,118 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) Photon Vision.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.photonvision.common.networking;
|
||||
|
||||
import edu.wpi.first.cscore.CameraServerJNI;
|
||||
import java.io.IOException;
|
||||
import java.net.InetAddress;
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import org.photonvision.common.dataflow.DataChangeService;
|
||||
import org.photonvision.common.dataflow.events.OutgoingUIEvent;
|
||||
import org.photonvision.common.logging.LogGroup;
|
||||
import org.photonvision.common.logging.Logger;
|
||||
|
||||
public class RoborioFinder {
|
||||
private static RoborioFinder instance;
|
||||
private static final Logger logger = new Logger(RoborioFinder.class, LogGroup.General);
|
||||
|
||||
public static RoborioFinder getInstance() {
|
||||
if (instance == null) instance = new RoborioFinder();
|
||||
return instance;
|
||||
}
|
||||
|
||||
public void findRios() {
|
||||
HashMap<String, Object> map = new HashMap<>();
|
||||
var subMap = new HashMap<String, Object>();
|
||||
// Separate from the above so we don't hold stuff up
|
||||
System.setProperty("java.net.preferIPv4Stack", "true");
|
||||
subMap.put(
|
||||
"deviceIps",
|
||||
Arrays.stream(CameraServerJNI.getNetworkInterfaces())
|
||||
.filter(it -> !it.equals("0.0.0.0"))
|
||||
.toArray());
|
||||
logger.info("Searching for rios");
|
||||
List<String> possibleRioList = new ArrayList<>();
|
||||
for (var ip : CameraServerJNI.getNetworkInterfaces()) {
|
||||
logger.info("Trying " + ip);
|
||||
var possibleRioAddr = getPossibleRioAddress(ip);
|
||||
if (possibleRioAddr != null) {
|
||||
logger.info("Maybe found " + ip);
|
||||
searchForHost(possibleRioList, possibleRioAddr);
|
||||
} else {
|
||||
logger.info("Didn't match RIO IP");
|
||||
}
|
||||
}
|
||||
|
||||
// String name =
|
||||
// "roboRIO-"
|
||||
// +
|
||||
// ConfigManager.getInstance().getConfig().getNetworkConfig().teamNumber
|
||||
// + "-FRC.local";
|
||||
// searchForHost(possibleRioList, name);
|
||||
// name =
|
||||
// "roboRIO-"
|
||||
// +
|
||||
// ConfigManager.getInstance().getConfig().getNetworkConfig().teamNumber
|
||||
// + "-FRC.lan";
|
||||
// searchForHost(possibleRioList, name);
|
||||
// name =
|
||||
// "roboRIO-"
|
||||
// +
|
||||
// ConfigManager.getInstance().getConfig().getNetworkConfig().teamNumber
|
||||
// + "-FRC.frc-field.local";
|
||||
// searchForHost(possibleRioList, name);
|
||||
// subMap.put("possibleRios", possibleRioList.toArray());
|
||||
|
||||
subMap.put("possibleRios", possibleRioList.toArray());
|
||||
map.put("networkInfo", subMap);
|
||||
DataChangeService.getInstance().publishEvent(new OutgoingUIEvent<>("deviceIpInfo", map));
|
||||
}
|
||||
|
||||
String getPossibleRioAddress(String ip) {
|
||||
try {
|
||||
InetAddress addr = InetAddress.getByName(ip);
|
||||
var address = addr.getAddress();
|
||||
if (address[0] != (byte) (10 & 0xff)) return null;
|
||||
address[3] = (byte) (2 & 0xff);
|
||||
return InetAddress.getByAddress(address).getHostAddress();
|
||||
} catch (UnknownHostException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
void searchForHost(List<String> list, String hostname) {
|
||||
try {
|
||||
logger.info("Looking up " + hostname);
|
||||
InetAddress testAddr = InetAddress.getByName(hostname);
|
||||
logger.info("Pinging " + hostname);
|
||||
var canContact = testAddr.isReachable(500);
|
||||
if (canContact) {
|
||||
logger.info("Was able to connect to " + hostname);
|
||||
if (!list.contains(hostname)) list.add(hostname);
|
||||
} else {
|
||||
logger.info("Unable to reach " + hostname);
|
||||
}
|
||||
} catch (IOException ignored) {
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) Photon Vision.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.photonvision.common.scripting;
|
||||
|
||||
public enum ScriptCommandType {
|
||||
kDefault(""),
|
||||
kBashScript("bash"),
|
||||
kPythonScript("python"),
|
||||
kPython3Script("python3");
|
||||
|
||||
public final String value;
|
||||
|
||||
ScriptCommandType(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
}
|
||||
@@ -1,84 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) Photon Vision.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.photonvision.common.util;
|
||||
|
||||
public class MemoryManager {
|
||||
private static final long MEGABYTE_FACTOR = 1024L * 1024L;
|
||||
|
||||
private int collectionThreshold;
|
||||
private long collectionPeriodMillis = -1;
|
||||
|
||||
private double lastUsedMb = 0;
|
||||
private long lastCollectionMillis = 0;
|
||||
|
||||
public MemoryManager(int collectionThreshold) {
|
||||
this.collectionThreshold = collectionThreshold;
|
||||
}
|
||||
|
||||
public MemoryManager(int collectionThreshold, long collectionPeriodMillis) {
|
||||
this.collectionThreshold = collectionThreshold;
|
||||
this.collectionPeriodMillis = collectionPeriodMillis;
|
||||
}
|
||||
|
||||
public void setCollectionThreshold(int collectionThreshold) {
|
||||
this.collectionThreshold = collectionThreshold;
|
||||
}
|
||||
|
||||
public void setCollectionPeriodMillis(long collectionPeriodMillis) {
|
||||
this.collectionPeriodMillis = collectionPeriodMillis;
|
||||
}
|
||||
|
||||
private static long getUsedMemory() {
|
||||
return Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();
|
||||
}
|
||||
|
||||
private static double getUsedMemoryMB() {
|
||||
return ((double) getUsedMemory() / MEGABYTE_FACTOR);
|
||||
}
|
||||
|
||||
private void collect() {
|
||||
System.gc();
|
||||
System.runFinalization();
|
||||
}
|
||||
|
||||
public void run() {
|
||||
run(false);
|
||||
}
|
||||
|
||||
public void run(boolean print) {
|
||||
var usedMem = getUsedMemoryMB();
|
||||
|
||||
if (usedMem != lastUsedMb) {
|
||||
lastUsedMb = usedMem;
|
||||
if (print) System.out.printf("Memory usage: %.2fMB\n", usedMem);
|
||||
}
|
||||
|
||||
boolean collectionThresholdPassed = usedMem >= collectionThreshold;
|
||||
boolean collectionPeriodPassed =
|
||||
collectionPeriodMillis != -1
|
||||
&& (System.currentTimeMillis() - lastCollectionMillis >= collectionPeriodMillis);
|
||||
|
||||
if (collectionThresholdPassed || collectionPeriodPassed) {
|
||||
collect();
|
||||
lastCollectionMillis = System.currentTimeMillis();
|
||||
if (print) {
|
||||
System.out.printf("Garbage collected at %.2fMB\n", usedMem);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) Photon Vision.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.photonvision.common.util;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
|
||||
public class NativeLibHelper {
|
||||
private static NativeLibHelper INSTANCE;
|
||||
|
||||
public static NativeLibHelper getInstance() {
|
||||
if (INSTANCE == null) {
|
||||
INSTANCE = new NativeLibHelper();
|
||||
}
|
||||
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
public final Path NativeLibPath;
|
||||
|
||||
private NativeLibHelper() {
|
||||
String home = System.getProperty("user.home");
|
||||
NativeLibPath = Paths.get(home, ".pvlibs", "nativecache");
|
||||
}
|
||||
}
|
||||
@@ -1,58 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) Photon Vision.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.photonvision.common.util;
|
||||
|
||||
public class ReflectionUtils {
|
||||
public static StackTraceElement[] getFullStackTrace() {
|
||||
return Thread.currentThread().getStackTrace();
|
||||
}
|
||||
|
||||
public static StackTraceElement getNthCaller(int n) {
|
||||
if (n < 0) n = 0;
|
||||
return Thread.currentThread().getStackTrace()[n];
|
||||
}
|
||||
|
||||
public static String getCallerClassName() {
|
||||
StackTraceElement[] stElements = Thread.currentThread().getStackTrace();
|
||||
for (int i = 1; i < stElements.length; i++) {
|
||||
StackTraceElement ste = stElements[i];
|
||||
if (!ste.getClassName().equals(ReflectionUtils.class.getName())
|
||||
&& ste.getClassName().indexOf("java.lang.Thread") != 0) {
|
||||
return ste.getClassName();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static String getCallerCallerClassName() {
|
||||
StackTraceElement[] stElements = Thread.currentThread().getStackTrace();
|
||||
String callerClassName = null;
|
||||
for (int i = 1; i < stElements.length; i++) {
|
||||
StackTraceElement ste = stElements[i];
|
||||
if (!ste.getClassName().equals(ReflectionUtils.class.getName())
|
||||
&& ste.getClassName().indexOf("java.lang.Thread") != 0) {
|
||||
if (callerClassName == null) {
|
||||
callerClassName = ste.getClassName();
|
||||
} else if (!callerClassName.equals(ste.getClassName())) {
|
||||
return ste.getClassName();
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -17,19 +17,12 @@
|
||||
|
||||
package org.photonvision.common.util.file;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.NoSuchFileException;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.attribute.PosixFileAttributes;
|
||||
import java.nio.file.attribute.PosixFilePermission;
|
||||
import java.util.Arrays;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import org.photonvision.common.hardware.Platform;
|
||||
import org.photonvision.common.logging.LogGroup;
|
||||
import org.photonvision.common.logging.Logger;
|
||||
|
||||
@@ -37,8 +30,6 @@ public class FileUtils {
|
||||
private FileUtils() {}
|
||||
|
||||
private static final Logger logger = new Logger(FileUtils.class, LogGroup.General);
|
||||
private static final Set<PosixFilePermission> allReadWriteExecutePerms =
|
||||
new HashSet<>(Arrays.asList(PosixFilePermission.values()));
|
||||
|
||||
public static boolean deleteDirectory(Path path) {
|
||||
try {
|
||||
@@ -109,37 +100,4 @@ public class FileUtils {
|
||||
boolean fileCopied = copyFile(src, dst);
|
||||
return fileDeleted && fileCopied;
|
||||
}
|
||||
|
||||
public static void setFilePerms(Path path) throws IOException {
|
||||
if (Platform.isLinux()) {
|
||||
File thisFile = path.toFile();
|
||||
Set<PosixFilePermission> perms =
|
||||
Files.readAttributes(path, PosixFileAttributes.class).permissions();
|
||||
if (!perms.equals(allReadWriteExecutePerms)) {
|
||||
logger.info("Setting perms on" + path);
|
||||
Files.setPosixFilePermissions(path, perms);
|
||||
var theseFiles = thisFile.listFiles();
|
||||
if (thisFile.isDirectory() && theseFiles != null) {
|
||||
for (File subfile : theseFiles) {
|
||||
setFilePerms(subfile.toPath());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void setAllPerms(Path path) {
|
||||
if (Platform.isLinux()) {
|
||||
String command = String.format("chmod 777 -R %s", path.toString());
|
||||
try {
|
||||
Process p = Runtime.getRuntime().exec(command);
|
||||
p.waitFor();
|
||||
|
||||
} catch (Exception e) {
|
||||
logger.error("Setting perms failed!", e);
|
||||
}
|
||||
} else {
|
||||
logger.info("Cannot set directory permissions on Windows!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) Photon Vision.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.photonvision.common.util.java;
|
||||
|
||||
public interface TriConsumer<T, U, V> {
|
||||
void accept(T t, U u, V v);
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) Photon Vision.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.photonvision.common.util.math;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class IPUtils {
|
||||
public static boolean isValidIPV4(final String ip) {
|
||||
String PATTERN =
|
||||
"^((0|1\\d?\\d?|2[0-4]?\\d?|25[0-5]?|[3-9]\\d?)\\.){3}(0|1\\d?\\d?|2[0-4]?\\d?|25[0-5]?|[3-9]\\d?)$";
|
||||
|
||||
return ip.matches(PATTERN);
|
||||
}
|
||||
|
||||
public static List<Byte> getDigitBytes(int num) {
|
||||
List<Byte> digits = new ArrayList<>();
|
||||
collectDigitBytes(num, digits);
|
||||
return digits;
|
||||
}
|
||||
|
||||
private static void collectDigitBytes(int num, List<Byte> digits) {
|
||||
if (num / 10 > 0) {
|
||||
collectDigitBytes(num / 10, digits);
|
||||
}
|
||||
digits.add((byte) (num % 10));
|
||||
}
|
||||
|
||||
public static List<Integer> getDigits(int num) {
|
||||
List<Integer> digits = new ArrayList<>();
|
||||
collectDigits(num, digits);
|
||||
return digits;
|
||||
}
|
||||
|
||||
private static void collectDigits(int num, List<Integer> digits) {
|
||||
if (num / 10 > 0) {
|
||||
collectDigits(num / 10, digits);
|
||||
}
|
||||
digits.add(num % 10);
|
||||
}
|
||||
}
|
||||
@@ -1,92 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) Photon Vision.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.photonvision.vision.calibration;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import java.util.Base64;
|
||||
import org.opencv.core.Mat;
|
||||
import org.opencv.core.MatOfByte;
|
||||
import org.opencv.imgcodecs.Imgcodecs;
|
||||
import org.photonvision.vision.opencv.Releasable;
|
||||
|
||||
/** JSON-serializable image. Data is stored as base64-encoded PNG data. */
|
||||
public class JsonImageMat implements Releasable {
|
||||
public final int rows;
|
||||
public final int cols;
|
||||
public final int type;
|
||||
|
||||
// We store image data as a base64-encoded PNG inside a Java string. This lets us serialize it
|
||||
// without too much overhead and still use JSON.
|
||||
public final String data;
|
||||
|
||||
// Cached matrices to avoid object recreation
|
||||
@JsonIgnore private Mat wrappedMat = null;
|
||||
|
||||
public JsonImageMat(Mat mat) {
|
||||
this.rows = mat.rows();
|
||||
this.cols = mat.cols();
|
||||
this.type = mat.type();
|
||||
|
||||
// Convert from Mat -> png byte array -> base64
|
||||
var buf = new MatOfByte();
|
||||
Imgcodecs.imencode(".png", mat, buf);
|
||||
data = Base64.getEncoder().encodeToString(buf.toArray());
|
||||
buf.release();
|
||||
}
|
||||
|
||||
public JsonImageMat(
|
||||
@JsonProperty("rows") int rows,
|
||||
@JsonProperty("cols") int cols,
|
||||
@JsonProperty("type") int type,
|
||||
@JsonProperty("data") String data) {
|
||||
this.rows = rows;
|
||||
this.cols = cols;
|
||||
this.type = type;
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
@JsonIgnore
|
||||
public Mat getAsMat() {
|
||||
if (wrappedMat == null) {
|
||||
// Convert back from base64 string -> png -> Mat
|
||||
var bytes = Base64.getDecoder().decode(data);
|
||||
var pngData = new MatOfByte(bytes);
|
||||
this.wrappedMat = Imgcodecs.imdecode(pngData, Imgcodecs.IMREAD_COLOR);
|
||||
}
|
||||
return this.wrappedMat;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void release() {
|
||||
if (wrappedMat != null) wrappedMat.release();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "JsonImageMat [rows="
|
||||
+ rows
|
||||
+ ", cols="
|
||||
+ cols
|
||||
+ ", type="
|
||||
+ type
|
||||
+ ", datalen="
|
||||
+ data.length()
|
||||
+ "]";
|
||||
}
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) Photon Vision.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.photonvision.vision.frame;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public interface FrameConsumer extends Consumer<Frame> {}
|
||||
@@ -1,28 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) Photon Vision.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.photonvision.vision.frame.consumer;
|
||||
|
||||
import org.photonvision.vision.frame.Frame;
|
||||
import org.photonvision.vision.frame.FrameConsumer;
|
||||
|
||||
public class DummyFrameConsumer implements FrameConsumer {
|
||||
@Override
|
||||
public void accept(Frame frame) {
|
||||
frame.release(); // lol ez
|
||||
}
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) Photon Vision.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.photonvision.vision.opencv;
|
||||
|
||||
import org.opencv.core.Mat;
|
||||
|
||||
public class DualMat {
|
||||
public Mat first;
|
||||
public Mat second;
|
||||
}
|
||||
@@ -1,64 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) Photon Vision.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.photonvision.vision.pipe.impl;
|
||||
|
||||
import org.opencv.core.Mat;
|
||||
import org.opencv.core.Size;
|
||||
import org.opencv.imgproc.Imgproc;
|
||||
import org.photonvision.vision.pipe.MutatingPipe;
|
||||
|
||||
/** Represents a pipeline that blurs the image. */
|
||||
public class BlurPipe extends MutatingPipe<Mat, BlurPipe.BlurParams> {
|
||||
/**
|
||||
* Processes this pipe.
|
||||
*
|
||||
* @param in Input for pipe processing.
|
||||
* @return The processed frame.
|
||||
*/
|
||||
@Override
|
||||
protected Void process(Mat in) {
|
||||
Imgproc.blur(in, in, params.getBlurSize());
|
||||
return null;
|
||||
}
|
||||
|
||||
public static class BlurParams {
|
||||
// Default BlurImagePrams with zero blur.
|
||||
public static BlurParams DEFAULT = new BlurParams(0);
|
||||
|
||||
// Member to store the blur size.
|
||||
private final int m_blurSize;
|
||||
|
||||
/**
|
||||
* Constructs a new BlurImageParams.
|
||||
*
|
||||
* @param blurSize The blur size.
|
||||
*/
|
||||
public BlurParams(int blurSize) {
|
||||
m_blurSize = blurSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the blur size.
|
||||
*
|
||||
* @return The blur size.
|
||||
*/
|
||||
public Size getBlurSize() {
|
||||
return new Size(m_blurSize, m_blurSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) Photon Vision.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.photonvision.vision.pipe.impl;
|
||||
|
||||
import java.util.List;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
import org.opencv.core.Mat;
|
||||
import org.opencv.core.Scalar;
|
||||
import org.opencv.imgproc.Imgproc;
|
||||
import org.photonvision.vision.pipe.MutatingPipe;
|
||||
import org.photonvision.vision.target.TrackedTarget;
|
||||
|
||||
public class DrawCornerDetectionPipe
|
||||
extends MutatingPipe<Pair<Mat, List<TrackedTarget>>, DrawCornerDetectionPipe.DrawCornerParams> {
|
||||
@Override
|
||||
protected Void process(Pair<Mat, List<TrackedTarget>> in) {
|
||||
Mat image = in.getLeft();
|
||||
|
||||
for (var target : in.getRight()) {
|
||||
var corners = target.getTargetCorners();
|
||||
for (var corner : corners) {
|
||||
Imgproc.circle(image, corner, params.dotRadius, params.dotColor);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static class DrawCornerParams {
|
||||
int dotRadius;
|
||||
Scalar dotColor;
|
||||
}
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) Photon Vision.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.photonvision.vision.pipe.impl;
|
||||
|
||||
import org.opencv.core.Mat;
|
||||
import org.opencv.core.Size;
|
||||
import org.opencv.imgproc.Imgproc;
|
||||
import org.photonvision.vision.pipe.MutatingPipe;
|
||||
|
||||
public class ErodeDilatePipe extends MutatingPipe<Mat, ErodeDilatePipe.ErodeDilateParams> {
|
||||
@Override
|
||||
protected Void process(Mat in) {
|
||||
if (params.shouldErode()) {
|
||||
Imgproc.erode(in, in, params.getKernel());
|
||||
}
|
||||
if (params.shouldDilate()) {
|
||||
Imgproc.dilate(in, in, params.getKernel());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static class ErodeDilateParams {
|
||||
private final boolean m_erode;
|
||||
private final boolean m_dilate;
|
||||
private final Mat m_kernel;
|
||||
|
||||
public ErodeDilateParams(boolean erode, boolean dilate, int kernelSize) {
|
||||
m_erode = erode;
|
||||
m_dilate = dilate;
|
||||
m_kernel =
|
||||
Imgproc.getStructuringElement(Imgproc.MORPH_RECT, new Size(kernelSize, kernelSize));
|
||||
}
|
||||
|
||||
public boolean shouldErode() {
|
||||
return m_erode;
|
||||
}
|
||||
|
||||
public boolean shouldDilate() {
|
||||
return m_dilate;
|
||||
}
|
||||
|
||||
public Mat getKernel() {
|
||||
return m_kernel;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,551 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) Photon Vision.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.photonvision.vision.pipe.impl;
|
||||
|
||||
import static com.jogamp.opengl.GL.*;
|
||||
import static com.jogamp.opengl.GL2ES2.*;
|
||||
|
||||
import com.jogamp.opengl.*;
|
||||
import com.jogamp.opengl.util.GLBuffers;
|
||||
import com.jogamp.opengl.util.texture.Texture;
|
||||
import com.jogamp.opengl.util.texture.TextureData;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.FloatBuffer;
|
||||
import java.nio.IntBuffer;
|
||||
import jogamp.opengl.GLOffscreenAutoDrawableImpl;
|
||||
import org.opencv.core.CvType;
|
||||
import org.opencv.core.Mat;
|
||||
import org.photonvision.common.logging.LogGroup;
|
||||
import org.photonvision.common.logging.Logger;
|
||||
import org.photonvision.vision.pipe.CVPipe;
|
||||
|
||||
public class GPUAcceleratedHSVPipe extends CVPipe<Mat, Mat, HSVPipe.HSVParams> {
|
||||
private static final String k_vertexShader =
|
||||
String.join(
|
||||
"\n",
|
||||
"#version 100",
|
||||
"",
|
||||
"attribute vec4 position;",
|
||||
"",
|
||||
"void main() {",
|
||||
" gl_Position = position;",
|
||||
"}");
|
||||
private static final String k_fragmentShader =
|
||||
String.join(
|
||||
"\n",
|
||||
"#version 100",
|
||||
"",
|
||||
"precision highp float;",
|
||||
"precision highp int;",
|
||||
"",
|
||||
"uniform vec3 lowerThresh;",
|
||||
"uniform vec3 upperThresh;",
|
||||
"uniform vec2 resolution;",
|
||||
"uniform sampler2D texture0;",
|
||||
"",
|
||||
"vec3 rgb2hsv(vec3 c) {",
|
||||
" vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0);",
|
||||
" vec4 p = mix(vec4(c.bg, K.wz), vec4(c.gb, K.xy), step(c.b, c.g));",
|
||||
" vec4 q = mix(vec4(p.xyw, c.r), vec4(c.r, p.yzx), step(p.x, c.r));",
|
||||
"",
|
||||
" float d = q.x - min(q.w, q.y);",
|
||||
" float e = 1.0e-10;",
|
||||
" return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x);",
|
||||
"}",
|
||||
"",
|
||||
"bool inRange(vec3 hsv) {",
|
||||
" bvec3 botBool = greaterThanEqual(hsv, lowerThresh);",
|
||||
" bvec3 topBool = lessThanEqual(hsv, upperThresh);",
|
||||
" return all(botBool) && all(topBool);",
|
||||
"}",
|
||||
"",
|
||||
"void main() {",
|
||||
" vec2 uv = gl_FragCoord.xy/resolution;",
|
||||
// Important! We do this .bgr swizzle because the image comes in as BGR, but we pretend
|
||||
// it's RGB for convenience+speed
|
||||
" vec3 col = texture2D(texture0, uv).bgr;",
|
||||
// Only the first value in the vec4 gets used for GL_RED, and only the last value gets
|
||||
// used for GL_ALPHA
|
||||
" gl_FragColor = inRange(rgb2hsv(col)) ? vec4(1.0, 0.0, 0.0, 1.0) : vec4(0.0, 0.0, 0.0, 0.0);",
|
||||
"}");
|
||||
private static final int k_startingWidth = 1280, k_startingHeight = 720;
|
||||
private static final float[] k_vertexPositions = {
|
||||
// Set up a quad that covers the screen
|
||||
-1f, +1f, +1f, +1f, -1f, -1f, +1f, -1f
|
||||
};
|
||||
private static final int k_positionVertexAttribute =
|
||||
0; // ID for the vertex shader position variable
|
||||
|
||||
public enum PBOMode {
|
||||
NONE,
|
||||
SINGLE_BUFFERED,
|
||||
DOUBLE_BUFFERED
|
||||
}
|
||||
|
||||
private final IntBuffer vertexVBOIds = GLBuffers.newDirectIntBuffer(1),
|
||||
unpackPBOIds = GLBuffers.newDirectIntBuffer(2),
|
||||
packPBOIds = GLBuffers.newDirectIntBuffer(2);
|
||||
|
||||
private final GL2ES2 gl;
|
||||
private final GLProfile profile;
|
||||
private final int outputFormat;
|
||||
private final PBOMode pboMode;
|
||||
private final GLOffscreenAutoDrawableImpl.FBOImpl drawable;
|
||||
private final Texture texture;
|
||||
// The texture uniform holds the image that's being processed
|
||||
// The resolution uniform holds the current image resolution
|
||||
// The lower and upper uniforms hold the lower and upper HSV limits for thresholding
|
||||
private final int textureUniformId, resolutionUniformId, lowerUniformId, upperUniformId;
|
||||
|
||||
private final Logger logger = new Logger(GPUAcceleratedHSVPipe.class, LogGroup.General);
|
||||
|
||||
private byte[] inputBytes, outputBytes;
|
||||
private Mat outputMat = new Mat(k_startingHeight, k_startingWidth, CvType.CV_8UC1);
|
||||
private int previousWidth = -1, previousHeight = -1;
|
||||
private int unpackIndex = 0, unpackNextIndex = 0, packIndex = 0, packNextIndex = 0;
|
||||
|
||||
public GPUAcceleratedHSVPipe(PBOMode pboMode) {
|
||||
this.pboMode = pboMode;
|
||||
|
||||
// Set up GL profile and ask for specific capabilities
|
||||
profile = GLProfile.get(pboMode == PBOMode.NONE ? GLProfile.GLES2 : GLProfile.GL4ES3);
|
||||
final var capabilities = new GLCapabilities(profile);
|
||||
capabilities.setHardwareAccelerated(true);
|
||||
capabilities.setFBO(true);
|
||||
capabilities.setDoubleBuffered(false);
|
||||
capabilities.setOnscreen(false);
|
||||
capabilities.setRedBits(8);
|
||||
capabilities.setBlueBits(0);
|
||||
capabilities.setGreenBits(0);
|
||||
capabilities.setAlphaBits(0);
|
||||
|
||||
// Set up the offscreen area we're going to draw to
|
||||
final var factory = GLDrawableFactory.getFactory(profile);
|
||||
drawable =
|
||||
(GLOffscreenAutoDrawableImpl.FBOImpl)
|
||||
factory.createOffscreenAutoDrawable(
|
||||
factory.getDefaultDevice(),
|
||||
capabilities,
|
||||
new DefaultGLCapabilitiesChooser(),
|
||||
k_startingWidth,
|
||||
k_startingHeight);
|
||||
drawable.display();
|
||||
drawable.getContext().makeCurrent();
|
||||
|
||||
// Get an OpenGL context; OpenGL ES 2.0 and OpenGL 2.0 are compatible with all the coprocs we
|
||||
// care about but not compatible with PBOs. Open GL ES 3.0 and OpenGL 4.0 are compatible with
|
||||
// select coprocs *and* PBOs
|
||||
gl = pboMode == PBOMode.NONE ? drawable.getGL().getGLES2() : drawable.getGL().getGL4ES3();
|
||||
final int programId = gl.glCreateProgram();
|
||||
|
||||
if (pboMode == PBOMode.NONE && !gl.glGetString(GL_EXTENSIONS).contains("GL_EXT_texture_rg")) {
|
||||
logger.warn(
|
||||
"OpenGL ES 2.0 implementation does not have the 'GL_EXT_texture_rg' extension, falling back to GL_ALPHA instead of GL_RED output format");
|
||||
outputFormat = GL_ALPHA;
|
||||
} else {
|
||||
outputFormat = GL_RED;
|
||||
}
|
||||
|
||||
// JOGL creates a framebuffer color attachment that has RGB set as the format, which is not
|
||||
// appropriate for us because we want a single-channel format
|
||||
// We make ourown FBO color attachment to remedy this
|
||||
// Detach and destroy the FBO color attachment that JOGL made for us
|
||||
drawable.getFBObject(GL_FRONT).detachColorbuffer(gl, 0, true);
|
||||
// Equivalent to calling glBindFramebuffer
|
||||
drawable.getFBObject(GL_FRONT).bind(gl);
|
||||
if (true) { // For now renderbuffers are disabled
|
||||
// Create a color attachment texture to hold our rendered output
|
||||
var colorBufferIds = GLBuffers.newDirectIntBuffer(1);
|
||||
gl.glGenTextures(1, colorBufferIds);
|
||||
gl.glBindTexture(GL_TEXTURE_2D, colorBufferIds.get(0));
|
||||
gl.glTexImage2D(
|
||||
GL_TEXTURE_2D,
|
||||
0,
|
||||
outputFormat == GL_RED ? GL_R8 : GL_ALPHA8,
|
||||
k_startingWidth,
|
||||
k_startingHeight,
|
||||
0,
|
||||
outputFormat,
|
||||
GL_UNSIGNED_BYTE,
|
||||
null);
|
||||
gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
// Attach the texture to the framebuffer
|
||||
gl.glBindTexture(GL_TEXTURE_2D, 0);
|
||||
gl.glFramebufferTexture2D(
|
||||
GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, colorBufferIds.get(0), 0);
|
||||
// Cleanup
|
||||
gl.glBindTexture(GL_TEXTURE_2D, 0);
|
||||
} else {
|
||||
// Create a color attachment texture to hold our rendered output
|
||||
var renderBufferIds = GLBuffers.newDirectIntBuffer(1);
|
||||
gl.glGenRenderbuffers(1, renderBufferIds);
|
||||
gl.glBindRenderbuffer(GL_RENDERBUFFER, renderBufferIds.get(0));
|
||||
gl.glRenderbufferStorage(
|
||||
GL_RENDERBUFFER,
|
||||
outputFormat == GL_RED ? GL_R8 : GL_ALPHA8,
|
||||
k_startingWidth,
|
||||
k_startingHeight);
|
||||
// Attach the texture to the framebuffer
|
||||
gl.glFramebufferRenderbuffer(
|
||||
GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, renderBufferIds.get(0));
|
||||
// Cleanup
|
||||
gl.glBindRenderbuffer(GL_RENDERBUFFER, 0);
|
||||
}
|
||||
drawable.getFBObject(GL_FRONT).unbind(gl);
|
||||
|
||||
// Check that the FBO is attached
|
||||
int fboStatus = gl.glCheckFramebufferStatus(GL_FRAMEBUFFER);
|
||||
if (fboStatus == GL_FRAMEBUFFER_UNSUPPORTED) {
|
||||
throw new RuntimeException(
|
||||
"GL implementation does not support rendering to internal format '"
|
||||
+ String.format("0x%08X", outputFormat == GL_RED ? GL_R8 : GL_ALPHA8)
|
||||
+ "' with type '"
|
||||
+ String.format("0x%08X", GL_UNSIGNED_BYTE)
|
||||
+ "'");
|
||||
} else if (fboStatus != GL_FRAMEBUFFER_COMPLETE) {
|
||||
throw new RuntimeException(
|
||||
"Framebuffer is not complete; framebuffer status is "
|
||||
+ String.format("0x%08X", fboStatus));
|
||||
}
|
||||
|
||||
logger.debug(
|
||||
"Created an OpenGL context with renderer '"
|
||||
+ gl.glGetString(GL_RENDERER)
|
||||
+ "', version '"
|
||||
+ gl.glGetString(GL.GL_VERSION)
|
||||
+ "', and profile '"
|
||||
+ profile
|
||||
+ "'");
|
||||
|
||||
var fmt = GLBuffers.newDirectIntBuffer(1);
|
||||
gl.glGetIntegerv(GLES3.GL_IMPLEMENTATION_COLOR_READ_FORMAT, fmt);
|
||||
var type = GLBuffers.newDirectIntBuffer(1);
|
||||
gl.glGetIntegerv(GLES3.GL_IMPLEMENTATION_COLOR_READ_TYPE, type);
|
||||
|
||||
// Tell OpenGL that the attribute in the vertex shader named position is bound to index 0 (the
|
||||
// index for the generic position input)
|
||||
gl.glBindAttribLocation(programId, 0, "position");
|
||||
|
||||
// Compile and set up our two shaders with our program
|
||||
final int vertexId = createShader(gl, programId, k_vertexShader, GL_VERTEX_SHADER);
|
||||
final int fragmentId = createShader(gl, programId, k_fragmentShader, GL_FRAGMENT_SHADER);
|
||||
|
||||
// Link our program together and check for errors
|
||||
gl.glLinkProgram(programId);
|
||||
IntBuffer status = GLBuffers.newDirectIntBuffer(1);
|
||||
gl.glGetProgramiv(programId, GL_LINK_STATUS, status);
|
||||
if (status.get(0) == GL_FALSE) {
|
||||
IntBuffer infoLogLength = GLBuffers.newDirectIntBuffer(1);
|
||||
gl.glGetProgramiv(programId, GL_INFO_LOG_LENGTH, infoLogLength);
|
||||
|
||||
ByteBuffer bufferInfoLog = GLBuffers.newDirectByteBuffer(infoLogLength.get(0));
|
||||
gl.glGetProgramInfoLog(programId, infoLogLength.get(0), null, bufferInfoLog);
|
||||
byte[] bytes = new byte[infoLogLength.get(0)];
|
||||
bufferInfoLog.get(bytes);
|
||||
String strInfoLog = new String(bytes);
|
||||
|
||||
throw new RuntimeException("Linker failure: " + strInfoLog);
|
||||
}
|
||||
gl.glValidateProgram(programId);
|
||||
|
||||
// Cleanup shaders that are now compiled in
|
||||
gl.glDetachShader(programId, vertexId);
|
||||
gl.glDetachShader(programId, fragmentId);
|
||||
gl.glDeleteShader(vertexId);
|
||||
gl.glDeleteShader(fragmentId);
|
||||
|
||||
// Tell OpenGL to use our program
|
||||
gl.glUseProgram(programId);
|
||||
|
||||
// Set up our texture
|
||||
texture = new Texture(GL_TEXTURE_2D);
|
||||
texture.setTexParameteri(gl, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
texture.setTexParameteri(gl, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
texture.setTexParameteri(gl, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
texture.setTexParameteri(gl, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
|
||||
// Set up a uniform holding our image as a texture
|
||||
textureUniformId = gl.glGetUniformLocation(programId, "texture0");
|
||||
gl.glUniform1i(textureUniformId, 0);
|
||||
|
||||
// Set up a uniform to hold image resolution
|
||||
resolutionUniformId = gl.glGetUniformLocation(programId, "resolution");
|
||||
|
||||
// Set up uniforms for the HSV thresholds
|
||||
lowerUniformId = gl.glGetUniformLocation(programId, "lowerThresh");
|
||||
upperUniformId = gl.glGetUniformLocation(programId, "upperThresh");
|
||||
|
||||
// Set up a quad that covers the entire screen so that our fragment shader draws onto the entire
|
||||
// screen
|
||||
gl.glGenBuffers(1, vertexVBOIds);
|
||||
|
||||
FloatBuffer vertexBuffer = GLBuffers.newDirectFloatBuffer(k_vertexPositions);
|
||||
gl.glBindBuffer(GL_ARRAY_BUFFER, vertexVBOIds.get(0));
|
||||
gl.glBufferData(
|
||||
GL_ARRAY_BUFFER,
|
||||
(long) vertexBuffer.capacity() * Float.BYTES,
|
||||
vertexBuffer,
|
||||
GL_STATIC_DRAW);
|
||||
|
||||
// Set up pixel unpack buffer (a PBO to transfer image data to the GPU)
|
||||
if (pboMode != PBOMode.NONE) {
|
||||
gl.glGenBuffers(2, unpackPBOIds);
|
||||
|
||||
gl.glBindBuffer(GLES3.GL_PIXEL_UNPACK_BUFFER, unpackPBOIds.get(0));
|
||||
gl.glBufferData(
|
||||
GLES3.GL_PIXEL_UNPACK_BUFFER,
|
||||
k_startingHeight * k_startingWidth * 3,
|
||||
null,
|
||||
GLES3.GL_STREAM_DRAW);
|
||||
if (pboMode == PBOMode.DOUBLE_BUFFERED) {
|
||||
gl.glBindBuffer(GLES3.GL_PIXEL_UNPACK_BUFFER, unpackPBOIds.get(1));
|
||||
gl.glBufferData(
|
||||
GLES3.GL_PIXEL_UNPACK_BUFFER,
|
||||
k_startingHeight * k_startingWidth * 3,
|
||||
null,
|
||||
GLES3.GL_STREAM_DRAW);
|
||||
}
|
||||
gl.glBindBuffer(GLES3.GL_PIXEL_UNPACK_BUFFER, 0);
|
||||
}
|
||||
|
||||
// Set up pixel pack buffer (a PBO to transfer the processed image back from the GPU)
|
||||
if (pboMode != PBOMode.NONE) {
|
||||
gl.glGenBuffers(2, packPBOIds);
|
||||
|
||||
gl.glBindBuffer(GLES3.GL_PIXEL_PACK_BUFFER, packPBOIds.get(0));
|
||||
gl.glBufferData(
|
||||
GLES3.GL_PIXEL_PACK_BUFFER,
|
||||
k_startingHeight * k_startingWidth,
|
||||
null,
|
||||
GLES3.GL_STREAM_READ);
|
||||
if (pboMode == PBOMode.DOUBLE_BUFFERED) {
|
||||
gl.glBindBuffer(GLES3.GL_PIXEL_PACK_BUFFER, packPBOIds.get(1));
|
||||
gl.glBufferData(
|
||||
GLES3.GL_PIXEL_PACK_BUFFER,
|
||||
k_startingHeight * k_startingWidth,
|
||||
null,
|
||||
GLES3.GL_STREAM_READ);
|
||||
}
|
||||
gl.glBindBuffer(GLES3.GL_PIXEL_PACK_BUFFER, 0);
|
||||
}
|
||||
}
|
||||
|
||||
private static int createShader(GL2ES2 gl, int programId, String glslCode, int shaderType) {
|
||||
int shaderId = gl.glCreateShader(shaderType);
|
||||
if (shaderId == 0) throw new RuntimeException("Shader ID is zero");
|
||||
|
||||
IntBuffer length = GLBuffers.newDirectIntBuffer(new int[] {glslCode.length()});
|
||||
gl.glShaderSource(shaderId, 1, new String[] {glslCode}, length);
|
||||
gl.glCompileShader(shaderId);
|
||||
|
||||
IntBuffer intBuffer = IntBuffer.allocate(1);
|
||||
gl.glGetShaderiv(shaderId, GL_COMPILE_STATUS, intBuffer);
|
||||
|
||||
if (intBuffer.get(0) != 1) {
|
||||
gl.glGetShaderiv(shaderId, GL_INFO_LOG_LENGTH, intBuffer);
|
||||
int size = intBuffer.get(0);
|
||||
if (size > 0) {
|
||||
ByteBuffer byteBuffer = ByteBuffer.allocate(size);
|
||||
gl.glGetShaderInfoLog(shaderId, size, intBuffer, byteBuffer);
|
||||
System.err.println(new String(byteBuffer.array()));
|
||||
}
|
||||
throw new RuntimeException("Couldn't compile shader");
|
||||
}
|
||||
|
||||
gl.glAttachShader(programId, shaderId);
|
||||
|
||||
return shaderId;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Mat process(Mat in) {
|
||||
if (in.width() != previousWidth && in.height() != previousHeight) {
|
||||
logger.debug("Resizing OpenGL viewport, byte buffers, and PBOs");
|
||||
|
||||
drawable.setSurfaceSize(in.width(), in.height());
|
||||
gl.glViewport(0, 0, in.width(), in.height());
|
||||
|
||||
previousWidth = in.width();
|
||||
previousHeight = in.height();
|
||||
|
||||
inputBytes = new byte[in.width() * in.height() * 3];
|
||||
|
||||
outputBytes = new byte[in.width() * in.height()];
|
||||
outputMat = new Mat(k_startingHeight, k_startingWidth, CvType.CV_8UC1);
|
||||
|
||||
if (pboMode != PBOMode.NONE) {
|
||||
gl.glBindBuffer(GLES3.GL_PIXEL_PACK_BUFFER, packPBOIds.get(0));
|
||||
gl.glBufferData(
|
||||
GLES3.GL_PIXEL_PACK_BUFFER,
|
||||
(long) in.width() * in.height(),
|
||||
null,
|
||||
GLES3.GL_STREAM_READ);
|
||||
|
||||
if (pboMode == PBOMode.DOUBLE_BUFFERED) {
|
||||
gl.glBindBuffer(GLES3.GL_PIXEL_PACK_BUFFER, packPBOIds.get(1));
|
||||
gl.glBufferData(
|
||||
GLES3.GL_PIXEL_PACK_BUFFER,
|
||||
(long) in.width() * in.height(),
|
||||
null,
|
||||
GLES3.GL_STREAM_READ);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (pboMode == PBOMode.DOUBLE_BUFFERED) {
|
||||
unpackIndex = (unpackIndex + 1) % 2;
|
||||
unpackNextIndex = (unpackIndex + 1) % 2;
|
||||
}
|
||||
|
||||
// Reset the fullscreen quad
|
||||
gl.glBindBuffer(GL_ARRAY_BUFFER, vertexVBOIds.get(0));
|
||||
gl.glEnableVertexAttribArray(k_positionVertexAttribute);
|
||||
gl.glVertexAttribPointer(0, 2, GL_FLOAT, false, 0, 0);
|
||||
gl.glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||
|
||||
// Load and bind our image as a 2D texture
|
||||
gl.glActiveTexture(GL_TEXTURE0);
|
||||
texture.enable(gl);
|
||||
texture.bind(gl);
|
||||
|
||||
// Load our image into the texture
|
||||
in.get(0, 0, inputBytes);
|
||||
if (pboMode == PBOMode.NONE) {
|
||||
ByteBuffer buf = ByteBuffer.wrap(inputBytes);
|
||||
// (We're actually taking in BGR even though this says RGB; it's much easier and faster to
|
||||
// switch it around in the fragment shader)
|
||||
texture.updateImage(
|
||||
gl,
|
||||
new TextureData(
|
||||
profile,
|
||||
GL_RGB8,
|
||||
in.width(),
|
||||
in.height(),
|
||||
0,
|
||||
GL_RGB,
|
||||
GL_UNSIGNED_BYTE,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
buf,
|
||||
null));
|
||||
} else {
|
||||
// Bind the PBO to the texture
|
||||
gl.glBindBuffer(GLES3.GL_PIXEL_UNPACK_BUFFER, unpackPBOIds.get(unpackIndex));
|
||||
|
||||
// Copy pixels from the PBO to the texture object
|
||||
gl.glTexSubImage2D(
|
||||
GLES3.GL_TEXTURE_2D,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
in.width(),
|
||||
in.height(),
|
||||
GLES3.GL_RGB8,
|
||||
GLES3.GL_UNSIGNED_BYTE,
|
||||
0);
|
||||
|
||||
// Bind (potentially) another PBO to update the texture source
|
||||
gl.glBindBuffer(GLES3.GL_PIXEL_UNPACK_BUFFER, unpackPBOIds.get(unpackNextIndex));
|
||||
|
||||
// This call with a nullptr for the data arg tells OpenGL *not* to wait to be in sync with the
|
||||
// GPU
|
||||
// This causes the previous data in the PBO to be discarded
|
||||
gl.glBufferData(
|
||||
GLES3.GL_PIXEL_UNPACK_BUFFER,
|
||||
(long) in.width() * in.height() * 3,
|
||||
null,
|
||||
GLES3.GL_STREAM_DRAW);
|
||||
|
||||
// Map the buffer of GPU memory into a place that's accessible by us
|
||||
var buf =
|
||||
gl.glMapBufferRange(
|
||||
GLES3.GL_PIXEL_UNPACK_BUFFER,
|
||||
0,
|
||||
(long) in.width() * in.height() * 3,
|
||||
GLES3.GL_MAP_WRITE_BIT);
|
||||
buf.put(inputBytes);
|
||||
|
||||
gl.glUnmapBuffer(GLES3.GL_PIXEL_UNPACK_BUFFER);
|
||||
gl.glBindBuffer(GLES3.GL_PIXEL_UNPACK_BUFFER, 0);
|
||||
}
|
||||
|
||||
// Put values in a uniform holding the image resolution
|
||||
gl.glUniform2f(resolutionUniformId, in.width(), in.height());
|
||||
|
||||
// Put values in threshold uniforms
|
||||
var lowr = params.getHsvLower().val;
|
||||
var upr = params.getHsvUpper().val;
|
||||
gl.glUniform3f(lowerUniformId, (float) lowr[0], (float) lowr[1], (float) lowr[2]);
|
||||
gl.glUniform3f(upperUniformId, (float) upr[0], (float) upr[1], (float) upr[2]);
|
||||
|
||||
// Draw the fullscreen quad
|
||||
gl.glDrawArrays(GL_TRIANGLE_STRIP, 0, k_vertexPositions.length);
|
||||
|
||||
// Cleanup
|
||||
texture.disable(gl);
|
||||
gl.glDisableVertexAttribArray(k_positionVertexAttribute);
|
||||
gl.glUseProgram(0);
|
||||
|
||||
if (pboMode == PBOMode.NONE) {
|
||||
return saveMatNoPBO(gl, in.width(), in.height());
|
||||
} else {
|
||||
return saveMatPBO((GLES3) gl, in.width(), in.height(), pboMode == PBOMode.DOUBLE_BUFFERED);
|
||||
}
|
||||
}
|
||||
|
||||
private Mat saveMatNoPBO(GL2ES2 gl, int width, int height) {
|
||||
ByteBuffer buffer = GLBuffers.newDirectByteBuffer(width * height);
|
||||
// We use GL_RED/GL_ALPHA to get things in a single-channel format
|
||||
// Note that which pixel format you use is *very* important to performance
|
||||
// E.g. GL_ALPHA is super slow in this case
|
||||
gl.glReadPixels(0, 0, width, height, outputFormat, GL_UNSIGNED_BYTE, buffer);
|
||||
|
||||
return new Mat(height, width, CvType.CV_8UC1, buffer);
|
||||
}
|
||||
|
||||
private Mat saveMatPBO(GLES3 gl, int width, int height, boolean doubleBuffered) {
|
||||
if (doubleBuffered) {
|
||||
packIndex = (packIndex + 1) % 2;
|
||||
packNextIndex = (packIndex + 1) % 2;
|
||||
}
|
||||
|
||||
// Set the target framebuffer attachment to read
|
||||
gl.glReadBuffer(GLES3.GL_COLOR_ATTACHMENT0);
|
||||
|
||||
// Read pixels from the framebuffer to the PBO
|
||||
gl.glBindBuffer(GLES3.GL_PIXEL_PACK_BUFFER, packPBOIds.get(packIndex));
|
||||
// We use GL_RED (which is always supported in GLES3) to get things in a single-channel format
|
||||
// Note that which pixel format you use is *very* important to performance
|
||||
// E.g. GL_ALPHA is super slow in this case
|
||||
gl.glReadPixels(0, 0, width, height, GLES3.GL_RED, GLES3.GL_UNSIGNED_BYTE, 0);
|
||||
|
||||
// Map the PBO into the CPU's memory
|
||||
gl.glBindBuffer(GLES3.GL_PIXEL_PACK_BUFFER, packPBOIds.get(packNextIndex));
|
||||
var buf =
|
||||
gl.glMapBufferRange(
|
||||
GLES3.GL_PIXEL_PACK_BUFFER, 0, (long) width * height, GLES3.GL_MAP_READ_BIT);
|
||||
buf.get(outputBytes);
|
||||
outputMat.put(0, 0, outputBytes);
|
||||
gl.glUnmapBuffer(GLES3.GL_PIXEL_PACK_BUFFER);
|
||||
gl.glBindBuffer(GLES3.GL_PIXEL_PACK_BUFFER, 0);
|
||||
|
||||
return outputMat;
|
||||
}
|
||||
}
|
||||
@@ -19,7 +19,6 @@ package org.photonvision.vision.processes;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
import org.photonvision.common.dataflow.websocket.UICameraConfiguration;
|
||||
import org.photonvision.common.logging.LogGroup;
|
||||
import org.photonvision.common.logging.Logger;
|
||||
|
||||
@@ -83,24 +82,4 @@ public class VisionModuleManager {
|
||||
|
||||
return idx;
|
||||
}
|
||||
|
||||
public static class UiVmmState {
|
||||
public final List<UICameraConfiguration> visionModules;
|
||||
|
||||
UiVmmState(List<UICameraConfiguration> _v) {
|
||||
this.visionModules = _v;
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized UiVmmState getState() {
|
||||
return new UiVmmState(
|
||||
this.visionModules.stream()
|
||||
.map(VisionModule::toUICameraConfig)
|
||||
.map(
|
||||
it -> {
|
||||
it.calibrations = null;
|
||||
return it;
|
||||
})
|
||||
.collect(Collectors.toList()));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user